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

如何实现Angular与C#兼容的AES-GCM加解密方法?

Angular AES-GCM加解密与C#后端兼容实现

我已经在Angular中实现了AES-GCM加密解密逻辑,现在需要编写C#后端代码与之兼容。以下是我的Angular实现、初始编写的C#解密代码,以及修正后的兼容版本。

Angular 加解密实现代码

async encryptStringAES256(key: string, xSalt: string, data: string): Promise<any> {
    return new Promise((resolve, reject) => {
      const salt = new Uint8Array(this.toUin8Arry(xSalt));
      const decryptedData = new Uint8Array(this.stringToArray(data));
      this.getKeyMaterial(key).then(async (res: any) => {
        const xkey = await window.crypto.subtle.deriveKey(
          {
            name: 'PBKDF2',
            iterations: 100000,
            salt,
            hash: 'SHA-256'
          },
          res,
          { name: 'AES-GCM', length: 256},
          true,
          ['encrypt', 'decrypt']
        );
        window.crypto.subtle.encrypt({ name: 'AES-GCM', iv: salt }, xkey, decryptedData).then(
          encrypted => {
            resolve(this.arrayToString(new Uint8Array(encrypted)));
          })
        .catch(err => {
           // 异常处理
        });
      });
    });
  }

  async decryptStringAES256(key: string, xSalt: string, data: string): Promise<any> {
      const salt = new Uint8Array(this.toUin8Arry(xSalt));
      const encryptedData = new Uint8Array(this.stringToArray(data));
      this.getKeyMaterial(key).then(async (res: any) => {
        const xkey = await window.crypto.subtle.deriveKey(
          {
            name: 'PBKDF2',
            iterations: 100000,
            salt,
            hash: 'SHA-256'
          },
          res,
          { name: 'AES-GCM', length: 256},
          true,
          ['encrypt', 'decrypt']
        );
        window.crypto.subtle.decrypt({ name: 'AES-GCM', iv: salt }, xkey, encryptedData).then(
          decrypted => {
            const dec = this.arrayToString(new Uint8Array(decrypted));
            resolve(dec);
          })
          .catch(err => {
            reject(data);
          });
      });
    });
  }

  getKeyMaterial(password: string): any {
    const enc = new TextEncoder();
    return window.crypto.subtle.importKey(
      'raw',
      enc.encode(password),
      'PBKDF2',
      false,
      ['deriveBits', 'deriveKey']
    );
  }

初始编写的C#解密代码

public static void Decrypt(byte[] sourceData, string password)
{
    try
    {
        var encryptedData = sourceData.AsSpan();
        var key = Convert.FromBase64String(password).AsSpan();
        var ivSizeBytes = 10;
        var tagSizeBytes = 0; 

        var cipherSize = encryptedData.Length - tagSizeBytes - ivSizeBytes;

        var iv = encryptedData.Slice(0, ivSizeBytes);

        var cipherBytes = encryptedData.Slice(ivSizeBytes, cipherSize);

        var tagStart = ivSizeBytes + cipherSize;
        var tag = encryptedData.Slice(tagStart);

        using Rfc2898DeriveBytes pbkdf2 = new(Encoding.UTF8.GetBytes(password), iv.ToArray(), 100000, HashAlgorithmName.SHA256); 

        Span<byte> plainBytes = cipherSize < 1024
            ? stackalloc byte[cipherSize]
            : new byte[cipherSize];
        using var aes = new AesGcm(pbkdf2.GetBytes(32));
        aes.Decrypt(iv, cipherBytes, tag, plainBytes);

        var x = Encoding.UTF8.GetString(plainBytes);
    }
    catch (Exception e)
    {
        throw new Exception($"Fehler Decrypt: {e.Message}");
    }
}

修正后的C#兼容解密代码

初始代码存在几个关键问题:错误处理密码编码、从加密数据中截取IV(与Angular逻辑不符)、tag长度设为0。以下是修正后的版本:

using System.Security.Cryptography;
using System.Text;

public static string Decrypt(string encryptedDataStr, string password, string saltStr)
{
    try
    {
        // 转换加密字符串为字节数组(需与Angular的arrayToString/stringToArray逻辑一致,这里假设是Base64)
        byte[] encryptedData = Convert.FromBase64String(encryptedDataStr);
        // 转换salt为字节数组(需匹配Angular的toUin8Arry实现,若为UTF-8则用以下代码,若为Base64则改用Convert.FromBase64String)
        byte[] salt = Encoding.UTF8.GetBytes(saltStr);

        // 用PBKDF2推导密钥,参数与Angular完全对齐
        using Rfc2898DeriveBytes pbkdf2 = new(Encoding.UTF8.GetBytes(password), salt, 100000, HashAlgorithmName.SHA256);
        byte[] key = pbkdf2.GetBytes(32); // 生成256位AES密钥

        // AES-GCM默认tag长度为16字节,Web Crypto返回的加密数据是「密文+tag」
        int tagLength = 16;
        byte[] cipherText = encryptedData[..^tagLength];
        byte[] tag = encryptedData[^tagLength..];

        // 解密:Angular中直接用salt作为IV,因此这里传入salt作为IV
        using var aesGcm = new AesGcm(key);
        byte[] plainTextBytes = new byte[cipherText.Length];
        aesGcm.Decrypt(salt, cipherText, tag, plainTextBytes);

        return Encoding.UTF8.GetString(plainTextBytes);
    }
    catch (Exception e)
    {
        throw new Exception($"解密错误: {e.Message}");
    }
}

核心兼容要点

  • PBKDF2参数一致:迭代次数100000、哈希算法SHA-256、密钥长度32字节必须与Angular完全匹配
  • IV与Salt共用:Angular中直接使用传入的salt作为AES-GCM的IV,后端需要前端传递该salt值,不能从加密数据中提取IV
  • 加密数据结构:Web Crypto的encrypt返回结果是「密文+16字节tag」,需从加密数据末尾截取tag
  • 编码统一:密码、salt的编码需与Angular保持一致(Angular使用TextEncoder即UTF-8编码)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 16:12:09