1
votes

I have some problem decrypting text with CryptoJS that has been encrypted with Java. The decryption should be done with AES/CBC/PKCS5Padding. The encrypted string is base64 encoded and I decode it before trying to decrypt the string.

This is how the Java code looks like:

private static byte[] doAES(int mode, byte[] dataToEncrypt, String secretKey, String salt) throws Exception {
        byte[] iv = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
        IvParameterSpec ivspec = new IvParameterSpec(iv);
        SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
        KeySpec spec = new PBEKeySpec(secretKey.toCharArray(), salt.getBytes(), 65536, 256);
        SecretKey tmp = factory.generateSecret(spec);
        SecretKeySpec secretKeySpec = new SecretKeySpec(tmp.getEncoded(), "AES");
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(mode, secretKeySpec, ivspec);
        
        return cipher.doFinal(dataToEncrypt);
}

And this is how I attempt to decrypt it in CryptoJS. SharedSecretKey is both the secretKey and the salt value in Java.

let decoded = Buffer.from(encodedString, 'base64')
console.log("Result: " + CryptoJS.AES.decrypt({
  ciphertext: CryptoJS.enc.Hex.parse(decoded.toString().substring(32))
}, CryptoJS.enc.Hex.parse(CryptoJS.SHA1(sharedSecretKey).toString().substring(0,32)),  
  {
    iv: CryptoJS.lib.WordArray.create(Buffer.alloc(16)),
  }).toString(CryptoJS.enc.Utf8))

Where decoded is the decoded Base64-string that I want to decrypt. However, this does not work and I get the error 'Error: Malformed UTF-8 data'. I am not sure if there is anything I have missed, any help is very much appreciated.

1
The encrypt function in Java is using a key derivation method called PBKDF2 that is missing in cryptojs. Your Java encryption does not show what further encoding is used with the ciphertext (result of doAES) but that is important when talking about decryption in another framework. Some examples how to run a complete cross platform encryption with PBKDF2 and AES/CBC can be found here (Disclaimer: I'm the author): github.com/java-crypto/cross_platform_crypto/blob/main/docs/…Michael Fehr

1 Answers

2
votes

In the CryptoJS code the key derivation with PBKDF2 is missing. CryptoJS uses SHA1 for this by default.
The ciphertext can be passed Base64 encoded and will be implicitly converted to a CipherParams object:

var encodedString = "0O15lUg8sE1G0+BjO5N2j8AjVKXV4J+18z5DinbM6tYjoILhL0WDTFWbcTiN+pG/";
var key = CryptoJS.PBKDF2(
  "my passphrase", 
  "my salt", 
  {
    keySize: 256 / 32, 
    iterations: 65536
  }
);
var decryptedData = CryptoJS.AES.decrypt(encodedString, key,  
  {
    iv: CryptoJS.enc.Utf8.parse("\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"),
  }
);

console.log("Result: " + decryptedData.toString(CryptoJS.enc.Utf8));
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>

The ciphertext in the above example was previously generated with the posted Java code.

Note that a static IV is insecure. The IV is usually randomly generated for each encryption and passed along with the ciphertext (typically concatenated). Analogously for the salt, it is randomly generated for each key derivation and passed together with ciphertext and IV, e.g. salt|iv|ciphertext. Both, IV and salt, are not secret and need not be encrypted.

In addition, the encoding should be specified in the Java code when encoding with getBytes() and decoding with new String(), otherwise cross-platform problems may occur.