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

AES CBC加解密:Angular与Java填充模式不兼容问题求助

Angular与Java AES-CBC加密解密兼容问题

问题背景

Angular端使用CryptoJS实现AES-CBC加密,采用PKCS7Padding填充模式,将密钥和密文传输至Java后端解密时遇到以下问题:

  • Java端使用"AES/CBC/PKCS7Padding"实例化Cipher时报错
  • 改用"AES/CBC/PKCS5Padding"后Java可正常运行,但Angular端切换为PKCS5Padding填充时会报错

原始代码

Angular端加密代码

import { Injectable } from '@angular/core';
import * as CryptoJS from 'crypto-js';

@Injectable({
    providedIn: 'root',
})
export class CryptoService {
  
  encrypt(message: string, clef: string): string {
    const salt = CryptoJS.SHA256("123456789123");

    const key = CryptoJS.PBKDF2(clef, salt, {
      keySize: 128 / 32,
      iterations: 1000
    });

    let iv = CryptoJS.enc.Utf8.parse(clef);
    let encrypted = CryptoJS.AES.encrypt(CryptoJS.enc.Utf8.parse(message.toString()), key, {
        keySize: 128 / 8,
        iv: iv,
        mode: CryptoJS.mode.CBC,
        padding: CryptoJS.pad.Pkcs7
    });

    return encrypted.toString();
  }

  generateKey(): string {
      let result = '';
      const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
      const charactersLength = characters.length;
      let counter = 0;
      while (counter < 16) {
          result += characters.charAt(Math.floor(Math.random() * charactersLength));
          counter += 1;
      }
      return result;
  }
}

Java端解密代码

import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.util.Base64;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;

public class AESUtilService {
    
    protected static final String salt = "123456789123";
    
    public SecretKey getKeyFromPassword(String password)
        throws NoSuchAlgorithmException, InvalidKeySpecException {
        
        SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
        KeySpec spec = new PBEKeySpec(password.toCharArray(), salt.getBytes(), 1000, 128/32);
        SecretKey secret = factory.generateSecret(spec);
        return secret;
    }
    public String decrypt(String cipherText, SecretKey key)
        throws NoSuchPaddingException, NoSuchAlgorithmException,
        InvalidAlgorithmParameterException, InvalidKeyException,
        BadPaddingException, IllegalBlockSizeException {
            
        String algorithm = "AES/CBC/PKCS7Padding";
        IvParameterSpec iv = new IvParameterSpec("MnTQLHcWumIKTXpQ".getBytes());
        
        Cipher cipher = Cipher.getInstance(algorithm);
        cipher.init(Cipher.DECRYPT_MODE, key, iv);
        byte[] plainText = cipher.doFinal(Base64.getDecoder().decode(cipherText));
        return new String(plainText);
    }
    
}

兼容解决方案

1. 填充模式兼容逻辑

对于AES算法(块大小固定为16字节),PKCS5Padding与PKCS7Padding实际效果完全等价:

  • PKCS5Padding仅针对8字节块(如DES)定义,Java标准库中当块大小为16字节时,PKCS5Padding的实现会自动适配PKCS7的填充逻辑
  • Angular端必须保留CryptoJS.pad.Pkcs7(CryptoJS的Pkcs5Padding仅支持8字节块,用于AES会报错)
  • Java端改用"AES/CBC/PKCS5Padding"即可与Angular的PKCS7Padding兼容

2. 修正密钥生成的一致性问题

原始代码中PBKDF2密钥生成存在两处不一致:

  • 盐值处理:Angular对盐字符串做了SHA256哈希,Java端需同步该逻辑
  • 密钥长度参数:Java的PBEKeySpec密钥长度单位是位,应设为128,而非128/32(错误值4)

3. 统一IV生成逻辑

Angular使用密码(clef)的UTF8字节作为IV,Java端不能硬编码IV,必须使用相同的密码生成IV(注意密码长度需为16字节,与AES块大小一致)

修正后的Java代码

import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.MessageDigest;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.util.Base64;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;

public class AESUtilService {
    
    protected static final String SALT_STR = "123456789123";
    
    public SecretKey getKeyFromPassword(String password)
        throws NoSuchAlgorithmException, InvalidKeySpecException {
        
        // 同步Angular的盐处理逻辑:对盐字符串做SHA256哈希
        MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
        byte[] salt = sha256.digest(SALT_STR.getBytes());
        
        SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
        // 密钥长度设为128位,与Angular一致
        KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 1000, 128);
        SecretKey secret = factory.generateSecret(spec);
        return secret;
    }
    
    public String decrypt(String cipherText, SecretKey key, String password)
        throws NoSuchPaddingException, NoSuchAlgorithmException,
        InvalidAlgorithmParameterException, InvalidKeyException,
        BadPaddingException, IllegalBlockSizeException {
            
        // 使用与Angular一致的IV:密码的UTF8字节(密码长度需为16字节)
        IvParameterSpec iv = new IvParameterSpec(password.getBytes());
        
        // 使用PKCS5Padding与Angular的PKCS7Padding兼容
        String algorithm = "AES/CBC/PKCS5Padding";
        Cipher cipher = Cipher.getInstance(algorithm);
        cipher.init(Cipher.DECRYPT_MODE, key, iv);
        byte[] plainText = cipher.doFinal(Base64.getDecoder().decode(cipherText));
        return new String(plainText);
    }
    
}

注意事项

  • 确保密码(clef)长度为16字节:因为AES的IV长度必须等于块大小(16字节),若密码长度不符,建议随机生成IV并与密文一起传输(如拼接在密文前),避免安全风险
  • 统一字符编码:两端均使用UTF-8处理字符串与字节数组的转换
  • 避免用密码直接作为IV:随机生成IV是更安全的实践,可将IV和密文打包后传输

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 08:25:58