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

如何将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#代码存在以下核心问题:

  1. 解密模式初始化错误:解密需使用false标识解密模式,原代码误用了true(加密模式)
  2. 密文解码错误:未对Base64格式的加密内容做解码,直接转成UTF8字节数组导致数据损坏
  3. 冗余逻辑干扰:加密函数中额外的自校验逻辑无必要,且可能引发状态异常

修正后的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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 10:55:56