本文介绍了使用 AesCryptoServiceProvider 获取不正确的解密值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have following code that uses AesCryptoServiceProvider for encrypting and decrypting. The iv and key used are same for both encryption and decryption. Still the decrypted value differ from the source string.

  1. 解密后需要更正什么才能得到原始值?
  2. 此代码在 inputValue = valid128BitString 时有效.但是当 inputString = Test" 我得到以下异常 Padding is invalid and cannot be removed..我们该如何纠正?
  1. What need to be corrected to get the original value after decrypt?
  2. This code is working when inputValue = valid128BitString. But when the inputString = "Test" I am getting the following exception Padding is invalid and cannot be removed.. How can we correct it?

更新的问题

以下内容将根据@jbtule 的答案进行操作.

The following will do the trick based on @jbtule answer.

encyptedValue.IV = result.IV;

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

The IV value from encryption result changes. Suppose encryption is done in a separate process, how can we know the IV for decryption? Is there a way to make it constant or known?

答案:您的另一个选择是在开始加密转换之前将 IV 传递给 Encrypt 并分配它,而不是让 aesProvider 为您生成一个随机的.– @斯科特张伯伦

Answer: Your other option is pass a IV in to Encrypt and assign it before you begin your crypto transform, instead of letting aesProvider generate a random one for you. – @Scott Chamberlain

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

更新:请参阅如何为 Base64 应用填充.我们可以使用 UTF8 对源输入和结果输出进行编码.密钥和 IV 可以保留在 Base64 中.

Update: Refer How to apply padding for Base64. We can use UTF8 for encoding the source input and result output. The key and IV may remain in Base64.

将 Base64 用于源输入会导致某些值出现问题,例如MyTest",其中字符串的长度不是 4 的倍数

Using Base64 for source input will cause issues with some values, for example, "MyTest" where length of string is not a multiple of 4

相关要点:

要解密使用 SymmetricAlgorithm 类之一加密的数据,您必须将 Key 属性和 IV 属性设置为用于加密的相同值.

SymmetricAlgorithm.IV 属性:来自前一个块的信息混合到加密下一个块的过程中.因此,两个相同的纯文本块的输出是不同的.由于此技术使用前一个块来加密下一个块,因此需要一个初始化向量来加密第一个数据块.(根据 SymmetricAlgorithm.IV 属性 MSDN 文章)

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)

有效的密钥大小为:128、192、256 位(根据 为我的 AES 方法创建字节数组需要多少个字符?)

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. 要创建多少个字符我的 AES 方法的字节数组?
  2. 指定的密钥不是此算法的有效大小算法
  3. 使用 AES-256 和初始化向量进行加密
  4. Base-64 字符数组的长度无效
  5. UTF8 有什么区别/UTF16 和 Base64 在编码方面

推荐答案

使用密码原语很容易犯实现错误,人们一直在这样做,最好使用 高级库,如果可以的话.

It is easy to make implementation mistakes with cryptographic primitives, people do it all the time, it's best to use a high level library if you can.

我有一个代码段,我会不断查看并更新它,它与您的效果非常接近'重新做.它还对密文进行身份验证,如果有任何对手可以将选定的密文发送到您的解密实现,我会建议这样做,有很多与修改密文相关的旁道攻击.

I have a snippet that I try to keep reviewed and up to date, that works pretty close to what you're doing. It also does authentication on the cipher text, which I would recommend if there is anyway an adversary could send chosen ciphertext to your decryption implementation, there are a lot of side channel attacks related to modifying the ciphertext.

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

However, the problem you're having does not have any thing to do with padding, if your ciphertext doesn't matchup to your key and iv, and you didn't authenticate your iv and ciphertext, you'll typically get a padding error (if this is bubbled up a client it's called a padding oracle). You need to change your main statement to:

    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 来解密.

So you use the same IV to decrypt as you did to encrypt.

这篇关于使用 AesCryptoServiceProvider 获取不正确的解密值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-19 06:57