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

.NET 6 AES极简实现解密报错:输入数据不是完整块

AES解密抛出「输入数据不是完整块」异常

我查了很多相关讨论都没解决问题,先贴我原本觉得可行的极简实现代码:

public class CustomEncryptor : IEncryptor
{
    public string Key { get; } // 32字节,比如 6GUNtgba8Rqgm5tc9JGhzxjN6XbzuyNM

    public CustomEncryptor(string key)
    {
        Key = key;
    }

    public byte[] Encrypt(byte[] data)
    {
        using var aes = Aes.Create();
        aes.Key = Encoding.UTF8.GetBytes(Key);
        aes.IV = RandomNumberGenerator.GetBytes(16);
        aes.Padding = PaddingMode.PKCS7;
        return aes.EncryptCbc(data, aes.IV);
    }

    public byte[] Decrypt(byte[] data)
    {
        using var aes = Aes.Create();
        aes.Key = Encoding.UTF8.GetBytes(Key);
        aes.IV = RandomNumberGenerator.GetBytes(16);
        aes.Padding = PaddingMode.PKCS7;
        return aes.DecryptCbc(data, aes.IV);
    }
}

加密数据是正常的,但解密时一直抛出这个异常:

System.Security.Cryptography.CryptographicException: The input data is not a complete block.

我用了AES内置方法,提供32字节Key,生成16字节IV,按理说应该没问题。试过用new byte[16]空数组当IV,换不同填充模式,都没用。

补充背景:我用扩展方法简化字符串加密解密场景,实际使用是用户操作后加密数据存储,应用重启后解密使用:

public static string Encrypt(this IEncryptor encryptor, string textToEncrypt)
{
    var textAsBytes = Encoding.UTF8.GetBytes(textToEncrypt);
    var encryptedData = encryptor.Encrypt(textAsBytes);
    return Convert.ToBase64String(encryptedData);
}

public static string Decrypt(this IEncryptor encryptor, string encryptedText)
{
    var textAsBytes = Convert.FromBase64String(encryptedText);
    var decryptedData = encryptor.Decrypt(textAsBytes);
    return Encoding.UTF8.GetString(decryptedData);
}

网上很多示例都是类似写法,我实在搞不懂哪里错了。


更新1

看到评论说加密解密的Key和IV必须一致,我改成了显式设置相同Key和IV的版本,代码如下:

public class CustomEncryptor : IEncryptor
{
    public byte[] Key { get; }
    public byte[] IV { get; }

    public CustomEncryptor(byte[] key, byte[] iv)
    {
        IV = iv;
        Key = key;
    }
    
    public byte[] Encrypt(byte[] data)
    {
        using var aes = Aes.Create();
        aes.Key = Key;
        return aes.EncryptCbc(data, IV);
    }

    public byte[] Decrypt(byte[] data)
    {
        using var aes = Aes.Create();
        aes.Key = Key;
        return aes.DecryptCbc(data, IV);
    }
}

// 使用示例
var key = Encoding.UTF8.GetBytes("UxRBN8hfjzTG86d6SkSSNzyUhERGu5Zj"); // 32字节
var iv = Encoding.UTF8.GetBytes("7cA8jkRMJGZ8iMeJ"); // 16字节
services.AddSingleton<IEncryptor>(new CustomEncryptor(key, iv));

但还是报同样的错误。


问题根源与解决办法

1. 初始版本的致命错误:IV未随密文存储

AES-CBC模式要求解密必须使用加密时的同一个IV。你最初的代码里,加密时生成随机IV,但只返回了加密后的密文,解密时又重新生成新的随机IV,这完全不符合CBC模式的要求,必然解密失败。而且EncryptCbc方法仅返回密文,不包含IV,必须手动把IV和密文拼接后存储,解密时再拆分使用。

2. 更新1的问题:IV生成不规范+潜在的密文完整性问题

你用UTF8字符串转字节生成IV,首先固定IV严重违反加密安全规范(相同明文会生成相同密文),其次如果字符串转字节后长度不符合AES要求(比如含多字节字符),会导致IV异常。另外你需要确认加密后的密文是否完整传递到解密环节——比如存储时字段长度不足截断了密文,也会触发「输入数据不是完整块」的错误。

正确实现方式(安全且符合规范)

推荐使用随机IV,将IV与密文拼接存储,解密时拆分:

public class CustomEncryptor : IEncryptor
{
    private readonly byte[] _key;

    public CustomEncryptor(byte[] key)
    {
        if (key.Length != 32) // AES-256要求32字节密钥
            throw new ArgumentException("密钥必须为32字节", nameof(key));
        _key = key;
    }

    public byte[] Encrypt(byte[] data)
    {
        using var aes = Aes.Create();
        aes.Key = _key;
        aes.GenerateIV(); // 生成随机安全的IV
        aes.Padding = PaddingMode.PKCS7;

        var encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
        using var ms = new MemoryStream();
        // 先写入IV到密文头部(前16字节)
        ms.Write(aes.IV, 0, aes.IV.Length);
        using var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write);
        cs.Write(data, 0, data.Length);
        cs.FlushFinalBlock();
        return ms.ToArray();
    }

    public byte[] Decrypt(byte[] data)
    {
        if (data.Length < 16)
            throw new ArgumentException("密文格式错误", nameof(data));

        using var aes = Aes.Create();
        aes.Key = _key;
        aes.Padding = PaddingMode.PKCS7;

        // 从密文头部读取IV(前16字节)
        var iv = new byte[16];
        Array.Copy(data, 0, iv, 0, iv.Length);
        aes.IV = iv;

        var decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
        // 跳过IV部分,读取实际密文
        using var ms = new MemoryStream(data, iv.Length, data.Length - iv.Length);
        using var cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read);
        using var outputMs = new MemoryStream();
        cs.CopyTo(outputMs);
        return outputMs.ToArray();
    }
}

// 原扩展方法无需修改,可直接使用

额外注意事项

  • 永远不要用固定IV,这会彻底破坏AES的安全性,必须使用随机IV。
  • 密钥不要直接用UTF8字符串转换,推荐使用密码派生函数(如Rfc2898DeriveBytes)从用户密码生成,或者直接使用随机生成的字节数组作为密钥。
  • 检查密文存储环节是否存在截断问题(比如数据库字段长度不足),这也是触发该异常的常见原因。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 22:57:27