-
Notifications
You must be signed in to change notification settings - Fork 55
Crypto Recipes
If it isn't documented here, there's a very good chance it is still available in the library. The documentation below is incomplete.
A couple of very common mistakes made by would-be cryptographers:
-
Using a key that is not one of the allowed sizes. What are the allowed sizes?
-
Picking an algorithm that does not include padding and then not supplying data that exactly fills a cipher block. Which algorithms include padding?
// WinRT-like API
uint length = 15;
byte[] cryptoRandomBuffer = WinRTCrypto.CryptographicBuffer.GenerateRandom(length);
// .NET Framework-like API
byte[] cryptoRandomBuffer = new byte[15];
NetFxCrypto.RandomNumberGenerator.GetBytes(cryptoRandomBuffer);
uint random = WinRTCrypto.CryptographicBuffer.GenerateRandomNumber();
uint randomFrom0To15 = WinRTCrypto.CryptographicBuffer.GenerateRandomNumber() % 16;
To use crypto APIs with a user-supplied password, you must at least convert the password into a byte buffer. But it's more secure to run it through a key derivation algorithm, using this technique:
string password; // comes in from the user.
byte[] salt; // best initialized to a unique value for each user, and stored with the user record
int iterations = 5000; // higher makes brute force attacks more expensive
int keyLengthInBytes;
byte[] key = NetFxCrypto.DeriveBytes.GetBytes(password, salt, iterations, keyLengthInBytes);
// A similar technique is possible using WinRT-emulated API via the `WinRTCrypto.KeyDerivationAlgorithmProvider`.
byte[] data;
var hasher = WinRTCrypto.HashAlgorithmProvider.OpenAlgorithm(HashAlgorithm.Sha1);
byte[] hash = hasher.HashData(data);
string hashBase64 = Convert.ToBase64String(hash);
byte[] keyMaterial;
byte[] data;
var algorithm = WinRTCrypto.MacAlgorithmProvider.OpenAlgorithm(MacAlgorithm.HmacSha1);
CryptographicHash hasher = algorithm.CreateHash(keyMaterial);
hasher.Append(data);
byte[] mac = hasher.GetValueAndReset();
string macBase64 = Convert.ToBase64String(mac);
byte[] keyMaterial;
byte[] data;
var provider = WinRTCrypto.SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithm.AesCbcPkcs7);
var key = provider.CreateSymmetricKey(keyMaterial);
// The IV may be null, but supplying a random IV increases security.
// The IV is not a secret like the key is.
// You can transmit the IV (w/o encryption) alongside the ciphertext.
var iv = WinRTCrypto.CryptographicBuffer.GenerateRandom(provider.BlockLength);
byte[] cipherText = WinRTCrypto.CryptographicEngine.Encrypt(key, data, iv);
// When decrypting, use the same IV that was passed to encrypt.
byte[] plainText = WinRTCrypto.CryptographicEngine.Decrypt(key, cipherText, iv);
This method will perform encrypt or decrypt a file, depending on its arguments:
static async Task CryptoTransformFileAsync(string sourcePath, string destinationPath, ICryptoTransform[] transforms, CancellationToken cancellationToken = default)
{
const int BufferSize = 4096;
using (var sourceStream = new FileStream(sourcePath, FileMode.Open, FileAccess.Read, FileShare.Read, BufferSize, useAsync: true))
{
using (var destinationStream = new FileStream(destinationPath, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None, BufferSize, useAsync: true))
{
using (var cryptoStream = CryptoStream.WriteTo(destinationStream, transforms))
{
await sourceStream.CopyToAsync(cryptoStream, BufferSize, cancellationToken);
await cryptoStream.FlushAsync(cancellationToken);
cryptoStream.FlushFinalBlock();
}
}
}
}
The following method calls the above method to encrypt then decrypt a file.
private const string beforeFilePath = @"d:\temp\before.txt";
private const string cipherFilePath = @"d:\temp\cipher.txt";
private const string afterFilePath = @"d:\temp\after.txt";
static async Task EncryptThenDecryptAsync()
{
ISymmetricKeyAlgorithmProvider aesGcm = WinRTCrypto.SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithm.AesCbcPkcs7);
byte[] keyMaterial = WinRTCrypto.CryptographicBuffer.GenerateRandom(32);
var cryptoKey = aesGcm.CreateSymmetricKey(keyMaterial);
var encryptor = new ICryptoTransform[] { WinRTCrypto.CryptographicEngine.CreateEncryptor(cryptoKey) };
var decryptor = new ICryptoTransform[] { WinRTCrypto.CryptographicEngine.CreateDecryptor(cryptoKey) };
await CryptoTransformFileAsync(beforeFilePath, cipherFilePath, encryptor);
await CryptoTransformFileAsync(cipherFilePath, afterFilePath, decryptor);
}