c# - 使用 AesCryptoServiceProvider 获取不正确的解密值

标签 c# .net encryption cryptography

我有以下代码使用 AesCryptoServiceProvider 进行加密和解密。 ivkey 用于加密和解密。解密后的值仍然与源字符串不同。

  1. 解密后需要修正什么才能得到原始值?
  2. 此代码在 inputValue = valid128BitString 时有效。但是当 inputString = “Test” 时,我收到以下异常 Padding is invalid and cannot be removed.。我们如何纠正它?

更新问题

下面将根据@jbtule 的回答来解决这个问题。

encyptedValue.IV = result.IV;

加密结果的IV值发生变化。假设加密是在一个单独的进程中完成的,我们如何知道解密的 IV?有没有办法让它保持不变或已知?

回答:您的另一个选择是将 IV 传递给 Encrypt 并在开始加密转换之前分配它,而不是让 aesProvider 为您生成一个随机的。 – @Scott Chamberlain

 aesProvider.IV = Convert.FromBase64String("4uy34C9sqOC9rbV4GD8jrA==");

更新:引用How to apply padding for Base64 .我们可以使用 UTF8 对源输入和结果输出进行编码。 key 和 IV 可能保留在 Base64 中。

对源输入使用 Base64 会导致某些值出现问题,例如,字符串长度不是 4 的倍数的“MyTest”

相关要点:

To decrypt data that was encrypted using one of the SymmetricAlgorithm classes, you must set the Key property and IV property to the same values that were used for encryption.

SymmetricAlgorithm.IV Property: Information from the previous block is mixed into the process of encrypting the next block. Thus, the output of two identical plain text blocks is different. Because this technique uses the previous block to encrypt the next block, an initialization vector is needed to encrypt the first block of data. (As per SymmetricAlgorithm.IV Property MSDN article)

The valid Key sizes are: 128, 192, 256 bits (as per How many characters to create a byte array for my AES method?)

主程序

class Program
{
    static void Main(string[] args)
    {
        string valid128BitString = "AAECAwQFBgcICQoLDA0ODw==";
        string inputValue = valid128BitString;
        string keyValue = valid128BitString;
        string iv = valid128BitString;

        byte[] byteValForString = Convert.FromBase64String(inputValue);
        EncryptResult result = Aes128Utility.EncryptData(byteValForString, keyValue);
        EncryptResult encyptedValue = new EncryptResult();
        encyptedValue.IV = iv;
        encyptedValue.EncryptedMsg = result.EncryptedMsg;

        string finalResult = Convert.ToBase64String(Aes128Utility.DecryptData(encyptedValue, keyValue));
        Console.WriteLine(finalResult);

        if (String.Equals(inputValue, finalResult))
        {
            Console.WriteLine("Match");
        }
        else
        {
            Console.WriteLine("Differ");
        }

        Console.ReadLine();
    }
}

AES 实用程序

public static class Aes128Utility
{
    private static byte[] key;

    public static EncryptResult EncryptData(byte[] rawData, string strKey)
    {
        EncryptResult result = null;
        if (key == null)
        {
            if (!String.IsNullOrEmpty(strKey))
            {
                key = Convert.FromBase64String((strKey));
                result = Encrypt(rawData);
            }
        }
        else
        {
            result = Encrypt(rawData);
        }

        return result; 

    }

    public static byte[] DecryptData(EncryptResult encryptResult, string strKey)
    {
        byte[] origData = null;
        if (key == null)
        {
            if (!String.IsNullOrEmpty(strKey))
            {
                key = Convert.FromBase64String(strKey);
                origData = Decrypt(Convert.FromBase64String(encryptResult.EncryptedMsg), Convert.FromBase64String(encryptResult.IV));
            }
        }
        else
        {
            origData = Decrypt(Convert.FromBase64String(encryptResult.EncryptedMsg), Convert.FromBase64String(encryptResult.IV));
        }

        return origData; 
    }

