AES / CBC在Java中加密,在Ruby中解密

我正在尝试将以下(工作)Java代码转换为Ruby。

public static final String PROVIDER = "BC"; public static final int IV_LENGTH = 16; private static final String HASH_ALGORITHM = "SHA-512"; private static final String PBE_ALGORITHM = "PBEWithSHA256And256BitAES-CBC-BC"; private static final String CIPHER_ALGORITHM = "AES/CBC/PKCS5Padding"; private static final String SECRET_KEY_ALGORITHM = "AES"; public String decrypt(SecretKey secret, String encrypted) { Cipher decryptionCipher = Cipher.getInstance(CIPHER_ALGORITHM, PROVIDER); String ivHex = encrypted.substring(0, IV_LENGTH * 2); String encryptedHex = encrypted.substring(IV_LENGTH * 2); IvParameterSpec ivspec = new IvParameterSpec(HexEncoder.toByte(ivHex)); decryptionCipher.init(Cipher.DECRYPT_MODE, secret, ivspec); byte[] decryptedText = decryptionCipher.doFinal(HexEncoder.toByte(encryptedHex)); String decrypted = new String(decryptedText, "UTF-8"); return decrypted; } 

我的(不工作)Ruby代码是这样的:

 require 'openssl' require 'digest/sha2' SECRET = "MY PASSWORD AS RAW TEXT" IV_LENGHT = 16 encoded = "45D0EC4D910C0A6FF67325FF7362DCBC4613B6F3BFDFE35930D764FB1FE62251" iv = encoded.slice(0, IV_LENGHT * 2) e = encoded.slice(IV_LENGHT*2..-1) binary_iv = iv.unpack('a2'*IV_LENGHT).map{|x| x.hex}.pack('c'*IV_LENGHT) binary_e = e.unpack('a2'*IV_LENGHT).map{|x| x.hex}.pack('c'*IV_LENGHT) c = OpenSSL::Cipher::Cipher.new("aes-256-cbc") c.decrypt c.key = Digest::SHA256.digest(SECRET).slice(0, IV_LENGHT* 2 ) c.iv = binary_iv d = c.update(binary_e) d << c.final puts "decrypted: #{d}\n" 

我已经尝试过二进制和非二进制版本,没有运气。 有人可以指出这个问题吗?

基于此处的标题,我将假设您希望能够使用Java 加密消息,然后使用基于密码的AES-CBC加密在Ruby中解密该消息。

现在,Ruby中的openssl标准库很容易支持基于PKCS5的基于密码的密钥派生function2 。 如果在Java 加密中利用它,可以大大简化Ruby 解密代码。

以下是在Java中使用PKCS5中的PBKDF2加密的方法:

 // in Java-land import java.security.AlgorithmParameters; import java.security.spec.KeySpec; import javax.crypto.Cipher; import javax.crypto.SecretKey; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.PBEKeySpec; import javax.crypto.spec.SecretKeySpec; ... static String printHex(byte[] bytes) { StringBuilder sb = new StringBuilder(); for (byte b : bytes) { sb.append(String.format("%02x", (b & 0xFF))); } return sb.toString(); } public static Map encrypt(String msg, String pwd, byte[] salt) throws Exception { Map retval = new HashMap(); // prepare to use PBKDF2/HMAC+SHA1, since ruby supports this readily SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); // our key is 256 bits, and can be generated knowing the password and salt KeySpec spec = new PBEKeySpec(pwd.toCharArray(), salt, 1024, 256); SecretKey tmp = factory.generateSecret(spec); SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES"); // given key above, our cippher will be aes-256-cbc in ruby/openssl Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); cipher.init(Cipher.ENCRYPT_MODE, secret); AlgorithmParameters params = cipher.getParameters(); // generate the intialization vector byte[] iv = params.getParameterSpec(IvParameterSpec.class).getIV(); retval.put("iv", printHex(iv)); byte[] ciphertext = cipher.doFinal(msg.getBytes("UTF-8")); retval.put("encrypted", printHex(ciphertext)); return retval; } public static void main(String[] args) throws Exception { String msg = "To Ruby, from Java, with love..."; String pwd = "password"; String salt = "8 bytes!"; // in reality, you would use SecureRandom! System.out.println("password (plaintext): " + pwd); System.out.println("salt: " + salt); Map m = encrypt(msg, pwd, salt.getBytes()); System.out.println("encrypted: " + m.get("encrypted")); System.out.println("iv: " + m.get("iv")); } 

运行以上将导致类似以下输出。

 password (plaintext): password salt: 8 bytes! encrypted: 4a39f1a967c728e11c7a5a3fb5d73ad07561f504c9d084d0b1ae600cc1f75137cbb82a4d826c060cb06e2e283449738d iv: ecbc985b3550edc977a17acc066f2192 

hex字符串用于加密消息和初始化向量,因为您可以使用OpenSSL来validation加密/解密过程(强烈建议)。

现在,从Ruby程序中,您将使用AES-256-CBC密码,并从passwordsalt字符串(而非按照Java的byte[] )派生密钥。 使用上述Java程序的输出,我们有:

 # from Ruby-land require 'openssl' d = OpenSSL::Cipher.new("AES-256-CBC") d.decrypt key = OpenSSL::PKCS5.pbkdf2_hmac_sha1("password", "8 bytes!", 1024, d.key_len) d.key = key d.iv = "ecbc985b3550edc977a17acc066f2192".scan(/../).map{|b|b.hex}.pack('c*') data = "4a39f1a967c728e11c7a5a3fb5d73ad07561f504c9d084d0b1ae600cc1f75137cbb82a4d826c060cb06e2e283449738d".scan(/../).map{|b|b.hex}.pack('c*') d.update(data) << d.final => "To Ruby, from Java, with love..." 

注意:此代码的Ruby部分几乎完全来自openssl标准库上的日语文档 。

我曾经遇到类似的问题CIPHER_ALGORITHM =“AES / CBC / PKCS5Padding”; 并通过C中的openSSL库解密,我无法解决。 我通过使用“AES / CBC / NoPadding”并通过手动向明文添加特定填充来避免此问题。