C# 基于密码的AES加解密算法转Java实现,相关方法替代咨询
C# AES加解密逻辑Java迁移方案
现有Java代码问题梳理
- 密钥衍生逻辑错误:C#中
Rfc2898DeriveBytes仅生成128位AES密钥,IV是独立生成的随机值,现有Java代码从PBKDF2输出结果中截取IV,和C#逻辑完全不匹配 - 密文结构错误:C#最终返回的Base64密文是「16字节盐 + 16字节随机IV + 实际密文」的拼接结果,现有Java代码仅返回了实际密文,缺失前两段数据
- PBKDF2参数错误:现有代码申请了384位衍生结果,实际只需要128位密钥即可
对应C#类的Java完整实现
import javax.crypto.Cipher; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.PBEKeySpec; import javax.crypto.spec.SecretKeySpec; import java.security.SecureRandom; import java.util.Base64; public class StringCipher { // 对应C#的密钥长度配置,单位为比特 private static final int KEY_SIZE = 128; // 和C#的迭代次数保持一致 private static final int DERIVATION_ITERATIONS = 1000; // 128比特对应16字节 private static final int BLOCK_SIZE = KEY_SIZE / 8; private static byte[] generate128BitsOfRandomEntropy() { byte[] randomBytes = new byte[BLOCK_SIZE]; new SecureRandom().nextBytes(randomBytes); return randomBytes; } // 加密方法和C#逻辑完全对齐 public static String encrypt(String plainText, String passPhrase) throws Exception { byte[] salt = generate128BitsOfRandomEntropy(); byte[] iv = generate128BitsOfRandomEntropy(); byte[] plainTextBytes = plainText.getBytes(java.nio.charset.StandardCharsets.UTF_8); // 对应C#的Rfc2898DeriveBytes,默认PBKDF2WithHmacSHA1算法和C#完全一致 SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); PBEKeySpec spec = new PBEKeySpec(passPhrase.toCharArray(), salt, DERIVATION_ITERATIONS, KEY_SIZE); byte[] keyBytes = factory.generateSecret(spec).getEncoded(); SecretKeySpec secretKey = new SecretKeySpec(keyBytes, "AES"); // 对应C# RijndaelManaged配置,AES是Rijndael块大小固定128位的标准实现,PKCS5Padding和C#的PKCS7Padding兼容 Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(iv)); byte[] cipherTextBytes = cipher.doFinal(plainTextBytes); // 拼接盐、IV、密文,和C#输出结构完全一致 byte[] result = new byte[BLOCK_SIZE * 2 + cipherTextBytes.length]; System.arraycopy(salt, 0, result, 0, BLOCK_SIZE); System.arraycopy(iv, 0, result, BLOCK_SIZE, BLOCK_SIZE); System.arraycopy(cipherTextBytes, 0, result, BLOCK_SIZE * 2, cipherTextBytes.length); return Base64.getEncoder().encodeToString(result); } // 解密方法和C#逻辑完全对齐 public static String decrypt(String cipherText, String passPhrase) throws Exception { byte[] cipherDataWithSaltIv = Base64.getDecoder().decode(cipherText); // 提取前16字节盐 byte[] salt = new byte[BLOCK_SIZE]; System.arraycopy(cipherDataWithSaltIv, 0, salt, 0, BLOCK_SIZE); // 提取16-32字节作为IV byte[] iv = new byte[BLOCK_SIZE]; System.arraycopy(cipherDataWithSaltIv, BLOCK_SIZE, iv, 0, BLOCK_SIZE); // 提取剩余部分为实际密文 byte[] cipherTextBytes = new byte[cipherDataWithSaltIv.length - BLOCK_SIZE * 2]; System.arraycopy(cipherDataWithSaltIv, BLOCK_SIZE * 2, cipherTextBytes, 0, cipherTextBytes.length); // 生成匹配的密钥 SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); PBEKeySpec spec = new PBEKeySpec(passPhrase.toCharArray(), salt, DERIVATION_ITERATIONS, KEY_SIZE); byte[] keyBytes = factory.generateSecret(spec).getEncoded(); SecretKeySpec secretKey = new SecretKeySpec(keyBytes, "AES"); // 执行解密 Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(iv)); byte[] plainTextBytes = cipher.doFinal(cipherTextBytes); return new String(plainTextBytes, java.nio.charset.StandardCharsets.UTF_8); } }
适配说明
- C#的
RijndaelManaged当块大小设置为128时就是标准AES算法,Java的AES算法实现完全兼容 - C#的
PaddingMode.PKCS7和Java的PKCS5Padding在块大小为8的倍数时完全等价,AES块大小固定为16字节,无需额外适配 - 本实现和原版C#代码完全互通,C#加密的密文可以直接用Java解密,反之亦然
内容的提问来源于stack exchange,提问作者bjalexmdd
相关产品推荐
相关产品推荐

