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中的“ 2”标准库很容易支持基于PKCS5的基于密码的密钥派生功能2。如果在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<String,String> encrypt(String msg, String pwd, byte[] salt)
        throws Exception {
    Map<String,String> retval = new HashMap<String,String>();

    // 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<String,String> 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
十六进制字符串用于加密的消息和初始化向量,因为您可以使用OpenSSL来验证加密/解密过程(强烈建议)。 现在,从Ruby程序中,您将使用ѭ5cip密码,并从
password
salt
字符串(不是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 \”并通过手动向明文添加特定的填充来避免了该问题。     

要回复问题请先登录注册