1

CryptoJS AES encryption and Java AES decryption I came across the above solution. I tried changing hashing ago from md5 to SHA-256, but it doesnt work. gives my the following error:

javax.crypto.BadPaddingException: Given final block not properly padded. Such issues can arise if a bad key is used during decryption.

Is there anything i need to change in the helper function

  • As you're information to us is very sparse we can only put on the "guess mode". I believe it's the outputlength of MD5 compared to SHA-256. The MD5-hash is half the length of SHA-256 so this might cause the error but without seeing the source code you used for encription and decryption on CryptoJS + Java sides it's all speculation. – Michael Fehr Jul 08 '20 at 15:00
  • 2
    `CryptoJS.AES.encrypt` uses _implicit_ (and hard-coded) MD5 (as digest for the OpenSSL function `EVP_BytesToKey`). If SHA256 is used instead of MD5 in the Java code, this results in different keys for encryption and decryption. – Topaco Jul 08 '20 at 16:04
  • I have the solution iin exactly as in the link above. Please refer the link in my description – Shivang Chauhan Jul 08 '20 at 19:47
  • Also near dupe https://stackoverflow.com/questions/29151211/how-to-decrypt-an-encrypted-aes-256-string-from-cryptojs-using-java – dave_thompson_085 Jul 09 '20 at 00:05

1 Answers1

1

I used the answer in CryptoJS AES encryption and Java AES decryption as basis for a program that includes encryption and changes the MessageDigest from MD5 to SHA-256.

Running this program it sucessfully decrypts the encrypted data (as Base64-string in cipherText):

Changes: added encryption and using SHA-256 as hash algorithm
cipherText: AAAAAAAAAAAUUMNwaxNxbgvkWpqE+kfq0f3K/cQ6wwwiwFFsIBa8PXsoi0Z7dRhtNVurV1MbysOzNh9R9YMltSM/6en8e9JmEingdD3Rgp8=
The quick brown fox jumps over the lazy dog.  

Running this program "stand alone" will work as expected but not as asked "encrypt in CryptoJS and decrypt in Java". That's because the MD5-hashing is "built in" (or "hard-coded") in CryptoJS as already commented by @Topaco some hours ago (all credits to him). So in the end there is no other way as to use MD5 on Java-side as CryptoJS uses it as MessageDigest. Maybe there is another crypto-library available for JavaScript.

Here is my code:

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.util.Arrays;
import java.util.Base64;

public class MainOrgSha256WithEncryption {
    public static void main(String[] args) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
        System.out.println("https://stackoverflow.com/questions/41432896/cryptojs-aes-encryption-and-java-aes-decryption");
        System.out.println("Changes: added encryption and using SHA-256 as hash algorithm");

        String secret = "René Über";
        String plainText = "The quick brown fox jumps over the lazy dog. \uD83D\uDC7B \uD83D\uDC7B";
        // generate random salt
        SecureRandom secureRandom = new SecureRandom();
        byte[] saltDataEncryption = new byte[8];
        secureRandom.nextBytes(saltDataEncryption);
        // changed MessageDigest from MD5 to SHA-256
        MessageDigest md5 = MessageDigest.getInstance("SHA-256");
        //MessageDigest md5 = MessageDigest.getInstance("MD5");
        final byte[][] keyAndIVEncryption = GenerateKeyAndIV(32, 16, 1, saltDataEncryption, secret.getBytes(StandardCharsets.UTF_8), md5);
        SecretKeySpec keyEncryption = new SecretKeySpec(keyAndIVEncryption[0], "AES");
        IvParameterSpec ivEncryption = new IvParameterSpec(keyAndIVEncryption[1]);
        // encryption
        Cipher aesCBCEncryption = Cipher.getInstance("AES/CBC/PKCS5Padding");
        aesCBCEncryption.init(Cipher.ENCRYPT_MODE, keyEncryption, ivEncryption);
        byte[] cipherTextEncryption = aesCBCEncryption.doFinal(plainText.getBytes(StandardCharsets.UTF_8));
        // concate 8 zero bytes + salt + cipherTextEncryption
        int arrayLength = 8 + saltDataEncryption.length + cipherTextEncryption.length;
        byte[] cipherTextEncryptionComplete = new byte[arrayLength];
        System.arraycopy(saltDataEncryption, 0, cipherTextEncryptionComplete, 8, saltDataEncryption.length);
        System.arraycopy(cipherTextEncryption, 0, cipherTextEncryptionComplete, 16, cipherTextEncryption.length);
        String cipherTextBase64 = Base64.getEncoder().encodeToString(cipherTextEncryptionComplete);
        // now we are using the new cipherTextBase64 as input for the decryption
        String cipherText = cipherTextBase64;
        System.out.println("cipherText: " + cipherText);

