Don't use a block cipher with ECB for symmetric encryption
(Applies to AES, 3DES, ... )
Here is a post and a very similar Microsoft KB article regarding how ECB mode results in code that isn't encrypted.
Also see this similar post from Rook
Plain text message:
The same message encrypted with ECB mode (doesn't matter what cipher you use):
The EXACT same message using CBC mode (again, it doesn't matter what cipher you use):
The wrong way
public static string Encrypt(string toEncrypt, string key, bool useHashing)
{
byte[] keyArray = UTF8Encoding.UTF8.GetBytes(key);
byte[] toEncryptArray = UTF8Encoding.UTF8.GetBytes(toEncrypt);
if (useHashing)
keyArray = new MD5CryptoServiceProvider().ComputeHash(keyArray);
var tdes = new TripleDESCryptoServiceProvider()
{ Key = keyArray, Mode = CipherMode.ECB, Padding = PaddingMode.PKCS7 };
ICryptoTransform cTransform = tdes.CreateEncryptor();
byte[] resultArray = cTransform.TransformFinalBlock(
toEncryptArray, 0, toEncryptArray.Length);
return Convert.ToBase64String(resultArray, 0, resultArray.Length);
}
The error is in the following line
{ Key = keyArray, Mode = CipherMode.ECB, Padding = PaddingMode.PKCS7 };
The right way
The good folks at Microsoft sent me the following code to correct that KB article linked above. This is referenced in case# 111021973179005
This sample code is using AES to encrypt data, and the key for the AES encryption is the hash code generated by SHA256. AES is the Advanced Encryption Standard (AES) algorithm. The AES algorithm is based on permutations and substitutions. Permutations are rearrangements of data, and substitutions replace one unit of data with another. AES performs permutations and substitutions using several different techniques. For more details of AES, please refer to the article “Keep Your Data Secure with the New Advanced Encryption Standard” on MSDN Magazine at http://msdn.microsoft.com/en-us/magazine/cc164055.aspx .
SHA is the Secure Hash Algorithm. SHA-2 (SHA-224, SHA-256, SHA-384, SHA-512) is now recommended. For more detailed information on Hash Values in .NET Framework, please refer to http://msdn.microsoft.com/en-us/library/92f9ye3s.aspx#hash_values .
The default value of the mode for operation of the symmetric algorithm for AesCryptoServiceProvider
is CBC. CBC is the Cipher Block Chaining mode. It introduces feedback. Before each plain text block is encrypted, it is combined with the cipher text of the previous block by a bitwise exclusive OR operation. This ensures that even if the plain text contains many identical blocks, they will each encrypt to a different cipher text block. The initialization vector is combined with the first plain text block by a bitwise exclusive OR operation before the block is encrypted. If a single bit of the cipher text block is mangled, the corresponding plain text block will also be mangled. In addition, a bit in the subsequent block, in the same position as the original mangled bit, will be mangled. For more detailed information about CipherMode
, please refer to http://msdn.microsoft.com/en-us/library/system.security.cryptography.ciphermode.aspx .
Here’s the sample code.
// This function is used for encrypting the data with key and iv.
byte[] Encrypt(byte[] data, byte[] key, byte[] iv)
{
// Create an AESCryptoProvider.
using (var aesCryptoProvider = new AesCryptoServiceProvider())
{
// Initialize the AESCryptoProvider with key and iv.
aesCryptoProvider.KeySize = key.Length * 8;
aesCryptoProvider.IV = iv;
aesCryptoProvider.Key = key;
// Create encryptor from the AESCryptoProvider.
using (ICryptoTransform encryptor = aesCryptoProvider.CreateEncryptor())
{
// Create memory stream to store the encrypted data.
using (MemoryStream stream = new MemoryStream())
{
// Create a CryptoStream to encrypt the data.
using (CryptoStream cryptoStream = new CryptoStream(stream, encryptor, CryptoStreamMode.Write))
// Encrypt the data.
cryptoStream.Write(data, 0, data.Length);
// return the encrypted data.
return stream.ToArray();
}
}
}
}
// This function is used for decrypting the data with key and iv.
byte[] Decrypt(byte[] data, byte[] key, byte[] iv)
{
// Create an AESCryptoServiceProvider.
using (var aesCryptoProvider = new AesCryptoServiceProvider())
{
// Initialize the AESCryptoServiceProvier with key and iv.
aesCryptoProvider.KeySize = key.Length * 8;
aesCryptoProvider.IV = iv;
aesCryptoProvider.Key = key;
// Create decryptor from the AESCryptoServiceProvider.
using (ICryptoTransform decryptor = aesCryptoProvider.CreateDecryptor())
{
// Create a memory stream including the encrypted data.
using (MemoryStream stream = new MemoryStream(data))
{
// Create a CryptoStream to decrypt the encrypted data.
using (CryptoStream cryptoStream = new CryptoStream(stream, decryptor, CryptoStreamMode.Read))
{
// Create a byte buffer array.
byte[] readData = new byte[1024];
int readDataCount = 0;
// Create a memory stream to store the decrypted data.
using (MemoryStream resultStream = new MemoryStream())
{
do
{
// Decrypt the data and write the data into readData buffer array.
readDataCount = cryptoStream.Read(readData, 0, readData.Length);
// Write the decrypted data to resultStream.
resultStream.Write(readData, 0, readDataCount);
}
// Check whether there is any more encrypted data in stream.
while (readDataCount > 0);
// Return the decrypted data.
return resultStream.ToArray();
}
}
}
}
}
}
// This function is used for generating a valid key binary with UTF8 encoding and SHA256 hash algorithm.
byte[] GetKey(string key)
{
// Create SHA256 hash algorithm class.
using (SHA256Managed sha256 = new SHA256Managed())
// Decode the string key to binary and compute the hash binary of the key.
return sha256.ComputeHash(Encoding.UTF8.GetBytes(key));
}
For more details of the classes in the sample code, please refer to the following links:
· AesCryptoServiceProvider Class
· SHA256Managed Class
· CryptoStream Class
Additionally, there are several articles which may help get a better understanding of cryptography in .NET Framework, please refer to the links below:
· Cryptographic Services
· .NET Framework Cryptography Model
· A Simple Guide to Cryptography
· Encrypting Without Secrets