Как правильно и consistely получить байт из строки для шифрования AES?
в настоящее время я работаю над реализацией AES в C#. Метод шифрования имеет два параметра: строку и пароль. Я беру предоставленную строку и преобразую ее в массив байтов, поэтому я могу использовать ее позже для записи данных в поток с BinaryWriter
.
проблема в том, что когда я использую Convert.FromBase64String(string)
Я FormatException: Invalid length.
и когда я использую Encoding.UTF8.GetBytes(string)
мой метод дешифрования бросает и недопустимый PKCS7.Исключение заполнения.
Я пытался решить эту проблему для последние пару дней. Я прочитал почти бесконечные вопросы в stackoverflow.com и другие сайты, но я до сих пор не знаю, какой самый надежный способ решить эту проблему.
строки, которые будут использоваться в этой программе, ограничены предложениями (ex. - Что-нибудь зашифровать.") и цифры (например. "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;
}
}
}
}
}
3 ответов
вам нужно преобразовать между байтами и строками до и после шифрования/дешифрования. Это не та же операция, и вы не должны использовать тот же метод.
при шифровании вы начинаете с произвольной строкой. Преобразуйте это в байт [], используя Encoding.UTF8.GetBytes()
. Зашифровать его. Полученный байт[] теперь можно преобразовать в строку с помощью Convert.ToBase64String()
.
при расшифровке теперь вы начинаете с строки, закодированной в Base64. Декодируйте это в байт [], используя Convert.FromBase64String()
. Расшифруй его. Теперь у вас есть кодировка UTF-8 вашей исходной строки, которую вы можете декодировать с помощью Encoding.UTF8.GetString()
.
помните:
- кодировка.UTF8 работает для преобразования произвольных строк в байтовые массивы (но он может конвертировать только байтовые массивы, содержащие фактические кодировки UTF8).
- преобразовать.[To/From]Base64String работает для преобразования произвольных байтовых массивов в строки (но он может конвертировать только строки, содержащие фактические кодировки Base64).
глядя на ваши строки
public byte[] EncryptStringToBytes_Aes(string plainText, string Key)
byte[] plainTextInBytes = Convert.FromBase64String(plainText);
произвольный простой текст будет в кодировке base 64 строку. Даже если предполагается, что это кодированный текст base 64, ваше сообщение об ошибке указывает, что длина не делится на 4
FormatException
Длина s, игнорируя символы пробела, не равна нулю или кратна 4. -или- Недопустимый формат s. s содержит не базовый символ-64, более двух символов заполнения или a > non-white space-символ среди символов заполнения.
http://msdn.microsoft.com/en-us/library/system.convert.frombase64string(v=vs. 110).aspx
Если это базовая 64-кодированная строка, вам нужно заполнить ее accorgingly
получит строку, сгенерированную Convert.ToBase64String(byte[]);
передача произвольной строки не будет работать.
самое простое решение-заменить BinaryWriter
и BinaryReader
С StreamWriter
и a StreamReader
и не делать никакого преобразования вообще.
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();
}
}
}
могут быть и другие ошибки с вашим кодом, но, по крайней мере, это приведет вас на правильный путь.