        // decryption
        byte[] cipherData = Base64.getDecoder().decode(cipherText);
        byte[] saltData = Arrays.copyOfRange(cipherData, 8, 16);

        final byte[][] keyAndIV = GenerateKeyAndIV(32, 16, 1, saltData, secret.getBytes(StandardCharsets.UTF_8), md5);
        SecretKeySpec key = new SecretKeySpec(keyAndIV[0], "AES");
        IvParameterSpec iv = new IvParameterSpec(keyAndIV[1]);

        byte[] encrypted = Arrays.copyOfRange(cipherData, 16, cipherData.length);
        Cipher aesCBC = Cipher.getInstance("AES/CBC/PKCS5Padding");
        aesCBC.init(Cipher.DECRYPT_MODE, key, iv);
        byte[] decryptedData = aesCBC.doFinal(encrypted);
        String decryptedText = new String(decryptedData, StandardCharsets.UTF_8);

        System.out.println(decryptedText);
    }
    /**
     * Generates a key and an initialization vector (IV) with the given salt and password.
     * <p>
     * This method is equivalent to OpenSSL's EVP_BytesToKey function
     * (see https://github.com/openssl/openssl/blob/master/crypto/evp/evp_key.c).
     * By default, OpenSSL uses a single iteration, MD5 as the algorithm and UTF-8 encoded password data.
     * </p>
     * @param keyLength the length of the generated key (in bytes)
     * @param ivLength the length of the generated IV (in bytes)
     * @param iterations the number of digestion rounds
     * @param salt the salt data (8 bytes of data or <code>null</code>)
     * @param password the password data (optional)
     * @param md the message digest algorithm to use
     * @return an two-element array with the generated key and IV
     */
    public static byte[][] GenerateKeyAndIV(int keyLength, int ivLength, int iterations, byte[] salt, byte[] password, MessageDigest md) {

        int digestLength = md.getDigestLength();
        int requiredLength = (keyLength + ivLength + digestLength - 1) / digestLength * digestLength;
        byte[] generatedData = new byte[requiredLength];
        int generatedLength = 0;

        try {
            md.reset();

            // Repeat process until sufficient data has been generated
            while (generatedLength < keyLength + ivLength) {

                // Digest data (last digest if available, password data, salt if available)
                if (generatedLength > 0)
                    md.update(generatedData, generatedLength - digestLength, digestLength);
                md.update(password);
                if (salt != null)
                    md.update(salt, 0, 8);
                md.digest(generatedData, generatedLength, digestLength);

                // additional rounds
                for (int i = 1; i < iterations; i++) {
                    md.update(generatedData, generatedLength, digestLength);
                    md.digest(generatedData, generatedLength, digestLength);
                }

                generatedLength += digestLength;
            }

            // Copy key and IV into separate byte arrays
            byte[][] result = new byte[2][];
            result[0] = Arrays.copyOfRange(generatedData, 0, keyLength);
            if (ivLength > 0)
                result[1] = Arrays.copyOfRange(generatedData, keyLength, keyLength + ivLength);

            return result;

        } catch (DigestException e) {
            throw new RuntimeException(e);

        } finally {
            // Clean out temporary data
            Arrays.fill(generatedData, (byte)0);
        }
    }
}
Michael Fehr
  • 5,827
  • 2
  • 19
  • 40
  • 1
    FWIW cryptojs.AES only forces old-OpenSSL-style key derivation i.e. EVP_BytesToKey(md5,salt8,n=1) including IV, and data format with `Salted__`, if you give it a password in a js string. You can instead do standard encryption by giving it a key, and if applicable IV, in js Buffer(s), and that key can use a better PBKDF (like PBKDF2, bcrypt, argon2) or a non-password-based scheme (like DH or EG) that you choose and implement. – dave_thompson_085 Jul 09 '20 at 00:05