AES CBC nodejs加密和Java解密

AES CBC nodejs encryption and Java decryption

节点: 我正在尝试在 java 中使用 NodeJs 中的 AES CBC PKCS7 和 PKCS5 解密文本。我收到错误消息:Given final block not properly padded. Such issues can arise if a bad key is used during decryption.

已更新

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

固定 IV 工作正常

NodeJs

var encKey = CryptoJS.enc.Utf8.parse("ThirtyTwoBytes3$ThirtyTwoBytes3$");
var encKeyIv = CryptoJS.enc.Utf8.parse("SixteenBytes6$");
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("SixteenBytes6$".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()));

CryptoJS 部分存在一些问题,应用以下修复:

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

在Java密码中,IV和密文必须分开,例如:

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

ivnew IvParameterSpec(iv)中传递,ciphertextaesCBC.doFinal(ciphertext)中传递。上面的密文解密为敏捷的棕狐跳过懒狗.


编辑:
关于您的评论:在您问题的修改后的 CryptoJS 代码中, random IV 仍然是 Utf8 编码的。这种 Utf8 编码是错误的,因为它破坏了随机数据(例如 here)并且需要如上所述进行更改。您将在下面找到完整的、有效的 CryptoJS 代码。使用此代码生成的密文可以使用修改后的 Java 代码进行解密。

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>