AES/CBC encrypt in Java, decrypt in Ruby

后端 未结 2 1091
小蘑菇
小蘑菇 2021-01-03 10:27

I am trying to translate the following (working) Java code to Ruby.

   public static final String PROVIDER = \"BC\";
   public static final int IV_LENGTH = 1         


        
相关标签:
2条回答
  • 2021-01-03 11:14

    Based on the title here, I am going to assume that you want to be able to encrypt a message in Java, and then decrypt that message in Ruby, using password-based AES-CBC encryption.

    Now, the openssl standard library in Ruby readily supports password-based key derivation function 2 based on PKCS5. You can greatly simplify your Ruby decryption code if you leverage this in your Java encryption.

    Here is how you would encrypt using PBKDF2 in PKCS5 in Java:

    // 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"));
    }
    

    Running the above will result in something like the following output.

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

    Hex strings are used for the encrypted message and initialization vector since you can use OpenSSL to verify the encryption/decryption process (highly recommended).

    Now from a Ruby program, you would use the AES-256-CBC cipher, and derive the secret key from the password and salt strings (not byte[] as per Java). Using the output from the above-mentioned Java program, we have:

    # 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..."
    

    NOTE: The Ruby part of this code pretty much comes verbatim from the Japanese documentation on the openssl standard library.

    0 讨论(0)
  • 2021-01-03 11:19

    I once had a similar problem with CIPHER_ALGORITHM = "AES/CBC/PKCS5Padding"; and decryption through the openSSL-library in C which I could't solve. I avoided the problem by using "AES/CBC/NoPadding" and by adding a particular padding to the plaintext manually.

    0 讨论(0)
提交回复
热议问题