Skip to content

Instantly share code, notes, and snippets.

@MovGP0
Created June 30, 2015 13:32
Show Gist options
  • Save MovGP0/f4cf6258ff8fa7735456 to your computer and use it in GitHub Desktop.
Save MovGP0/f4cf6258ff8fa7735456 to your computer and use it in GitHub Desktop.
Encryption in .NET
// Used to exchange symmetric keys
private static int RsaKeySize = 2048;
private void CreateKeys()
{
using(var provider = new RSACryptoServiceProvider(RsaKeySize))
{
provider.PersistKeyInCsp = false;
var publicKey = provider.ExportParameters(false);
var privateKey = provider.ExportParameters(true);
publicKey.Dump("Public Key");
privateKey.Dump("Private Key");
var publicKeyXml = provider.ToXmlString(false);
var privateKeyXml = provider.ToXmlString(true);
publicKeyXml.Dump("Public XML Key");
privateKeyXml.Dump("Private XML Key");
}
}
private byte[] EncryptAsymmetric(byte[] data, RSAParameters publicKey)
{
using(var provider = new RSACryptoServiceProvider(RsaKeySize))
{
provider.PersistKeyInCsp = false;
provider.ImportParameters(publicKey);
//provider.FromXmlString(publicKeyXml);
return provider.Encrypt(data, false);
}
}
private byte[] DecryptAsymmetric(byte[] cipher, RSAParameters privateKey)
{
using(var provider = new RSACryptoServiceProvider(RsaKeySize))
{
provider.PersistKeyInCsp = false;
provider.ImportParameters(privateKey);
// provider.FromXmlString(privateKeyXml);
return provider.Decrypt(cipher, false);
}
}
private void CreateKeyInCsp(string containerName)
{
var parameters = CreateCspParameters(containerName);
using(var provider = new RSACryptoServiceProvider(RsaKeySize, parameters))
{
provider.PersistKeyInCsp = true;
}
}
private void DeleteKeyFromCsp(string containerName)
{
var parameters = CreateCspParameters(containerName);
using(var provider = new RSACryptoServiceProvider(RsaKeySize, parameters))
{
provider.PersistKeyInCsp = false;
provider.Clear();
}
}
private byte[] EncryptUsingCsp(byte[] data, string providerName)
{
var parameters = CreateCspParameters(providerName);
using(var provider = new RSACryptoServiceProvider(RsaKeySize, parameters))
{
return provider.Encrypt(data, false);
}
}
private byte[] DecryptUsingCsp(byte[] cipher, string providerName)
{
var parameters = CreateCspParameters(providerName);
using(var provider = new RSACryptoServiceProvider(RsaKeySize, parameters))
{
return provider.Decrypt(cipher, false);
}
}
private CspParameters CreateCspParameters(string containerName)
{
return new CspParameters(1) // 1 = RSA
{
KeyContainerName = containerName,
Flags = CspProviderFlags.UseMachineKeyStore,
ProviderName = "Microsoft Strong Cryptographic Provider"
};
}
private byte[] Sign(byte[] sha512Hash, RSAParameters privateKey)
{
using(var provider = new RSACryptoServiceProvider(RsaKeySize))
{
provider.PersistKeyInCsp = false;
provider.ImportParameters(privateKey);
var formatter = new RSAPKCS1SignatureFormatter(provider);
formatter.SetHashAlgorithm("SHA512");
return formatter.CreateSignature(sha512Hash);
}
}
private bool VerifySignature(byte[] sha512Hash, byte[] signature, RSAParameters publicKey)
{
using(var provider = new RSACryptoServiceProvider(RsaKeySize))
{
provider.PersistKeyInCsp = false;
provider.ImportParameters(publicKey);
var deformatter = new RSAPKCS1SignatureDeformatter(provider);
deformatter.SetHashAlgorithm("SHA512");
return deformatter.VerifySignature(sha512Hash, signature);
}
}
private byte[] ComputeSha512Hmac(Stream message, byte[] key)
{
// could also use HMACMD5, HMACSHA1, HMACSHA256, HMACSHA512
using(var hmac = new HMACSHA512(key))
{
return hmac.ComputeHash(message);
}
}
// do not use for passwords
// Rainbow Table Attack with GPU; ie. Crackstaion
private byte[] ComputeSha512(Stream message)
{
using(var hasher = SHA512.Create())
{
return hasher.ComputeHash(message);
}
}
private byte[] ComputeSha512(byte[] message)
{
// could also use MD5, SHA1, SHA256
using(var hasher = SHA512.Create())
{
return hasher.ComputeHash(message);
}
}
private Stream ConvertToStream(string @string)
{
var stream = new MemoryStream();
var writer = new BinaryWriter(stream);
writer.Write(@string);
writer.Flush();
stream.Position = 0;
return stream;
}
private Stream ConvertToStream(byte[] buffer)
{
var stream = new MemoryStream();
var writer = new BinaryWriter(stream);
writer.Write(buffer);
writer.Flush();
stream.Position = 0;
return stream;
}
public byte[] ConvertToBytes(Stream stream)
{
using(var memoryStream = new MemoryStream())
{
stream.CopyTo(memoryStream);
return memoryStream.ToArray();
}
}
private byte[] ConvertToBytes(string @string)
{
return Encoding.UTF8.GetBytes(@string);
}
private string ConvertToBase64(byte[] data)
{
return Convert.ToBase64String(data);
}
private string ConvertToString(byte[] data)
{
return Encoding.UTF8.GetString(data);
}
private static byte[] Combine(byte[] first, byte[] second)
{
var length = first.Length + second.Length;
var buffer = new byte[length];
Buffer.BlockCopy(first, 0, buffer, 0, first.Length);
Buffer.BlockCopy(second, 0, buffer, first.Length, second.Length);
return buffer;
}
private sealed class SecureMessage
{
///<summary>The encrypted message</summary>
///<remarks>The message is encrypted with the session key</summary>
public byte[] EncryptedMessage { get; set; }
///<summary>Symmetric AES key used to encrypt this message</summary>
///<remarks>The session key is encrypted with the public RSA key</remarks>
public byte[] EncryptedSessionKey { get; set; }
///<summary>Initialization Vector</summary>
///<remarks>The IV is public and must be changed for each message.</remarks>
public byte[] IV { get; set; }
///<summary>Hashed Message Authentication Code of the encrypted data.</summary>
///<remarks>Prefer SHA512</remarks>
public byte[] HMAC { get; set; }
///<summary>The signature of the hash (HMAC)</summary>
///<remarks>Prefer SHA512</remarks>
public byte[] Signature { get; set; }
}
///<paramter name="publicKey">The public key of the recipient.</paramter>
///<paramter name="privateKey">The private key of the sender.</paramter>
private async Task<SecureMessage> EncryptMessageAsync(byte[] data, RSAParameters publicKey, RSAParameters privateKey)
{
var iv = GenerateRandomNumbers(16);
var sessionKey = GenerateRandomNumbers(32);
var encryptedSessionKey = EncryptAsymmetric(sessionKey, publicKey);
using(var encryptedDataStream = await EncryptSymmetricAsync(data, sessionKey, iv))
{
var hmac = ComputeSha512Hmac(encryptedDataStream, sessionKey);
var signature = Sign(hmac, privateKey);
return new SecureMessage
{
EncryptedMessage = ConvertToBytes(encryptedDataStream),
EncryptedSessionKey = encryptedSessionKey,
IV = iv,
HMAC = hmac,
Signature = signature
};
}
}
///<paramter name="privateKey">The private key of the recipient.</paramter>
///<paramter name="publicKey">The public key of the sender.</paramter>
private async Task<Stream> DecryptMessageAsync(SecureMessage message, RSAParameters privateKey, RSAParameters publicKey)
{
var decryptedSessionKey = DecryptAsymmetric(message.EncryptedSessionKey, privateKey);
byte[] hmac;
using(var encryptedMessageSteam = ConvertToStream(message.EncryptedMessage))
{
hmac = ComputeSha512Hmac(encryptedMessageSteam, decryptedSessionKey);
}
if(hmac != message.HMAC)
{
throw new CryptographicException("The HMAC of the message was invalid.");
}
var isValidSignature = VerifySignature(hmac, message.Signature, publicKey);
if(!isValidSignature)
{
throw new CryptographicException("The signature of the message was invalid.");
}
var decryptedDataStream = await DecryptSymmetricAsync(message.EncryptedMessage, decryptedSessionKey, message.IV);
return decryptedDataStream;
}
public static byte[] HashPassword(string message, byte[] salt, int numberOfRounds)
{
using (var hasher = new Rfc2898DeriveBytes(message, salt, numberOfRounds))
{
return hasher.GetBytes(32);
}
}
private byte[] GeneratePseudoRandomNumbers(int length)
{
var generator = new Random();
var buffer = new byte[length];
generator.NextBytes(buffer);
return buffer;
}
private byte[] GenerateRandomNumbers(int length)
{
using(var generator = new RNGCryptoServiceProvider())
{
var buffer = new byte[length];
generator.GetBytes(buffer);
return buffer;
}
}
// prefer PBKDF2 over salting
private void Salting()
{
var password = this.ConvertToBytes("This is a secret password!");
var salt = GenerateRandomNumbers(32);
var saltedPassword = Combine(password, salt);
ConvertToString(saltedPassword).Dump("Salted Password");
var hash = ComputeSha512(saltedPassword);
ConvertToBase64(hash).Dump("Hash");
}
///<summary>
/// Gets the string from the encrypted unmanaged memory and returns it
/// as an unencrypted managed string.
///</summary>
private static string ToString(SecureString secureString)
{
if(secureString == null)
{
throw new ArgumentNullException("secureString", "The secure string must not be null.");
}
var pointerToUnmanagedString = IntPtr.Zero;
try
{
pointerToUnmanagedString = Marshal.SecureStringToGlobalAllocUnicode(secureString);
return Marshal.PtrToStringUni(pointerToUnmanagedString);
}
finally
{
Marshal.ZeroFreeGlobalAllocUnicode(pointerToUnmanagedString);
}
}
private static SecureString ToSecureString(string secret)
{
if(secret == null)
{
throw new ArgumentNullException("secret", "The secret must not be null.");
}
unsafe
{
fixed(char* firstCharPointer = secret)
{
var secureString = new SecureString(firstCharPointer, secret.Length);
secureString.MakeReadOnly();
return secureString;
}
}
}
private bool CompareUnsecure(byte[] left, byte[] right)
{
if(left.Length != right.Length)
{
return false;
}
for(int i = 0; i < left.Length; i++)
{
if(left[i] != right[i])
{
return false;
}
}
return true;
}
private bool CompareSecure(byte[] left, byte[] right)
{
var result = left.Length == right.Length;
for(var i = 0; i < left.Length && i < right.Length; i++)
{
result &= left[i] == right[i];
}
// optional: introduce entropy using random wait time
return result;
}
// Initialization Vector (IV) is public; create a new IV for each message
// The key must be exchanged securely!
public async Task<Stream> EncryptSymmetricAsync(byte[] data, byte[] key, byte[] iv)
{
// alternative providers:
// DesCryptoServiceProvider()
// TripleDesCryptoServiceProvider()
using(var provider = new AesCryptoServiceProvider())
{
provider.Mode = CipherMode.CBC;
provider.Padding = PaddingMode.PKCS7;
provider.Key = key;
provider.IV = iv;
ICryptoTransform encryptor = null;
try
{
var stream = new MemoryStream();
encryptor = provider.CreateEncryptor();
using(var cryptoStream = new CryptoStream(stream, encryptor, CryptoStreamMode.Write))
{
encryptor = null;
await cryptoStream.WriteAsync(data, 0, data.Length);
cryptoStream.FlushFinalBlock();
stream.Position = 0;
return stream;
}
}
finally
{
if(encryptor != null)
{
encryptor.Dispose();
}
}
}
}
public async Task<Stream> DecryptSymmetricAsync(byte[] cipher, byte[] key, byte[] iv)
{
using(var provider = new AesCryptoServiceProvider())
{
provider.Mode = CipherMode.CBC;
provider.Padding = PaddingMode.PKCS7;
provider.Key = key;
provider.IV = iv;
ICryptoTransform decryptor = null;
try
{
var stream = new MemoryStream();
decryptor = provider.CreateDecryptor();
using(var cryptoStream = new CryptoStream(stream, decryptor, CryptoStreamMode.Write))
{
decryptor = null;
await cryptoStream.WriteAsync(cipher, 0, cipher.Length);
cryptoStream.FlushFinalBlock();
stream.Position = 0;
return stream;
}
}
finally
{
if(decryptor != null)
decryptor.Dispose();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment