How to Decrypt an Encrypted Aes-256 String from Cryptojs Using Java

How to decrypt an encrypted AES-256 string from CryptoJS using Java?

When a message is encrypted in this way:

CryptoJS.AES.encrypt("message", "passphrase")

a password-based approach is used. For that CryptoJS generates a new salt and uses this salt in conjunction with the passphrase to derive the key and IV (I've recreated a derivation function for this question).

After the ciphertext is produced a special OpenSSL formatter is used to encode the ciphertext including the used salt:

var wordArray = WordArray.create([0x53616c74, 0x65645f5f]).concat(salt).concat(ciphertext);

You need to decode the Base64 encoded string to get the byte array out if it. Then you can check if the header matches:

byte[] ctBytes = Base64.getDecoder().decode(ciphertext.getBytes("UTF-8"));
System.out.println("Is salted: " + new String(Arrays.copyOf(ctBytes, 8)).equals("Salted__"));

After that you can recover the salt and ciphertext from it:

byte[] saltBytes = Arrays.copyOfRange(ctBytes, 8, 16);
byte[] ciphertextBytes = Arrays.copyOfRange(ctBytes, 16, ctBytes.length);

Derive the key+IV:

byte[] key = new byte[keySize/8];
byte[] iv = new byte[ivSize/8];
EvpKDF(password.getBytes("UTF-8"), keySize, ivSize, saltBytes, key, iv);

And decrypt the ciphertext:

Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "AES"),
new IvParameterSpec(iv));
byte[] recoveredPlaintextBytes = cipher.doFinal(ciphertextBytes);
String recoveredPlaintext = new String(recoveredPlaintextBytes);

Complete code:

public static void main(String[] args) throws UnsupportedEncodingException, GeneralSecurityException {
String ciphertext = "U2FsdGVkX1+0m/gle/XQX1shjnpveUrl1fO3oOlurPMlTks6+oQlEPfOrucihzEz";
String plaintext = "This is some example plaintext";
String password = "This is a very strong password";
int keySize = 256;
int ivSize = 128;

// var wordArray = WordArray.create([0x53616c74, 0x65645f5f]).concat(salt).concat(ciphertext);
byte[] ctBytes = Base64.getDecoder().decode(ciphertext.getBytes("UTF-8"));
System.out.println("Is salted: " + Arrays.equals(Arrays.copyOf(ctBytes, 8), new byte[]{0x53, 0x61, 0x6c, 0x74, 0x65, 0x64, 0x5f, 0x5f}));
System.out.println("Is salted: " + new String(Arrays.copyOf(ctBytes, 8)).equals("Salted__"));

byte[] saltBytes = Arrays.copyOfRange(ctBytes, 8, 16);
System.out.println("Salt matches: " + Arrays.equals(saltBytes, hexStringToByteArray("b49bf8257bf5d05f")));

byte[] ciphertextBytes = Arrays.copyOfRange(ctBytes, 16, ctBytes.length);
System.out.println("CT matches: " + Arrays.equals(ciphertextBytes, hexStringToByteArray("5b218e7a6f794ae5d5f3b7a0e96eacf3254e4b3afa842510f7ceaee722873133")));

byte[] key = new byte[keySize/8];
byte[] iv = new byte[ivSize/8];
EvpKDF(password.getBytes("UTF-8"), keySize, ivSize, saltBytes, key, iv);

Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "AES"), new IvParameterSpec(iv));
byte[] recoveredPlaintextBytes = cipher.doFinal(ciphertextBytes);
String recoveredPlaintext = new String(recoveredPlaintextBytes);

System.out.println("Recovered Plaintext: " + recoveredPlaintext);
System.out.println("Expected Plaintext: " + plaintext);
}

public static byte[] EvpKDF(byte[] password, int keySize, int ivSize, byte[] salt, byte[] resultKey, byte[] resultIv) throws NoSuchAlgorithmException {
return EvpKDF(password, keySize, ivSize, salt, 1, "MD5", resultKey, resultIv);
}

