【问题标题】:Getting incorrect decryption value using AesCryptoServiceProvider使用 AesCryptoServiceProvider 获取不正确的解密值
【发布时间】:2013-02-02 23:11:54
【问题描述】:

我有以下代码使用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 为您生成一个随机的。 ——@斯科特·张伯伦

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

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

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

相关要点:

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

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

有效的密钥大小为:128、192、256 位(根据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

【问题讨论】:

  • 实际的异常类型是什么,你是在解密还是加密时得到这个?想知道,因为byte[] byteValForString = Convert.FromBase64String("Test"); 对您的纯文本无效。
  • 你能在 Encrypt() 和 Decrypt() 中尝试aesProvider.Padding = PaddingMode.None 吗?如果它有效吗?想知道如果使用相同的填充,为什么会出错?
  • @jbtule 我在 Decrypt() 方法中遇到异常 - decStream.FlushFinalBlock();陈述。填充无效,无法移除。
  • 参考stackoverflow.com/questions/14954248/…作为Key和IV的字符串长度

标签: c# .net encryption cryptography


【解决方案1】:

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

我有一个snippet,我会努力保持审查并保持最新状态,它与您正在做的事情非常接近。它还对密文进行身份验证,如果有对手可以将选择的密文发送到您的解密实现,我会建议这样做,有很多与修改密文相关的旁道攻击。

但是,您遇到的问题与填充没有任何关系,如果您的密文与您的密钥和 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 进行解密。

【讨论】:

  • 任何线索为什么它会导致异常? FlushFinalBlock 在加密中被调用
  • @Amitd 你是对的,问题是解密时 IV 错误。
  • @Lijo IV 不需要保密,只需要密钥。通常 Crytpo 协议只会在加密 blob 被保存/传输时将 IV 预先添加到加密 blob 的前面。像这样的事情就是为什么最好使用 jbtule 建议的更高级别的库,它只是为你处理它。
  • @Lijo 您的另一个选择是将 IV 传递给 Encrypt 并在开始加密转换之前分配它,而不是让 aesProvider 为您随机生成一个。
  • @Lijo 但是我仍然强调您应该使用更高级别的库,您正在跳过重要的步骤,例如确保您的加密消息没有被篡改等等。去看看许多可用的库并使用其中之一。
猜你喜欢
  • 2012-12-15
  • 2013-01-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-04-29
相关资源
最近更新 更多