Created
June 30, 2015 13:32
-
-
Save MovGP0/f4cf6258ff8fa7735456 to your computer and use it in GitHub Desktop.
Encryption in .NET
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" | |
}; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
private byte[] ComputeSha512Hmac(Stream message, byte[] key) | |
{ | |
// could also use HMACMD5, HMACSHA1, HMACSHA256, HMACSHA512 | |
using(var hmac = new HMACSHA512(key)) | |
{ | |
return hmac.ComputeHash(message); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
* [Key and Hash Lengths](https://msdn.microsoft.com/en-us/library/windows/desktop/bb204778.aspx#supported_algorithms) | |
* [Data Protection API (DPAPI)](https://msdn.microsoft.com/en-us/library/ms995355.aspx) | |
* "Cryptographic Services" service (services.msc) | |
* Certificate Management (certmgr.msc) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public static byte[] HashPassword(string message, byte[] salt, int numberOfRounds) | |
{ | |
using (var hasher = new Rfc2898DeriveBytes(message, salt, numberOfRounds)) | |
{ | |
return hasher.GetBytes(32); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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"); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
///<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; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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