You need to enable JavaScript to run this app. Angular与Spring Boot两端DES加密解密不兼容问题求助
优惠活动
大模型
产品
解决方案
定价
更多

Angular与Spring Boot两端DES加密解密不兼容问题求助

问题根因

两端加解密不兼容的核心原因是密钥派生逻辑、算法参数、编码规则三个层面均未对齐,具体如下:

  • 密钥派生规则不一致:
    CryptoJS的DES.encrypt默认使用OpenSSL标准的EVP_BytesToKey算法派生真实DES密钥,哈希算法为MD5、迭代次数固定为1,且加密时会自动生成随机8字节盐,生成的密文以Salted__为前缀标识;而Java端使用固定硬编码的盐、迭代次数为3,基于PBEKeySpec派生密钥,两端生成的真实DES密钥完全不同,自然会出现BadPadding异常。
  • 编码规则不一致:
    Java加密返回时使用encodeBase64URLSafeString生成URL安全的Base64编码,会将+替换为-、/替换为_且去掉末尾=填充,而CryptoJS默认输出标准Base64编码,编码规则不匹配也会导致解密时数据解析错误。
  • 加密模式与填充规则未显式对齐:
    CryptoJS的DES默认使用CBC模式、PKCS7填充,IV向量从密码和盐共同派生,若Java端的PBE算法参数未和该规则匹配,也会导致解密失败。
解决方案

推荐选择以下任意一种方案对齐两端规则即可:

方案1:修改Angular端逻辑对齐Java端

Angular端不再使用默认的加密接口,手动实现和Java端一致的密钥派生规则,指定固定盐、迭代次数3、算法为PBEWithMD5AndDES,示例代码如下:

convertText() {
  const text = this.textToConvert.trim();
  const pwd = this.password.trim();
  if (!text || !pwd) {
    this.conversionOutput = 'Please fill the textboxes.';
    return;
  }
  // 与Java端一致的固定盐
  const salt = CryptoJS.lib.WordArray.create([0xA99BC832, 0x5635E303]);
  const key = CryptoJS.PBKDF2(pwd, salt, {
    keySize: 64/32,
    iterations: 3,
    hasher: CryptoJS.algo.MD5
  });
  // PBEWithMD5AndDES的IV派生规则和Java对齐
  const iv = CryptoJS.lib.WordArray.create([0x00000000, 0x00000000]);
  if (this.encryptMode) {
    const encrypted = CryptoJS.DES.encrypt(text, key, {
      iv: iv,
      padding: CryptoJS.pad.PKCS5,
      mode: CryptoJS.mode.CBC
    });
    // 输出URL安全Base64和Java对齐
    this.conversionOutput = encrypted.ciphertext.toString(CryptoJS.enc.Base64)
      .replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/g, '');
  } else {
    // 先把URL安全Base64转成标准Base64
    const standardBase64 = text.replace(/-/g, '+').replace(/_/g, '/');
    const decrypted = CryptoJS.DES.decrypt(standardBase64, key, {
      iv: iv,
      padding: CryptoJS.pad.PKCS5,
      mode: CryptoJS.mode.CBC
    });
    this.conversionOutput = decrypted.toString(CryptoJS.enc.Utf8);
  }
}

方案2:修改Java端逻辑对齐CryptoJS默认规则

Java端解析CryptoJS生成的密文中的随机盐,使用和CryptoJS一致的EVP_BytesToKey派生密钥和IV,示例核心代码如下:

// 解密CryptoJS生成的DES密文
public static String decryptForCryptoJs(String cipherText, String password) throws Exception {
    byte[] cipherBytes = Base64.decodeBase64(cipherText);
    // 提取CryptoJS密文中的盐:前8字节是Salted__,第9-16字节是盐
    byte[] salt = Arrays.copyOfRange(cipherBytes, 8, 16);
    byte[] realCipher = Arrays.copyOfRange(cipherBytes, 16, cipherBytes.length);
    // 实现EVP_BytesToKey派生密钥和IV,迭代次数1,MD5哈希
    byte[] keyAndIv = evpBytesToKey(password.getBytes(StandardCharsets.UTF_8), salt, 48, 1);
    byte[] key = Arrays.copyOfRange(keyAndIv, 0, 8);
    byte[] iv = Arrays.copyOfRange(keyAndIv, 8, 16);
    // DES/CBC/PKCS5Padding解密
    Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
    cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "DES"), new IvParameterSpec(iv));
    byte[] result = cipher.doFinal(realCipher);
    return new String(result, StandardCharsets.UTF_8);
}

// 实现OpenSSL的EVP_BytesToKey算法
private static byte[] evpBytesToKey(byte[] password, byte[] salt, int length, int iterations) throws NoSuchAlgorithmException {
    MessageDigest md = MessageDigest.getInstance("MD5");
    byte[] result = new byte[length];
    int pos = 0;
    byte[] digest = new byte[0];
    while (pos < length) {
        md.update(digest);
        md.update(password);
        md.update(salt);
        digest = md.digest();
        for (int i = 1; i < iterations; i++) {
            digest = md.digest(digest);
        }
        int copyLen = Math.min(digest.length, length - pos);
        System.arraycopy(digest, 0, result, pos, copyLen);
        pos += copyLen;
    }
    return result;
}

修改后调用decryptForCryptoJs方法即可正常解密Angular端生成的密文。

内容的提问来源于stack exchange,提问作者user9634982

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.17 04:15:01