如何将Java AES/GCM/NoPadding加密算法移植到C#实现跨系统互解密
AES-GCM跨Java与.NET 7互加解密实现问题
我负责的跨系统通信项目中,对方Java系统采用AES-GCM加密传输内容,我方基于.NET 7的C#系统需要解密该内容。尝试用BouncyCastle.Cryptography框架模仿Java实现逻辑,但未成功,现求助解决互加解密的兼容问题。
已知信息:
- 密钥:
esp4tgsltzA2fml2 - Java加密示例结果:
cOYavtcA/pvUNgU1tYiNzEOOnazFVsfce/ApmHz0gAsq
Java端加密解密实现代码
import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; import javax.crypto.spec.GCMParameterSpec; import javax.crypto.spec.SecretKeySpec; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.util.Base64; public class AESUtil { private static final String KEY_ALGORITHM_AES = "AES"; private static final String DEFAULT_CIPHER_ALGORITHM = "AES/GCM/NoPadding"; private static final String CHARSET = "UTF-8"; public static String aesEncrypt(String content, String encryptPass) { try { byte[] iv = new byte[12]; SecureRandom secureRandom = new SecureRandom(); secureRandom.nextBytes(iv); byte[] contentBytes = content.getBytes(CHARSET); Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM); GCMParameterSpec params = new GCMParameterSpec(128, iv); cipher.init(Cipher.ENCRYPT_MODE, getSecretKey(encryptPass), params); byte[] encryptData = cipher.doFinal(contentBytes); assert encryptData.length == contentBytes.length + 16; byte[] message = new byte[12 + contentBytes.length + 16]; System.arraycopy(iv, 0, message, 0, 12); System.arraycopy(encryptData, 0, message, 12, encryptData.length); return Base64.getEncoder().encodeToString(message); } catch (Exception e) { e.printStackTrace(); } return null; } public static String aesDecrypt(String base64Content, String encryptPass) { try { byte[] content = Base64.getDecoder().decode(base64Content); if (content.length < 12 + 16) { throw new IllegalArgumentException(); } GCMParameterSpec params = new GCMParameterSpec(128, content, 0, 12); Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM); cipher.init(Cipher.DECRYPT_MODE, getSecretKey(encryptPass), params); byte[] decryptData = cipher.doFinal(content, 12, content.length - 12); return new String(decryptData, CHARSET); } catch (Exception e) { System.out.println("error:" + e.getMessage()); } return null; } private static SecretKeySpec getSecretKey(String encryptPass) throws NoSuchAlgorithmException { KeyGenerator kg = KeyGenerator.getInstance(KEY_ALGORITHM_AES); SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG"); secureRandom.setSeed(encryptPass.getBytes()); kg.init(128, secureRandom); SecretKey secretKey = kg.generateKey(); return new SecretKeySpec(secretKey.getEncoded(), KEY_ALGORITHM_AES); } }
我编写的C#代码(存在问题)
using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Security; using System.Text; static string Encrypt(string content, string encryptPass) { var size = 128; var plainTextData = Encoding.UTF8.GetBytes(content); CipherKeyGenerator keyGen = new CipherKeyGenerator(); var sr = SecureRandom.GetInstance("SHA1PRNG"); sr.SetSeed(Encoding.UTF8.GetBytes(encryptPass)); keyGen.Init(new KeyGenerationParameters(sr, size)); KeyParameter keyParam = keyGen.GenerateKeyParameter(); var cipher = CipherUtilities.GetCipher("AES/GCM/NoPadding"); byte[] iv = new byte[12]; var secureRandom = new SecureRandom(); secureRandom.NextBytes(iv); AeadParameters keyParamAead = new AeadParameters(keyParam, size, iv); cipher.Init(true, keyParamAead); int outputSize = cipher.GetOutputSize(plainTextData.Length); byte[] cipherTextData = new byte[outputSize]; int result = cipher.ProcessBytes(plainTextData, 0, plainTextData.Length, cipherTextData, 0); cipher.DoFinal(cipherTextData, result); if (cipherTextData.Length != plainTextData.Length + 16) { Console.WriteLine("error!"); return string.Empty; } else { cipher.Init(false, keyParamAead); outputSize = cipher.GetOutputSize(cipherTextData.Length); var decryptData = new byte[outputSize]; int decryptResult = cipher.ProcessBytes(cipherTextData, 0, cipherTextData.Length, decryptData, 0); cipher.DoFinal(decryptData, decryptResult); if (Encoding.UTF8.GetString(decryptData) != content) { Console.WriteLine($"error :{Encoding.UTF8.GetString(decryptData)}"); return string.Empty; } } byte[] message = new byte[12 + plainTextData.Length + 16]; Buffer.BlockCopy(iv, 0, message, 0, 12); Buffer.BlockCopy(cipherTextData, 0, message, 12, cipherTextData.Length); return Convert.ToBase64String(message); } static string Decrypt(string encryptString, string encryptPass) { var encryptedData = Encoding.UTF8.GetBytes(encryptString); CipherKeyGenerator keyGen = new CipherKeyGenerator(); var sr = SecureRandom.GetInstance("SHA1PRNG"); sr.SetSeed(Encoding.UTF8.GetBytes(encryptPass)); keyGen.Init(new KeyGenerationParameters(sr, 128)); KeyParameter keyParam = keyGen.GenerateKeyParameter(); var cipher = CipherUtilities.GetCipher("AES/GCM/NoPadding"); var iv = new byte[12]; Buffer.BlockCopy(encryptedData, 0, iv, 0, 12); AeadParameters keyParamAead = new AeadParameters(keyParam, 128, iv); cipher.Init(true, keyParamAead); var realEncryptedData = new byte[encryptedData.Length - 12]; var outputSize = cipher.GetOutputSize(realEncryptedData.Length); var decryptData = new byte[outputSize]; int decryptResult = cipher.ProcessBytes(realEncryptedData, 0, realEncryptedData.Length, decryptData, 0); cipher.DoFinal(decryptData, decryptResult); return Encoding.UTF8.GetString(decryptData); }
问题分析与修正后的C#代码
原C#代码存在以下核心问题:
- 解密模式初始化错误:解密需使用
false标识解密模式,原代码误用了true(加密模式) - 密文解码错误:未对Base64格式的加密内容做解码,直接转成UTF8字节数组导致数据损坏
- 冗余逻辑干扰:加密函数中额外的自校验逻辑无必要,且可能引发状态异常
修正后的C#代码:
using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Security; using System.Text; public static class AESGCMUtil { private const string CipherAlgorithm = "AES/GCM/NoPadding"; private const int KeySize = 128; private const int IvSize = 12; private const int TagSize = 128; public static string Encrypt(string content, string encryptPass) { var plainBytes = Encoding.UTF8.GetBytes(content); // 生成与Java兼容的密钥 var keyParam = GenerateKey(encryptPass); // 生成12字节标准IV var iv = new byte[IvSize]; new SecureRandom().NextBytes(iv); // 初始化加密器 var cipher = CipherUtilities.GetCipher(CipherAlgorithm); cipher.Init(true, new AeadParameters(keyParam, TagSize, iv)); // 执行加密 var cipherBytes = cipher.DoFinal(plainBytes); // 拼接IV + 密文+标签 var resultBytes = new byte[IvSize + cipherBytes.Length]; Buffer.BlockCopy(iv, 0, resultBytes, 0, IvSize); Buffer.BlockCopy(cipherBytes, 0, resultBytes, IvSize, cipherBytes.Length); return Convert.ToBase64String(resultBytes); } public static string Decrypt(string base64Content, string encryptPass) { // 解码Base64加密内容 var contentBytes = Convert.FromBase64String(base64Content); if (contentBytes.Length < IvSize + TagSize / 8) throw new ArgumentException("无效的加密内容"); // 提取IV var iv = new byte[IvSize]; Buffer.BlockCopy(contentBytes, 0, iv, 0, IvSize); // 提取密文+标签部分 var cipherBytes = new byte[contentBytes.Length - IvSize]; Buffer.BlockCopy(contentBytes, IvSize, cipherBytes, 0, cipherBytes.Length); // 生成密钥 var keyParam = GenerateKey(encryptPass); // 初始化解密器 var cipher = CipherUtilities.GetCipher(CipherAlgorithm); cipher.Init(false, new AeadParameters(keyParam, TagSize, iv)); // 执行解密 var plainBytes = cipher.DoFinal(cipherBytes); return Encoding.UTF8.GetString(plainBytes); } private static KeyParameter GenerateKey(string encryptPass) { var keyGen = new CipherKeyGenerator(); var sr = SecureRandom.GetInstance("SHA1PRNG"); sr.SetSeed(Encoding.UTF8.GetBytes(encryptPass)); keyGen.Init(new KeyGenerationParameters(sr, KeySize)); return keyGen.GenerateKeyParameter(); } }
验证测试
使用提供的密钥和Java加密结果验证:
var key = "esp4tgsltzA2fml2"; var javaEncrypted = "cOYavtcA/pvUNgU1tYiNzEOOnazFVsfce/ApmHz0gAsq"; var decrypted = AESGCMUtil.Decrypt(javaEncrypted, key); Console.WriteLine(decrypted); // 输出对应明文
内容的提问来源于stack exchange,提问作者danny
相关产品推荐
相关产品推荐