public static byte[] EvpKDF(byte[] password, int keySize, int ivSize, byte[] salt, int iterations, String hashAlgorithm, byte[] resultKey, byte[] resultIv) throws NoSuchAlgorithmException {
keySize = keySize / 32;
ivSize = ivSize / 32;
int targetKeySize = keySize + ivSize;
byte[] derivedBytes = new byte[targetKeySize * 4];
int numberOfDerivedWords = 0;
byte[] block = null;
MessageDigest hasher = MessageDigest.getInstance(hashAlgorithm);
while (numberOfDerivedWords < targetKeySize) {
if (block != null) {
hasher.update(block);
}
hasher.update(password);
block = hasher.digest(salt);
hasher.reset();

// Iterations
for (int i = 1; i < iterations; i++) {
block = hasher.digest(block);
hasher.reset();
}

System.arraycopy(block, 0, derivedBytes, numberOfDerivedWords * 4,
Math.min(block.length, (targetKeySize - numberOfDerivedWords) * 4));

numberOfDerivedWords += block.length/4;
}

System.arraycopy(derivedBytes, 0, resultKey, 0, keySize * 4);
System.arraycopy(derivedBytes, keySize * 4, resultIv, 0, ivSize * 4);

return derivedBytes; // key + iv
}

/**
* Copied from https://stackoverflow.com/a/140861
* */
public static byte[] hexStringToByteArray(String s) {
int len = s.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
+ Character.digit(s.charAt(i+1), 16));
}
return data;
}

JavaScript code:

var pw = "This is a very strong password";
var pt = "This is some example plaintext";
var encrypted = CryptoJS.AES.encrypt(pt, pw);
encrypted.toString(); // U2FsdGVkX1+0m/gle/XQX1shjnpveUrl1fO3oOlurPMlTks6+oQlEPfOrucihzEz
encrypted.salt.toString(); // b49bf8257bf5d05f
encrypted.ciphertext.toString(); // 5b218e7a6f794ae5d5f3b7a0e96eacf3254e4b3afa842510f7ceaee722873133

CryptoJs AES encryption and Java Decrypt

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. br>

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

AES encryption in java and decryption in javascript using CryptoJS

  • In the CryptoJS-documentation is explained which data types and parameters the CryptoJS.decrypt()-method expects and which encoders are available:

    • The key has to be passed to the CryptoJS.decrypt()-method as a WordArray. Since the key-data are Base64-encoded, they can be converted into a WordArray with the CryptoJS.enc.Base64.parse()-method.
    • The ciphertext can be passed to the CryptoJS.decrypt()-method as a WordArray inside a CipherParams-object. The Java-code stores the encrypted data in a file. Assuming that the string encryptedContent contains those data as hex-string (unfortunately, this does not emerge from the posted code, so that an assumption must be made here), they can be converted into a WordArray with the CryptoJS.enc.Hex.parse()-method and wrapped in a CipherParams-object.
    • The CryptoJS.decrypt()-method returns a WordArray which can be converted with the CryptoJS.enc.Utf8.stringify()-method into a string.
  • If the following plain text is contained in the input-file:

    This is the plain text which needs to be encrypted!

    the Java-code stores the following byte-sequence (= encrypted data) in the output-file:

    52F415AB673427C42278E8D6F34C16134D7E3FE7986500980ED4063F3CF51162592CE0F5412CCA0BC2DBAE3F2AEC2D585EE8D7

    The JavaScript-Code for the decryption is:

    var key = CryptoJS.enc.Base64.parse('uQsaW+WMUrjcsq1HMf+2JQ==');

    var encryptedContent = '52F415AB673427C42278E8D6F34C16134D7E3FE7986500980ED4063F3CF51162592CE0F5412CCA0BC2DBAE3F2AEC2D585EE8D7';
    var cipherParams = CryptoJS.lib.CipherParams.create({
    ciphertext: CryptoJS.enc.Hex.parse(encryptedContent)
    });

    var decrypted = CryptoJS.AES.decrypt(cipherParams, key, {
    mode: CryptoJS.mode.CTR,
    iv: key,
    padding: CryptoJS.pad.NoPadding
    });

    var decryptedText = CryptoJS.enc.Utf8.stringify(decrypted);
    console.log(decryptedText);

