I need to encrypt/decrypt some strings. I've build my wrapper class according to the msdn documentation but with some changes.
Since I want to encrypt/decrypt data with a given string/passphrase, I don't use AesManaged
for creating a key. (The user should be able to encrypt/decrypt with a key he enters, and therefore I cannot use the key from AesManaged
and I cannot save the key).
I instead create the key by using Rfc2898DeriveBytes
(PBKDF2) with a given salt. The given salt is used since I do not store the key and I think because of this, the salt must be always the same.
I then create an IV, encrypt the given string and concatenate the IV and the encrypted string. This will then eventually got saved in a file. This means the IV gets save together with the encrypted data.
Questions:
static void Main(string[] args)
{
const string stringToEncrypt = "String to be encrypted/decrypted. Encryption is done via AesManaged";
const string password = "m1Sup3rS3cre!Password";
string encrypted = EncryptString(stringToEncrypt, password);
string roundtrip = DecryptStringFromBytes_Aes(encrypted, password);
Console.WriteLine("Original: {0}", stringToEncrypt);
Console.WriteLine("Round Trip: {0}", roundtrip);
Console.ReadLine();
}
static string EncryptString(string plainText, string password)
{
string encryptedString;
using (AesManaged aesAlg = new AesManaged())
{
aesAlg.Key = PasswordAsByte(password);
ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
swEncrypt.Write(plainText);
}
var encrypted = msEncrypt.ToArray();
encryptedString = Encoding.Default.GetString(aesAlg.IV);
encryptedString += Encoding.Default.GetString(encrypted);
}
}
}
return encryptedString;
}
static string DecryptStringFromBytes_Aes(string cipherText, string password)
{
using (AesManaged aesAlg = new AesManaged())
{
aesAlg.Key = PasswordAsByte(password);
aesAlg.IV = Encoding.Default.GetBytes(cipherText).Take(16).ToArray();
ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
var encryptedByteArray = Encoding.Default.GetBytes(cipherText).Skip(16).ToArray();
using (MemoryStream msDecrypt = new MemoryStream(encryptedByteArray))
{
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (StreamReader srDecrypt = new StreamReader(csDecrypt))
{
return srDecrypt.ReadToEnd();
}
}
}
}
}
private static byte[] PasswordAsByte(string password)
{
byte[] salt = Encoding.Default.GetBytes("foobar42");
Rfc2898DeriveBytes passwordBytes = new Rfc2898DeriveBytes(password, salt);
return passwordBytes.GetBytes(32);
}
No, this is not okay.
1) You're using Encoding.Default
in various places. Don't do that - it means you're at the whim of the platform you're on. Always use an explicit encoding, ideally UTF-8 in most cases.
2) You're using Encoding.GetString
/ Encoding.GetBytes
to convert arbitrary binary data to a string and back. That's almost bound to lose data. (It happened to succeed on my machine, but it really depends on the encoding - and it's fundamentally a bad idea.) Encoding
is designed for data which is inherently text data, and you're just applying an encoding one way or the other. Your encrypted data is inherently binary data. Use Convert.ToBase64String
and Convert.FromBase64String
instead.
For your other questions:
passwordBytes.GetBytes(32)
- that's a 256-bit key, so it's AES256.See more on this question at Stackoverflow