如何正确一致地从字符串中获取字节进行AES加密

本文关键字:字节 获取 加密 AES 字符串 何正确 | 更新日期: 2023-09-27 18:27:08

我目前正在C#中进行AES实现。加密方法有两个参数:一个字符串和一个密码。我将获取提供的字符串并将其转换为字节数组,以便稍后使用它将数据写入具有BinaryWriter的流。

问题是,当我使用Convert.FromBase64String(string)时,我会得到FormatException: Invalid length.,而当我使用Encoding.UTF8.GetBytes(string)时,我的解密方法会抛出无效的PKCS7.PPadding异常。

最近几天我一直在努力解决这个问题。我在stackoverflow.com和其他网站上读到了几乎无限的问题,但我仍然不知道解决这个问题最可靠的方法是什么。

此程序中使用的字符串仅限于句子(例如"需要加密的东西")和数字(例如"12345")。

提前谢谢你,这是我现在的代码:

    public class AESProvider {
    public byte[] EncryptStringToBytes_Aes(string plainText, string Key)
    {
        // Check arguments. 
        if (plainText == null || plainText.Length <= 0)
            throw new ArgumentNullException("plainText");
        if (Key == null || Key.Length <= 0)
            throw new ArgumentNullException("Key");
        byte[] plainTextInBytes = Convert.FromBase64String(plainText);
        byte[] encrypted;
        //Create an Aes object
        //with the specified key and IV.
        using (Aes aesAlg = Aes.Create())
        {
            aesAlg.GenerateIV();
            byte[] IV = aesAlg.IV;
            //The Salt will be the first 8 bytes of the IV.
            byte[] theSalt = new byte[8];
            Array.Copy(IV,theSalt,8);
            //A key for AES is generated by expanding the password using the following method.
            Rfc2898DeriveBytes keyGen = new Rfc2898DeriveBytes(Key,theSalt);
            byte[] aesKey = keyGen.GetBytes(16);
            aesAlg.Key = aesKey;
            // Create a decrytor to perform the stream transform.
            ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, IV);
            // Create the streams used for encryption. 
            using (MemoryStream msEncrypt = new MemoryStream())
            {
                using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
                {
                    using (BinaryWriter swEncrypt = new BinaryWriter(csEncrypt))
                    {
                        //Write all data to the stream.
                        swEncrypt.Write(plainTextInBytes);
                    }
                    encrypted = msEncrypt.ToArray();
                }
            }
            // Prepend the IV to the ciphertext so it can be used in the decryption process.
            using (MemoryStream ivPlusCipher = new MemoryStream())
            {
                using (BinaryWriter tBinaryWriter = new BinaryWriter(ivPlusCipher))
                {
                    tBinaryWriter.Write(IV);
                    tBinaryWriter.Write(encrypted);
                    tBinaryWriter.Flush();
                }
                return ivPlusCipher.ToArray();
            }
        }
    }
    public byte[] DecryptStringFromBytes_Aes(byte[] cipherText, string Key)
    {
        // Check arguments. 
        if (cipherText == null || cipherText.Length <= 0)
            throw new ArgumentNullException("cipherText");
        if (Key == null || Key.Length <= 0)
            throw new ArgumentNullException("Key");
        // Declare the string used to hold 
        // the decrypted text. 
        byte[] decrypted;
        // Create an Aes object 
        // with the specified key and IV. 
        // Create the streams used for decryption. 
        using (Aes aesAlg = Aes.Create())
        {
            aesAlg.Mode = CipherMode.CBC;
            aesAlg.Padding = PaddingMode.PKCS7;
            //Grab IV from ciphertext
            byte[] IV = new byte[16];
            Array.Copy(cipherText,0,IV,0,16);
            //Use the IV for the Salt
            byte[] theSalt = new byte[8];
            Array.Copy(IV,theSalt,8);
            Rfc2898DeriveBytes keyGen = new Rfc2898DeriveBytes(Key,theSalt);
            byte[] aesKey = keyGen.GetBytes(16);
            aesAlg.Key = aesKey;
            // Create a decrytor to perform the stream transform.
            ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, IV);
            using (MemoryStream msDecrypt = new MemoryStream())
            {
                using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Write))
                {
                    using (BinaryWriter srDecrypt = new BinaryWriter(csDecrypt))
                    {
                        //Decrypt the ciphertext
                        srDecrypt.Write(cipherText, IV.Length, (cipherText.Length - IV.Length));
                    }
                    decrypted = msDecrypt.ToArray();
                    return decrypted;
                }
            }   
        }
    }
}

如何正确一致地从字符串中获取字节进行AES加密

您需要在加密/解密前后在字节和字符串之间进行转换。这不是同一个操作,您不应该使用相同的方法。