which displays the original plain text in the console. To run the code above at least CryptoJS-version 3.1.4 is needed (see versions, cdnjs).

Why can't java decrypt CryptoJS encrypted data?

Please find below a code pair that uses Javascript to en- and decrypt a string with static key in ECB mode. The Java code
is taking the ciphertext (= output of encryption function) from Javascript and decrypts the Base64 encoded ciphertext.

Security warning: Both codes are UNSECURE as they use the ECB mode and static encryption key.

Javascript output (see live example with this link: https://playcode.io/682378)

plaintext: Test
ciphertext: oNP8t53ZTi1WUptGCDh5NQ==
decryptedtext: Test

Java output:

ciphertextFromJavascript: oNP8t53ZTi1WUptGCDh5NQ==
decrypted: Test

Javascript code:

// *** Security warning **
// DO NOT USE THIS CODE IN PRODUCTION AS IT IS UNSECURE
// it uses ECB mode and static key

var plaintext = 'Test';
console.log('plaintext: ', plaintext);

/**
* Encryption
* @param word
* @returns {*}
*/
function encrypt(word){
const keyBase64 = "o9szYIOq1rRMiouNhNvaq96lqUvCekxR";
var key = CryptoJS.enc.Base64.parse(keyBase64);
var srcs = CryptoJS.enc.Utf8.parse(word);
var encrypted = CryptoJS.AES.encrypt(srcs, key, {mode:CryptoJS.mode.ECB,padding: CryptoJS.pad.Pkcs7});
return encrypted.toString();
}

/**
* Decrypt
* @param word
* @returns {*}
*/
function decrypt(word){
const keyBase64 = "o9szYIOq1rRMiouNhNvaq96lqUvCekxR";
var key = CryptoJS.enc.Base64.parse(keyBase64);
var decrypt = CryptoJS.AES.decrypt(word, key, {mode:CryptoJS.mode.ECB,padding: CryptoJS.pad.Pkcs7});
return CryptoJS.enc.Utf8.stringify(decrypt).toString();
}

var ciphertext = encrypt(plaintext);
console.log('ciphertext: ', ciphertext);
var decryptedtext = decrypt(ciphertext);
console.log('decryptedtext: ', decryptedtext);

Java code:

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;

public class Main {
public static void main(String[] args) throws Exception {
System.out.println("Why can't java decrypt CryptoJS encrypted data ?");
String ciphertextFromJavascript = "oNP8t53ZTi1WUptGCDh5NQ==";
System.out.println("ciphertextFromJavascript: " + ciphertextFromJavascript);
System.out.println("decrypted: " + decrypt(ciphertextFromJavascript));
}
public static String decrypt(String ciphertext) throws Exception {
SecretKey secretKey = getSecretKey("o9szYIOq1rRMiouNhNvaq96lqUvCekxR");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secretKey);
return new String(cipher.doFinal(Base64.getDecoder().decode(ciphertext)));
//return new String(cipher.doFinal(base64Decode("ASDASDADS")));
}

public static SecretKey getSecretKey(String secretKey) throws Exception {
byte[] decodeSecretKey = Base64.getDecoder().decode(secretKey);
//byte[] decodeSecretKey = base64Decode(secretKey);
return new SecretKeySpec(decodeSecretKey, 0, decodeSecretKey.length, "AES");
}
}

CryptoJS AES encryption and Java AES decryption

Disclaimer: Do not use encryption unless you understand encryption concepts including chaining mode, key derivation functions, IV and block size. And don't roll your own security scheme but stick to an established one. Just throwing in encryption algorithms doesn't mean an application has become any more secure.

CryptoJS implements the same key derivation function as OpenSSL and the same format to put the IV into the encrypted data. So all Java code that deals with OpenSSL encoded data applies.

Given the following Javascript code:

var text = "The quick brown fox jumps over the lazy dog. ;
var secret = "René Über";
var encrypted = CryptoJS.AES.encrypt(text, secret);
encrypted = encrypted.toString();
console.log("Cipher text: " + encrypted);

