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

时间:2013-02-18 13:40:31

标签: c# .net encryption cryptography

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

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

    以下将根据@jbtule答案进行操作。

    encyptedValue.IV = result.IV;
    

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

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

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

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

    使用Base64进行源输入会导致某些值出现问题,例如“MyTest”,其中string的长度不是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 个答案:

答案 0 :(得分:3)

使用加密原语很容易出现实施错误,人们会一直这样做,如果可以的话,最好使用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进行解密,就像加密一样。