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

本文关键字:解密 字符串 算法 CBC AES 使用 NoPadding | 更新日期: 2023-09-27 18:10:08

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

从这篇文章我使用AesManaged类解密。但是如何将填充设置为NoPadding,因为默认情况下填充设置为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:

当我解密我的加密字符串在thinline

 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。

如果我完全错了,建议我关于Windows Phone算法的文章的url

编辑2:

如下答案建议"我得到cyperText作为字节,它在解密方面很好。但是它给出了一个异常的描述

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

我认为问题是IV[盐键]或设置填充到aesmanaged。但我不能改变填充属性在Windows Phone的AesManaged。缺省情况下,对AesManged的填充为PKCS7。我想改成NoPadding。因为我的cyperText是使用AES/CBC/NoPadding算法加密的"

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

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

好吧,你很幸运!您可以使用pkcs# 7填充来解密密文。您所需要做的就是在手机上为密文添加填充,然后解密它。

要在事实之后添加填充,您将加密一小段数据并将其附加到密文中。然后,对修改后的密文进行解密,并去掉那一小部分数据,就得到了原始的明文。

方法如下:

  1. 在电话中接受密文。这是16字节的倍数,即使没有填充。没有其他的可能性——AES密文总是16字节的倍数。

  2. 将密文的最后16字节放在一边,并将其设置为AES加密的IV。(加密,而不是解密。)

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

  4. 将得到的16字节的密文附加到步骤1的原始密文中,现在您就有了一个正确的pkcs# 7填充的密文,其中包括原始明文和添加的'$'。

  5. 使用原来的IV,和相同的密钥,现在解密这个组合密文。你现在可以删除'$',它将出现在你的明文(或任何你在步骤3中添加的)的末尾

当小位用原始密文的最后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
    }
}

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

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