    private static EncryptResult Encrypt(byte[] rawData)
    {
        using (AesCryptoServiceProvider aesProvider = new AesCryptoServiceProvider())
        {
            aesProvider.Key = key;
            aesProvider.Mode = CipherMode.CBC;
            aesProvider.Padding = PaddingMode.PKCS7;
            using (MemoryStream memStream = new MemoryStream())
            {
                CryptoStream encStream = new CryptoStream(memStream, aesProvider.CreateEncryptor(), CryptoStreamMode.Write);
                encStream.Write(rawData, 0, rawData.Length);
                encStream.FlushFinalBlock();
                EncryptResult encResult = new EncryptResult();
                encResult.EncryptedMsg = Convert.ToBase64String(memStream.ToArray());
                encResult.IV = Convert.ToBase64String(aesProvider.IV);
                return encResult;
            }
        }
    }

    private static byte[] Decrypt(byte[] encryptedMsg, byte[] iv)
    {
        using (AesCryptoServiceProvider aesProvider = new AesCryptoServiceProvider())
        {
            aesProvider.Key = key;
            aesProvider.IV = iv;
            aesProvider.Mode = CipherMode.CBC;
            aesProvider.Padding = PaddingMode.PKCS7;
            using (MemoryStream memStream = new MemoryStream())
            {
                CryptoStream decStream = new CryptoStream(memStream, aesProvider.CreateDecryptor(), CryptoStreamMode.Write);
                decStream.Write(encryptedMsg, 0, encryptedMsg.Length);
                decStream.FlushFinalBlock();
                return memStream.ToArray();
            }
        }
    }

}

DTO 类

public class EncryptResult
{
    public string EncryptedMsg { get; set; }
    public string IV { get; set; }
}

引用文献

  1. How many characters to create a byte array for my AES method?
  2. Specified key is not a valid size for this algorithm
  3. Encryption with AES-256 and the Initialization Vector
  4. Invalid length for a Base-64 char array
  5. What's the difference between UTF8/UTF16 and Base64 in terms of encoding

最佳答案

使用加密原语很容易出现实现错误,人们总是这样做,最好使用 high level library如果可以的话。

我有一个 snippet我尽量保持审查和更新,这与您正在做的事情非常接近。它还对密文进行身份验证,如果对手无论如何都可以将选择的密文发送到您的解密实现,我建议这样做,有很多与修改密文相关的侧信道攻击。

但是,你遇到的问题与填充没有任何关系,如果你的密文与你的 key 和 iv 不匹配,并且你没有验证你的 iv 和密文,你通常会得到填充错误(如果这是在客户端冒泡,则称为 padding oracle )。您需要将主语句更改为:

    string valid128BitString = "AAECAwQFBgcICQoLDA0ODw==";
    string inputValue = "Test";
    string keyValue = valid128BitString;


    byte[] byteValForString = Encoding.UTF8.GetBytes(inputValue);
    EncryptResult result = Aes128Utility.EncryptData(byteValForString, keyValue);
    EncryptResult encyptedValue = new EncryptResult();
    encyptedValue.IV = result.IV; //<--Very Important
    encyptedValue.EncryptedMsg = result.EncryptedMsg;

    string finalResult =Encoding.UTF8.GetString(Aes128Utility.DecryptData(encyptedValue, keyValue));

因此您使用与加密相同的 IV 进行解密。

关于c# - 使用 AesCryptoServiceProvider 获取不正确的解密值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14937707/

相关文章:

c# - 如何在.NET服务器端检索Http请求参数

php - 在 PHP 中加密,在 Python 中解密

c# - Rijndael 使用 Java 加密,然后使用 C# 和 EnterpriseLibrary 4.1 解密

c# - 为什么我不能在函数级别声明一个委托(delegate)?

c# - 将字符串数组从 C# 返回到 Inno Setup

c# - C# 中的数据绑定(bind)()

c# - 添加 dll 引用到 asp.net mvc 应用程序,无需重新启动应用程序

.net - TPL vs.InvokeRequired/Invoke

c# - 加密和解密

php - 在 PHP : fast, 中加密和解密简短、有效的 HTML id 和 URL 安全