patterncsharpMinor
An AES Cryptography Wrapper
Viewed 0 times
wrappercryptographyaes
Problem
I built this wrapper over the System.Security.Cryptography.Aes encryption/decryption so that one need only instance the class and call the appropriate methods. This makes it much simpler to work with AES encryption so that developers don't need to reuse the same AES code everywhere they need encryption.
Any and all comments are much appreciated.
```
///
/// Provides Cryptography methods based on AES cryptography implementation.
///
public class AesCrypto
{
///
/// Gets or sets the salt to use with AES encryption.
///
public byte[] Salt { get; set; }
///
/// Gets or sets the passphrase for use with AES encryption.
///
public string Passphrase { get; set; }
///
/// Constructs a new instance of from the specified values.
///
/// The used in encryption.
/// The used in encryption.
public AesCrypto(byte[] salt, string passphrase)
{
if (string.IsNullOrWhiteSpace(passphrase))
{
throw new ArgumentException($"The parameter {nameof(passphrase)} is required.");
}
if (salt == null || salt.Length == 0)
{
throw new ArgumentException($"The parameter {nameof(salt)} is required.");
}
Salt = salt;
Passphrase = passphrase;
}
///
/// Uses AES encryption to encrypt a string of data.
///
/// The data to encrypt. Data is expected to be Unicode.
/// An encrypted Base64 string.
public string AesEncrypt(string clearText)
{
byte[] clearBytes = Encoding.Unicode.GetBytes(clearText);
using (Aes encryptor = Aes.Create())
{
Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(Passphrase, Salt);
encryptor.Key = pdb.GetBytes(32);
encryptor.IV = pdb.GetBytes(16);
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateEncryptor(), CryptoStreamMode.Write))
Any and all comments are much appreciated.
```
///
/// Provides Cryptography methods based on AES cryptography implementation.
///
public class AesCrypto
{
///
/// Gets or sets the salt to use with AES encryption.
///
public byte[] Salt { get; set; }
///
/// Gets or sets the passphrase for use with AES encryption.
///
public string Passphrase { get; set; }
///
/// Constructs a new instance of from the specified values.
///
/// The used in encryption.
/// The used in encryption.
public AesCrypto(byte[] salt, string passphrase)
{
if (string.IsNullOrWhiteSpace(passphrase))
{
throw new ArgumentException($"The parameter {nameof(passphrase)} is required.");
}
if (salt == null || salt.Length == 0)
{
throw new ArgumentException($"The parameter {nameof(salt)} is required.");
}
Salt = salt;
Passphrase = passphrase;
}
///
/// Uses AES encryption to encrypt a string of data.
///
/// The data to encrypt. Data is expected to be Unicode.
/// An encrypted Base64 string.
public string AesEncrypt(string clearText)
{
byte[] clearBytes = Encoding.Unicode.GetBytes(clearText);
using (Aes encryptor = Aes.Create())
{
Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(Passphrase, Salt);
encryptor.Key = pdb.GetBytes(32);
encryptor.IV = pdb.GetBytes(16);
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateEncryptor(), CryptoStreamMode.Write))
Solution
public string AesEncrypt(string clearText)
{
byte[] clearBytes = Encoding.Unicode.GetBytes(clearText);
using (Aes encryptor = Aes.Create())
{
Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(Passphrase, Salt);
encryptor.Key = pdb.GetBytes(32);
encryptor.IV = pdb.GetBytes(16);
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateEncryptor(), CryptoStreamMode.Write))
{
cs.Write(clearBytes, 0, clearBytes.Length);
cs.Close();
}
clearText = StringExtensions.ToBase64String(ms.ToArray());
}
}
return clearText;
}-
the call of the
Close() method isn't needed, because the disposing of the CryptoStream will close the stream (See the source). -
Instead of calling
ms.ToArray() you should use the MemoryStream.GetBuffer() method to avoid having a copy of the byte array. The same is true for the
AesDecryptInternal() method. - because
Rfc2898DeriveBytesis inheritingDerivedByteswhich is implementingIDisposableyou should enclose it in anusingblock too.
The conditions when you throw an
ArgumentExceptionin the constructor aren't sufficient. The call to the Rfc2898DeriveBytes constructor will throw if the Salt isn't at least 8 bytes long. So checking against salt.Length < 8 should be done. In addition to whats being said about the length of the
Salt, you should consider to have a backing field for the Salt property so you can change the property to have the same validation there. This will be true for the Passphrase property too.Code Snippets
public string AesEncrypt(string clearText)
{
byte[] clearBytes = Encoding.Unicode.GetBytes(clearText);
using (Aes encryptor = Aes.Create())
{
Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(Passphrase, Salt);
encryptor.Key = pdb.GetBytes(32);
encryptor.IV = pdb.GetBytes(16);
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateEncryptor(), CryptoStreamMode.Write))
{
cs.Write(clearBytes, 0, clearBytes.Length);
cs.Close();
}
clearText = StringExtensions.ToBase64String(ms.ToArray());
}
}
return clearText;
}Context
StackExchange Code Review Q#110754, answer score: 6
Revisions (0)
No revisions yet.