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

问题描述:

我有以下代码使用AesCryptoServiceProvider进行加密和解密。所使用的ivkey对于加密和解密都是相同的。解密值仍然与源字符串不同。使用AesCryptoServiceProvider获取不正确的解密值

  1. 什么需要纠正以获得解密后的原始值?
  2. 此代码适用于inputValue = valid128BitString。但是当inputString = “Test”我得到以下例外Padding is invalid and cannot be removed.。我们如何纠正它?

修订问题

下面将基于@jbtule答案做的伎俩。

​​

从加密结果改变IV值。假设加密是在一个单独的过程中完成的,我们如何知道解密的IV?有没有办法使其保持不变或已知?

答案:您的其他选项是通过IV加密并在开始加密转换之前分配它,而不是让aesProvider为您生成一个随机的加密转换。 - @Scott Chamberlain

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

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

使用的Base64源输入会导致问题的一些的值,例如,“MyTest的”,其中的字符串的长度不是4

相关点的整数倍:

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

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

有效键尺寸为: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
+1

什么是实际的异常类型,你得到解密或加密?想知道因为'byte [] byteValForString = Convert.FromBase64String(“Test”);'对你的纯文本无效。 – jbtule 2013-02-18 15:32:19

+0

你可以在Encrypt()和Decrypt()中使用'aesProvider.Padding = PaddingMode.None'吗?不知道为什么它使用相同的填充时出错? – Amitd 2013-02-18 15:51:47

+0

@jbtule我在Decrypt()方法中遇到异常 - decStream.FlushFinalBlock();声明。填充无效,无法删除。 – Lijo 2013-02-18 16:00:59

很容易使密码原语执行的错误,人们做这一切的时候,最好使用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进行解密,就像您进行加密一样。

+0

任何线索为什么会导致异常? FlushFinalBlock被调用加密 – Amitd 2013-02-18 16:30:13

+1

@Amitd你是对的问题是解密时IV是错误的。 – jbtule 2013-02-18 19:41:06

+0

谢谢..来自加密结果的IV值发生变化。假设加密是在一个单独的过程中完成的,我怎么能知道IV解密?有没有办法使其保持不变或已知? – Lijo 2013-02-19 04:55:28