You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

CryptoJS AES解密逻辑迁移至Java后端遇填充错误求助

AES解密逻辑从CryptoJS迁移到Java后端的问题排查

需要将前端可正常运行的CryptoJS AES解密逻辑迁移至Java后端,前端代码、自行编写的Java代码、运行错误如下,期望解密得到JSON对象{"name": "Burak", "surName": "Bayraktaroglu"},请求排查问题。

前端可运行的CryptoJS代码

const token = "U2FsdGVkX1+6YueVRKp6h0dZfk/a8AC9vyFfAjxD4nb7mXsKrM7rI7xZ0OgrF1sShHYNLMJglz4+67n/I7P+fg=="
const key = "p80a0811-47db-2c39-bcdd-4t3g5h2d5d1a"

// Decrypt
const iv = CryptoJS.enc.Utf8.parse(key);
const decryptedToken = CryptoJS.AES.decrypt(token, key, {
  keySize: 16,
  iv,
  mode: CryptoJS.mode.CBC,
  padding: CryptoJS.pad.Pkcs7,
});

const stringToken = decryptedToken.toString(CryptoJS.enc.Utf8);
const dataToUse = JSON.parse(stringToken)

console.log("decryptedToken => ", stringToken);
console.log("data => ",dataToUse);

自行编写的Java解密代码

public String decrypt() {
  String finalToken = null;
  Cipher cipher;
  try {
    String token = "U2FsdGVkX1+6YueVRKp6h0dZfk/a8AC9vyFfAjxD4nb7mXsKrM7rI7xZ0OgrF1sShHYNLMJglz4+67n/I7P+fg==";
    String key = "p80a0811-47db-2c39-bcdd-4t3g5h2d5d1a";
    Key skeySpec = new SecretKeySpec(Arrays.copyOf(key.getBytes(), 16), "AES");
    byte[] iv = Arrays.copyOf(key.getBytes(StandardCharsets.UTF_8), 16);
    IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
    byte[] bytesToDecrypt = Base64.decodeBase64(token.getBytes());
    cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
    cipher.init(Cipher.DECRYPT_MODE, skeySpec, ivParameterSpec);
    byte[] decrypted = cipher.doFinal(bytesToDecrypt);
    finalToken = new String(decrypted);
    log.info("Decrypt token succesfully {}", finalToken);
  } catch (NoSuchAlgorithmException
      | InvalidAlgorithmParameterException
      | IllegalBlockSizeException
      | NoSuchPaddingException
      | BadPaddingException
      | InvalidKeyException e) {
    e.printStackTrace();
    log.error("Error decrypting");
  }

  return finalToken;
}

运行错误信息

javax.crypto.BadPaddingException: pad block corrupted

若不截断密钥和IV至16字节则会出现长度错误,不确定密钥和IV的设置是否正确。


问题原因及修复方案

问题核心是CryptoJS与Java原生AES实现的密钥处理、密文格式逻辑不一致:

  1. CryptoJS传入字符串密钥时,会自动通过MD5哈希派生128位密钥,而非直接截断字符串;
  2. 给定的token是CryptoJS特有的格式化密文(开头U2FsdGVkX1是加盐标识),包含盐值,不能直接Base64解码后解密。

修复后的Java代码

import org.apache.commons.codec.binary.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.Arrays;

public class AESDecryptor {
    public String decrypt() {
        String finalToken = null;
        try {
            String token = "U2FsdGVkX1+6YueVRKp6h0dZfk/a8AC9vyFfAjxD4nb7mXsKrM7rI7xZ0OgrF1sShHYNLMJglz4+67n/I7P+fg==";
            String key = "p80a0811-47db-2c39-bcdd-4t3g5h2d5d1a";

            // 解析CryptoJS格式化密文:提取盐值和实际密文
            byte[] decodedToken = Base64.decodeBase64(token.getBytes(StandardCharsets.UTF_8));
            byte[] salt = Arrays.copyOfRange(decodedToken, 8, 16); // 跳过前8字节的"Salted__"标识,取后续8字节盐值
            byte[] ciphertext = Arrays.copyOfRange(decodedToken, 16, decodedToken.length);

            // 按照CryptoJS规则派生密钥和IV:两次MD5哈希
            MessageDigest md5 = MessageDigest.getInstance("MD5");
            byte[] keyPlusSalt = concat(key.getBytes(StandardCharsets.UTF_8), salt);
            byte[] hash1 = md5.digest(keyPlusSalt);
            byte[] hash2 = md5.digest(concat(hash1, keyPlusSalt));
            byte[] finalKey = Arrays.copyOf(hash1, 16); // 128位AES密钥
            byte[] finalIv = Arrays.copyOf(hash2, 16); // 128位IV

            // 初始化Cipher执行解密
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); // Java中PKCS5Padding等价于PKCS7Padding
            SecretKeySpec keySpec = new SecretKeySpec(finalKey, "AES");
            IvParameterSpec ivSpec = new IvParameterSpec(finalIv);
            cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);

            byte[] decryptedBytes = cipher.doFinal(ciphertext);
            finalToken = new String(decryptedBytes, StandardCharsets.UTF_8);
            System.out.println("Decrypted token: " + finalToken);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return finalToken;
    }

    // 辅助方法:拼接两个字节数组
    private byte[] concat(byte[] a, byte[] b) {
        byte[] result = new byte[a.length + b.length];
        System.arraycopy(a, 0, result, 0, a.length);
        System.arraycopy(b, 0, result, a.length, b.length);
        return result;
    }
}

关键说明

  • CryptoJS格式化密文结构:Salted__(8字节标识) + 8字节盐值 + 实际密文,需先拆分提取;
  • 密钥派生规则:对密钥字符串+盐值做两次MD5哈希,第一次哈希结果作为AES密钥,第二次作为IV;
  • Java中PKCS7Padding与PKCS5Padding等价(AES块大小为16字节,PKCS5Padding支持填充至该大小),直接使用AES/CBC/PKCS5Padding即可。

内容的提问来源于stack exchange,提问作者Eliana Chávez Valencia

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 03:45:33