使用AES / CBC / NoPadding算法解密字符串

时间:2014-06-25 09:20:03

标签: c# visual-studio-2012 windows-phone-8 encryption aes

我想在c#Windows Phone 8应用程序中使用AES/CBC/Nopadding解密加密Sting。我的字符串位于IsolatedSorage的文件中。我粘贴了字符串HERE,这是垃圾。

从这个Article我使用AesManaged类来解密。 但是如何将填充设置为NoPadding,因为默认情况下,填充设置为here.PKCS7

        string fileName = "titlepage.xhtml";

        if (fileStorage.FileExists(fileName))
        {
            IsolatedStorageFileStream someStream = fileStorage.OpenFile(fileName, System.IO.FileMode.Open, FileAccess.Read);
            using (StreamReader reader = new StreamReader(someStream))
            {
                str1 = reader.ReadToEnd();

                MessageBox.Show(str1);

                try
                {
                    string text = Decrypt(str1, "****************", "****************");

                    MessageBox.Show(text);
                }
                catch (CryptographicException cryptEx)
                {
                    MessageBox.Show(cryptEx.Message, "Encryption Error", MessageBoxButton.OK);
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message, "General Error", MessageBoxButton.OK);
                }
            }
        }

    public string Decrypt(string dataToDecrypt, string password, string salt)
    {
        AesManaged aes = null;
        MemoryStream memoryStream = null;

        try
        {
            //Generate a Key based on a Password and HMACSHA1 pseudo-random number generator
            //Salt must be at least 8 bytes long
            //Use an iteration count of at least 1000
            Rfc2898DeriveBytes rfc2898 = new Rfc2898DeriveBytes(password, Encoding.UTF8.GetBytes(salt), 10000);               

            //Create AES algorithm
            aes = new AesManaged();
            //Key derived from byte array with 32 pseudo-random key bytes
            aes.Key = rfc2898.GetBytes(32);
            //IV derived from byte array with 16 pseudo-random key bytes
            aes.IV = rfc2898.GetBytes(16);

            //Create Memory and Crypto Streams
            memoryStream = new MemoryStream();
            CryptoStream cryptoStream = new CryptoStream(memoryStream, aes.CreateDecryptor(), CryptoStreamMode.Write);

            byte[] data = Convert.FromBase64String(dataToDecrypt);
            cryptoStream.Write(data, 0, data.Length);
            cryptoStream.FlushFinalBlock();

            //Return Decrypted String
            byte[] decryptBytes = memoryStream.ToArray();

            //Dispose
            if (cryptoStream != null)
                cryptoStream.Dispose();

            //Retval
            return Encoding.UTF8.GetString(decryptBytes, 0, decryptBytes.Length);
        }
        finally
        {
            if (memoryStream != null)
                memoryStream.Dispose();

            if (aes != null)
                aes.Clear();
        }            
    }

修改1:

当我在最后一行解密我的加密字符串时

 byte[] data = Convert.FromBase64String(dataToDecrypt);

转到Finally block并获取The input is not a valid Base-64 string as it contains a non-base 64 character, more than two padding characters, or an illegal character among the padding characters in decrypted string.

的例外情况

在Windows Phone中,Decrypt支持类,这有点混乱。

如果我完全错误,请向我建议关于Windows Phone中算法的文章网址

编辑2:

如下面的回答所示:“我将cyperText作为字节获取,在解密方面很好。但它给出了描述的例外

       [Cryptography_SSD_InvalidDataSize]
    Arguments: 
    Debugging resource strings are unavailable. Often the key and arguments provide 
sufficient information to diagnose the problem

我认为问题是IV [盐键]或设置填充到AesManged。 但我无法在Windows Phone中将填充属性更改为AesManaged。 默认情况下,填充到AesManged的是PKCS7。我想改为NoPadding。因为我的cyperText是使用AES / CBC / NoPadding算法加密的“

2 个答案:

答案 0 :(得分:5)

如果我理解了这个问题,那么您的数据已经在AES CBC模式下加密,没有填充。但是在想要解密数据的手机上,唯一的选择是PKCS#7填充。

嗯,你很幸运!您可以使用PKCS#7填充来解密密文。您需要做的就是将填充添加到手机上的密文,然后解密。

