You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

AES加密IV前置实践安全性及CBC与AesGcm选型咨询

AES加密实现与技术疑问

加密实现代码

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

class Program
{
    static void Main()
    {
        // 实际场景中,密钥应从安全渠道获取(比如Azure密钥保管库)
        // 为简化演示,这里生成一个安全的随机密钥
        using var aes = Aes.Create();
        byte[] key = aes.Key; 

        Console.WriteLine("请输入敏感数据:");
        var input = Console.ReadLine();

        var encrypted = Encrypt(input, key);
        Console.WriteLine($"加密结果(IV + 密文):{encrypted}");

        var decrypted = Decrypt(encrypted, key);
        Console.WriteLine($"解密后的数据:{decrypted}");
    }

    static string Encrypt(string plainText, byte[] key)
    {
        using var aes = Aes.Create();
        aes.Key = key;
        aes.Mode = CipherMode.CBC; 
        aes.Padding = PaddingMode.PKCS7;
        
        // Aes.Create()会自动生成安全的随机IV
        byte[] iv = aes.IV;

        using var encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
        using var memoryStream = new MemoryStream();
        
        // 将IV写入输出流头部,IV无需保密,但解密时必须用到
        memoryStream.Write(iv, 0, iv.Length);
        
        using (var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
        {
            var plainBytes = Encoding.UTF8.GetBytes(plainText);
            cryptoStream.Write(plainBytes, 0, plainBytes.Length);
        }

        return Convert.ToBase64String(memoryStream.ToArray());
    }

    static string Decrypt(string encryptedText, byte[] key)
    {
        var encryptedBytes = Convert.FromBase64String(encryptedText);
        using var aes = Aes.Create();

        // AES的IV固定为16字节,从加密数据头部提取
        byte[] iv = new byte[16];
        Array.Copy(encryptedBytes, 0, iv, 0, iv.Length);

        using var memoryStream = new MemoryStream();
        using (var cryptoStream = new CryptoStream(memoryStream, aes.CreateDecryptor(key, iv), CryptoStreamMode.Write))
        {
            // 只写入密文部分(跳过头部的IV)
            cryptoStream.Write(encryptedBytes, iv.Length, encryptedBytes.Length - iv.Length);
        }

        return Encoding.UTF8.GetString(memoryStream.ToArray());
    }
}

技术疑问解答

1. 在.NET中将IV前置到密文的做法是否属于标准且安全的实践?

这是标准且安全的实践。IV本身不需要保密,它的核心作用是保证相同明文用同一密钥加密时生成不同密文,只要IV是随机生成的(你代码里用Aes.Create()自动生成的IV符合安全要求),把它附加在密文头部是最直观且通用的存储/传输方式,解密时直接提取即可,大部分加密库和工业级实现都采用这种方案。

2. 使用CipherMode.CBC搭配PaddingMode.PKCS7,无HMAC时是否易受Padding Oracle攻击?

Padding Oracle攻击的前提是攻击者能多次提交密文,并根据系统返回的错误差异(比如填充错误、密钥错误的不同提示)来窃取信息。你的代码是本地控制台应用,不存在外部攻击者反复提交密文并获取反馈的场景,所以当前实现不存在这个漏洞。但如果把这套逻辑放到服务端,对外提供解密接口,且接口会返回不同的错误信息(比如区分填充错误和密钥错误),那就必须添加HMAC做完整性校验,否则会有被攻击的风险。

3. 现代.NET应用是否应彻底弃用CBC模式,改用AesGcm(认证加密)?若使用AesGcm,是否会自动处理IV前置?

现代.NET应用优先推荐使用AesGcm这类认证加密算法,因为它同时提供加密功能和完整性校验,从根源上避免了Padding Oracle攻击、密文篡改等问题。

AesGcm不会自动处理nonce(GCM模式下对应CBC的IV)的存储,你仍然需要手动将nonce附加到密文头部(或其他易提取的位置)——因为解密时必须用到nonce,它不需要保密,但必须保证同一密钥下唯一。另外,AesGcm加密后会生成一个认证标签,用来验证数据完整性,所以最终的加密数据通常是nonce + 密文 + 标签的结构,解密时需要分别提取这三部分。


并发代码示例

示例1:使用ConcurrentBag实现线程安全集合操作

ConcurrentBag<int> numbers = new ConcurrentBag<int>();

app.MapPost("/add", async (HttpContext context) =>
{
    var form = await context.Request.ReadFormAsync();

    // 输入验证
    if (form.TryGetValue("number", out var numberStr) && int.TryParse(numberStr, out var number))
    {
        // ✅ 此操作完全线程安全,数千个线程可以并发添加数据而无冲突
        numbers.Add(number);
        await context.Response.WriteAsync("已添加");
    }
    else
    {
        context.Response.StatusCode = 400;
        await context.Response.WriteAsync("无效数字");
    }
});

示例2:使用Interlocked实现原子操作

long totalSum = 0;

app.MapPost("/add", async (HttpContext context) =>
{
    var form = await context.Request.ReadFormAsync();
    if (form.TryGetValue("number", out var numberStr) && int.TryParse(numberStr, out var number))
    {
        // ✅ 以线程安全的原子方式添加数字(无需锁,无额外内存消耗)
        Interlocked.Add(ref totalSum, number);
        await context.Response.WriteAsync("已添加");
    }
    // ...
});

app.MapGet("/sum", () => Interlocked.Read(ref totalSum));

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

火山引擎 最新活动