如何正确一致地从字符串中获取字节进行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;
}
}
}
}
}
您需要在加密/解密前后在字节和字符串之间进行转换。这不是同一个操作,您不应该使用相同的方法。
加密时,首先使用任意字符串。使用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[]);
生成的字符串,传入任意字符串将不起作用。
最简单的解决方案是用StreamWriter
和StreamReader
替换BinaryWriter
和BinaryReader
,并且根本不进行任何转换。
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();
}
}
}
您的代码可能还有其他错误,但至少这会让您走上正轨。