加密时,首先使用任意字符串。使用Encoding.UTF8.GetBytes()将其转换为字节[]。对其进行加密。生成的字节[]现在可以使用Convert.ToBase64String()转换为字符串。

解密时,您现在从一个Base64编码的字符串开始。使用Convert.FromBase64String()将其解码为字节[]。解密它。您现在拥有了原始字符串的UTF-8编码,可以使用Encoding.UTF8.GetString()对其进行解码。

记住:

  • Encoding.UTF8可以将任意字符串转换为字节数组(但它只能将包含实际UTF8编码的字节数组转换回)
  • 转换。[To/From]Base64String用于将任意字节数组转换为字符串(但它只能将包含实际Base64编码的字符串转换回)

查看您的线路

public byte[] EncryptStringToBytes_Aes(string plainText, string Key)
byte[] plainTextInBytes = Convert.FromBase64String(plainText);

任意纯文本将不是以64为基编码的字符串。即使它应该是基于64编码的文本,您的错误消息也表明长度不能被4 整除

FormatException
忽略空白字符后,s的长度不是零或4的倍数。-或-s的格式无效。s包含一个非base-64字符、两个以上的填充字符或填充字符中的>非空白字符。

http://msdn.microsoft.com/en-us/library/system.convert.frombase64string(v=vs.110).aspx

如果它是一个基于64编码的字符串,你需要根据进行填充

http://en.wikipedia.org/wiki/Base64

Convert.FromBase64String(string);应接收由Convert.ToBase64String(byte[]);生成的字符串,传入任意字符串将不起作用。

最简单的解决方案是用StreamWriterStreamReader替换BinaryWriterBinaryReader,并且根本不进行任何转换。

public byte[] EncryptStringToBytes_Aes(string plainText, string Key)
{
    // Check arguments. 
    if (plainText == null || plainText.Length <= 0)
        throw new ArgumentNullException("plainText");
    if (Key == null || Key.Length <= 0)
        throw new ArgumentNullException("Key");

    //Create an Aes object
    //with the specified key and IV.
    using (Aes aesAlg = Aes.Create())
    {
        aesAlg.GenerateIV();
        byte[] IV = aesAlg.IV;
        //The Salt will be the first 8 bytes of the IV.
        byte[] theSalt = new byte[8];
        Array.Copy(IV,theSalt,8);
        //A key for AES is generated by expanding the password using the following method.
        Rfc2898DeriveBytes keyGen = new Rfc2898DeriveBytes(Key,theSalt);
        byte[] aesKey = keyGen.GetBytes(16);
        aesAlg.Key = aesKey;
        // Create a decrytor to perform the stream transform.
        ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, IV);
        // Create the streams used for encryption. 
        using (MemoryStream msEncrypt = new MemoryStream())
        {
            //You can write the IV here and not need to do it later.
            msEncrypt.Write(IV, 0, IV.Length);
            using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
            {
                using (StreamWriter swEncrypt = new StreamWriter (csEncrypt))
                {    
                    //Write all data to the stream.
                    swEncrypt.Write(plainText);
                }
            }
            //Move this outside of the using statement for CryptoStream so it is flushed and dipsoed.
            return msEncrypt.ToArray();
        }
    }
}

此外,您的解密函数实际上正在尝试对文本进行第二次加密,您需要将字节数组传递给msDecrypt的构造函数,并将其置于解密模式。

public string DecryptStringFromBytes_Aes(byte[] cipherText, string Key)
{
    // Check arguments. 
    if (cipherText == null || cipherText.Length <= 0)
        throw new ArgumentNullException("cipherText");
    if (Key == null || Key.Length <= 0)
        throw new ArgumentNullException("Key");
    // Create an Aes object 
    // with the specified key and IV. 
    // Create the streams used for decryption. 
    using (Aes aesAlg = Aes.Create())
    {
        aesAlg.Mode = CipherMode.CBC;
        aesAlg.Padding = PaddingMode.PKCS7;
        //Grab IV from ciphertext
        byte[] IV = new byte[16];
        Array.Copy(cipherText,0,IV,0,16);
        //Use the IV for the Salt
        byte[] theSalt = new byte[8];
        Array.Copy(IV,theSalt,8);
        Rfc2898DeriveBytes keyGen = new Rfc2898DeriveBytes(Key,theSalt);
        byte[] aesKey = keyGen.GetBytes(16);
        aesAlg.Key = aesKey;
        // Create a decrytor to perform the stream transform.
        ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, IV);
        //You can chain using statements like this to make the code easier to read.
        using (MemoryStream msDecrypt = new MemoryStream(cipherText))
        using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read)) //Notice this is Read mode not Write mode.
        using (StreamReader srDecrypt = new StreamReader(csDecrypt))
        {
            //Decrypt the ciphertext
            return srDecrypt.ReadToEnd();
        }  
    }
}

您的代码可能还有其他错误,但至少这会让您走上正轨。