We get the cipher text:

U2FsdGVkX1+tsmZvCEFa/iGeSA0K7gvgs9KXeZKwbCDNCs2zPo+BXjvKYLrJutMK+hxTwl/hyaQLOaD7LLIRo2I5fyeRMPnroo6k8N9uwKk=

On the Java side, we have

String secret = "René Über";
String cipherText = "U2FsdGVkX1+tsmZvCEFa/iGeSA0K7gvgs9KXeZKwbCDNCs2zPo+BXjvKYLrJutMK+hxTwl/hyaQLOaD7LLIRo2I5fyeRMPnroo6k8N9uwKk=";

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

MessageDigest md5 = MessageDigest.getInstance("MD5");
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);

The result is:

The quick brown fox jumps over the lazy dog. br>

That's the text we started with. And emojis, accents and umlauts work as well.

GenerateKeyAndIV is a helper function that reimplements OpenSSL's key derivation function EVP_BytesToKey (see https://github.com/openssl/openssl/blob/master/crypto/evp/evp_key.c).

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

Note that you have to install the Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy. Otherwise, AES with key size of 256 won't work and throw an exception:

java.security.InvalidKeyException: Illegal key size

Update

I have replaced Ola Bini's Java code of EVP_BytesToKey, which I used in the first version of my answer, with a more idiomatic and easier to understand Java code (see above).

Also see How to decrypt file in Java encrypted with openssl command using AES?.

Decrypt Java AES/CBC/PKCS5Padding with CryptoJS

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>

AES CBC nodejs encryption and Java decryption

There are a few issues in the CryptoJS part, apply the following fixes:

const iv = CryptoJS.lib.WordArray.random(128 / 8); // no UTF8 encoding, this corrupts the data
...
encrypted = iv.concat(encrypted.ciphertext).toString(CryptoJS.enc.Base64); // typo in ciphertext; use base64 encoding because of the built-in support in Java
return encrypted; // return the data

In the Java code, IV and ciphertext must be separated, e.g.:

import java.nio.ByteBuffer;
import java.util.Base64;

...

String ivCiphertextB64 = "zuXYG1IbUNsiQYSTBUCv+Rx37EpJTw9SWfhRkL3yw2GncOvBDNU+w6UdB+ovfL2LyiCMYF1ptiXvuWynngc76Q=="; // data from CryptoJS code
byte[] ivCiphertext = Base64.getDecoder().decode(ivCiphertextB64);

ByteBuffer bufIvCiphertext = ByteBuffer.wrap(ivCiphertext);
byte[] iv = new byte[16];
bufIvCiphertext.get(iv);
byte[] ciphertext = new byte[bufIvCiphertext.remaining()];
bufIvCiphertext.get(ciphertext);
...

iv is passed in new IvParameterSpec(iv), ciphertext in aesCBC.doFinal(ciphertext). This decrypts the above ciphertext in The quick brown fox jumps over the lazy dog.


Edit:

Regarding your comment: In the modified CryptoJS code of your question the random IV is still Utf8 encoded. This Utf8 encoding is wrong because it corrupts the random data (s. e.g. here) and needs to be changed as already described above. Below you will find the complete, working CryptoJS code. A ciphertext generated with this code can be decrypted with the modified Java code.

function encrypt() {
const _key = CryptoJS.enc.Utf8.parse('ThirtyTwoBytes3$ThirtyTwoBytes3$');
const iv = CryptoJS.lib.WordArray.random(128 / 8);
let encrypted = CryptoJS.AES.encrypt(
CryptoJS.enc.Utf8.parse('The quick brown fox jumps over the lazy dog'),
_key,
{
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7,
}
);
encrypted = iv
.concat(encrypted.ciphertext)
.toString(CryptoJS.enc.Base64);
return encrypted;
}

document.getElementById("ct").innerHTML = encrypt();
<p style="font-family:'Courier New', monospace;" id="ct"></p>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"></script>


Related Topics



Leave a reply



Submit