2

NodeJs: I am trying decrypt text using AES CBC PKCS7 in NodeJs and PKCS5 in java. I am getting error: Given final block not properly padded. Such issues can arise if a bad key is used during decryption.

UPDATED

encrypt() {
  var key = 'ThirtyTwoBytes3$ThirtyTwoBytes3$';     
  var iv = CryptoJS.enc.Utf8.parse(CryptoJS.lib.WordArray.random(128 / 8));
  let utf8Pass = CryptoJS.enc.Utf8.parse("Hello");
  let encVal = CryptoJS.AES.encrypt(utf8Pass.toString(), key, {mode: 
               CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7, iv: iv});
  return iv.concat(encVal.ciphertext).toString(CryptoJS.enc.Base64);
  }

Java:

byte[] keyB = "ThirtyTwoBytes3$ThirtyTwoBytes3$".getBytes(StandardCharsets.UTF_8);
IvParameterSpec ivParameterSpec = new IvParameterSpec(encryptedText.getBytes(), 0, 16);
SecretKeySpec key = new SecretKeySpec(keyB, "AES");

Cipher aesCBC = Cipher.getInstance("AES/CBC/PKCS5Padding");
aesCBC.init(Cipher.DECRYPT_MODE, key, ivParameterSpec);
byte[] decryptedData = Base64.getDecoder().decode(encryptedText);           
decryptedText = new String(Hex.decodeHex(new String(aesCBC.doFinal(decryptedData), StandardCharsets.UTF_8).toCharArray()));

Fixed IV is working fine

NodeJs

var encKey = CryptoJS.enc.Utf8.parse("ThirtyTwoBytes3$ThirtyTwoBytes3$");
var encKeyIv = CryptoJS.enc.Utf8.parse("$1SixteenBytes6$");
let utf8Pass = CryptoJS.enc.Utf8.parse("Hello");
let encVal = CryptoJS.AES.encrypt(utf8Pass.toString(), encKey, {mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7, iv: encKeyIv});
encVal.ciphertext.toString();

Java:

SecretKey key = new SecretKeySpec("ThirtyTwoBytes3$ThirtyTwoBytes3$".getBytes(), "AES");
AlgorithmParameterSpec iv = new IvParameterSpec("$1SixteenBytes6$".getBytes());
byte[] decodeBase64 = Base64.decode(encVal);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, key, iv);
decString = new String(Hex.decodeHex(new String(cipher.doFinal(decodeBase64), "UTF-8").toCharArray()));
Pat
  • 535
  • 1
  • 16
  • 41
  • Shouldn't it be `AES/CBC/PKCS7Padding`? Also it seems like you take 16 bytes of the cipher as IV, but then decrypt the whole thing? If your CryptoJS appends iv to the beginning of cipher then the cipher can't contain the IV. Typically though it would be sent separately? – zaitsman Jan 07 '22 at 04:39
  • @zaitsman in Java PKCS7 and PKCS5 are same. Regarding IV, yes that is what I thought. In encrypted string it should be prefixed, in my java code if you see I am reading first 16 as IV. But in NodeJs do i need append it explicitly before i return? I thought Crypto will take care – Pat Jan 07 '22 at 04:46
  • 1
    I would try hardcoding an IV to begin with. If you can decrypt you will know you need to send it along yourself. Also, re: PKCS7 is the same as PKCS5 it is not strictly speaking so: https://stackoverflow.com/questions/20770072/aes-cbc-pkcs5padding-vs-aes-cbc-pkcs7padding-with-256-key-size-performance-java – zaitsman Jan 07 '22 at 04:52
  • 1
    _...But in NodeJs do i need append it explicitly before i return? I thought Crypto will take care..._ In your use case `encrypted.toString()` contains the Base64 encoded ciphertext _without_ IV! You have to do the concatenation yourself. Also, in Java PKCS#5 means PKCS#7 in this context. – Topaco Jan 07 '22 at 07:14
  • @Topaco I did a concatenation of IV and cypherText, but there is no change in the error message. Tried fixed IV, which is working fine – Pat Jan 07 '22 at 19:06

1 Answers1

1

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>
Topaco
  • 40,594
  • 4
  • 35
  • 62
  • Thanks, I am able to decrypt the ciphertext you posted above in JAVA. But in NodeJs, even after making the changes you suggested the encrypted string doesn't look complete and not able to decrypted in Java. Node Js: "Hello" is encrypted as "OThhODYzZDY1M2Q4YjBjNTZiMWUxYmM1NjlmMzFkYmbp9rKO5ux5gh9xpYiK6e8+" – Pat Jan 08 '22 at 02:32
  • 1
    @Pat - Have a look at the Edit section of my answer please. – Topaco Jan 08 '22 at 08:10
  • thanks CryptoJs code was having issue as you mentioned i was using utf8. Works fine now. – Pat Jan 08 '22 at 22:15