要在事后添加填充,您将加密一小部分数据并将其附加到密文。然后,您解密修改后的密文,关闭那一小段数据,然后您就拥有原始明文。

以下是您的操作方法:

  1. 在手机上输入密文。即使没有填充,这也是16个字节的倍数。没有其他可能性 - AES密文始终是16字节的倍数。

  2. 将密文的最后16个字节放在一边,并将其设置为AES ENCRYPT的IV。 (加密,而不是解密。)使用您将要用于稍后解密的相同密钥。

  3. 现在加密小于16字节的内容,例如字符'$'。手机将为此添加PKCS#7填充。

  4. 将生成的16字节密文附加到步骤1中的原始密文,现在您有一个正确的PKCS#7填充密文,其中包含原始明文和添加的“$”。

  5. 使用原始的IV和相同的密钥,现在将这个组合的密文解压缩。您现在可以删除明文末尾显示的'$'(或者您在步骤3中添加的任何内容。)

  6. 当使用原始密文的最后16个字节对小位进行加密时,您实际上是在真正的AES CBC模式下扩展密文,而您恰好使用PKCS#7填充进行加密,因此您现在可以解密整个事情,并取消一点点。你将拥有没有填充的原始明文。

    我认为在代码中显示会很有趣:

    var rfc2898 = new Rfc2898DeriveBytes("password", new byte[8]);
    
    using (var aes = new AesManaged())
    {
        aes.Key = rfc2898.GetBytes(32);
        aes.IV = rfc2898.GetBytes(16);
    
        var originalIV = aes.IV; // keep a copy
    
        // Prepare sample plaintext that has no padding
        aes.Padding = PaddingMode.None;
        var plaintext = Encoding.UTF8.GetBytes("this plaintext has 32 characters");
        byte[] ciphertext;
        using (var encryptor = aes.CreateEncryptor())
        {
            ciphertext = encryptor.TransformFinalBlock(plaintext, 0, plaintext.Length);
            Console.WriteLine("ciphertext: " + BitConverter.ToString(ciphertext));
        }
    
        // From this point on we do everything with PKCS#7 padding
        aes.Padding = PaddingMode.PKCS7;
    
        // This won't decrypt -- wrong padding
        try
        {
            using (var decryptor = aes.CreateDecryptor())
            {
                var oops = decryptor.TransformFinalBlock(ciphertext, 0, ciphertext.Length);
            }
        }
        catch (Exception e)
        {
            Console.WriteLine("caught: " + e.Message);
        }
    
        // Last block of ciphertext is used as IV to encrypt a little bit more
        var lastBlock = new byte[16];
        var modifiedCiphertext = new byte[ciphertext.Length + 16];
    
        Array.Copy(ciphertext, ciphertext.Length - 16, lastBlock, 0, 16);
        aes.IV = lastBlock;
    
        using (var encryptor = aes.CreateEncryptor())
        {
            var dummy = Encoding.UTF8.GetBytes("$");
            var padded = encryptor.TransformFinalBlock(dummy, 0, dummy.Length);
    
            // Set modifiedCiphertext = ciphertext + padded
            Array.Copy(ciphertext, modifiedCiphertext, ciphertext.Length);
            Array.Copy(padded, 0, modifiedCiphertext, ciphertext.Length, padded.Length);
            Console.WriteLine("modified ciphertext: " + BitConverter.ToString(modifiedCiphertext));
        }
    
        // Put back the original IV, and now we can decrypt...
        aes.IV = originalIV;
    
        using (var decryptor = aes.CreateDecryptor())
        {
            var recovered = decryptor.TransformFinalBlock(modifiedCiphertext, 0, modifiedCiphertext.Length);
            var str = Encoding.UTF8.GetString(recovered);
            Console.WriteLine(str);
    
            // Now you can remove the '$' from the end
        }
    }
    

答案 1 :(得分:1)

您链接的字符串是不是 Base-64。它看起来好像是原始加密字节,解释为字符。要么在加密端工作,要么输出原始字节的Base-64字符串编码,要么在解密端工作,将密文读作原始字节,而不是文本,忘记删除Base-64。

通常更好地在加密端工作,因为传递Base-64文本比传递原始字节容易出错。