vote buttons
4
01 Feb 2015 by K Bonneau

String Encryption And Decryption

Contract
IStringUtilities
string Encrypt(string message, string password); string Decrypt(string encryptedMessage, string password);
Test Cases
vote buttons
1
Created By K Bonneau
public void WhenEncryptingAndDecrypting_ReturnsTheOriginalMessage(IStringUtilities stringUtilities) { var message = "This is a secret message"; var encrypted = stringUtilities.Encrypt(message, "p@ssw0rd0123"); var decrypted = stringUtilities.Decrypt(encrypted, "p@ssw0rd0123"); Assert.AreEqual(message, decrypted); }
You must be logged in to add test cases

2 Solutions

vote buttons
2
352.21
avg time in ms
07 Feb 2015 by K Bonneau
    * This work (Modern Encryption of a String C#, by James Tuley), 
    * identified by James Tuley, is free of known copyright restrictions.
    * https://gist.github.com/4336842
    * http://creativecommons.org/publicdomain/mark/1.0/ 

using System.Security.Cryptography; public class Solution : IStringUtilities { public string Encrypt(string message, string password) { return AESThenHMAC.SimpleEncryptWithPassword(message, password); } public string Decrypt(string encryptedMessage, string password) { return AESThenHMAC.SimpleDecryptWithPassword(encryptedMessage, password); } } public class AESThenHMAC { private static readonly RandomNumberGenerator Random = RandomNumberGenerator.Create(); //Preconfigured Encryption Parameters public static readonly int BlockBitSize = 128; public static readonly int KeyBitSize = 256; //Preconfigured Password Key Derivation Parameters public static readonly int SaltBitSize = 64; public static readonly int Iterations = 10000; public static readonly int MinPasswordLength = 12; /// <summary> /// Helper that generates a random key on each call. /// </summary> /// <returns></returns> public static byte[] NewKey() { var key = new byte[KeyBitSize / 8]; Random.GetBytes(key); return key; } /// <summary> /// Simple Encryption (AES) then Authentication (HMAC) for a UTF8 Message. /// </summary> /// <param name="secretMessage">The secret message.</param> /// <param name="cryptKey">The crypt key.</param> /// <param name="authKey">The auth key.</param> /// <param name="nonSecretPayload">(Optional) Non-Secret Payload.</param> /// <returns> /// Encrypted Message /// </returns> /// <exception cref="System.ArgumentException">Secret Message Required!;secretMessage</exception> /// <remarks> /// Adds overhead of (Optional-Payload + BlockSize(16) + Message-Padded-To-Blocksize + HMac-Tag(32)) * 1.33 Base64 /// </remarks> public static string SimpleEncrypt(string secretMessage, byte[] cryptKey, byte[] authKey, byte[] nonSecretPayload = null) { if (string.IsNullOrEmpty(secretMessage)) throw new ArgumentException("Secret Message Required!", "secretMessage"); var plainText = Encoding.UTF8.GetBytes(secretMessage); var cipherText = SimpleEncrypt(plainText, cryptKey, authKey, nonSecretPayload); return Convert.ToBase64String(cipherText); } /// <summary> /// Simple Authentication (HMAC) then Decryption (AES) for a secrets UTF8 Message. /// </summary> /// <param name="encryptedMessage">The encrypted message.</param> /// <param name="cryptKey">The crypt key.</param> /// <param name="authKey">The auth key.</param> /// <param name="nonSecretPayloadLength">Length of the non secret payload.</param> /// <returns> /// Decrypted Message /// </returns> /// <exception cref="System.ArgumentException">Encrypted Message Required!;encryptedMessage</exception> public static string SimpleDecrypt(string encryptedMessage, byte[] cryptKey, byte[] authKey, int nonSecretPayloadLength = 0) { if (string.IsNullOrWhiteSpace(encryptedMessage)) throw new ArgumentException("Encrypted Message Required!", "encryptedMessage"); var cipherText = Convert.FromBase64String(encryptedMessage); var plainText = SimpleDecrypt(cipherText, cryptKey, authKey, nonSecretPayloadLength); return plainText == null ? null : Encoding.UTF8.GetString(plainText); } /// <summary> /// Simple Encryption (AES) then Authentication (HMAC) of a UTF8 message /// using Keys derived from a Password (PBKDF2). /// </summary> /// <param name="secretMessage">The secret message.</param> /// <param name="password">The password.</param> /// <param name="nonSecretPayload">The non secret payload.</param> /// <returns> /// Encrypted Message /// </returns> /// <exception cref="System.ArgumentException">password</exception> /// <remarks> /// Significantly less secure than using random binary keys. /// Adds additional non secret payload for key generation parameters. /// </remarks> public static string SimpleEncryptWithPassword(string secretMessage, string password, byte[] nonSecretPayload = null) { if (string.IsNullOrEmpty(secretMessage)) throw new ArgumentException("Secret Message Required!", "secretMessage"); var plainText = Encoding.UTF8.GetBytes(secretMessage); var cipherText = SimpleEncryptWithPassword(plainText, password, nonSecretPayload); return Convert.ToBase64String(cipherText); } /// <summary> /// Simple Authentication (HMAC) and then Descryption (AES) of a UTF8 Message /// using keys derived from a password (PBKDF2). /// </summary> /// <param name="encryptedMessage">The encrypted message.</param> /// <param name="password">The password.</param> /// <param name="nonSecretPayloadLength">Length of the non secret payload.</param> /// <returns> /// Decrypted Message /// </returns> /// <exception cref="System.ArgumentException">Encrypted Message Required!;encryptedMessage</exception> /// <remarks> /// Significantly less secure than using random binary keys. /// </remarks> public static string SimpleDecryptWithPassword(string encryptedMessage, string password, int nonSecretPayloadLength = 0) { if (string.IsNullOrWhiteSpace(encryptedMessage)) throw new ArgumentException("Encrypted Message Required!", "encryptedMessage"); var cipherText = Convert.FromBase64String(encryptedMessage); var plainText = SimpleDecryptWithPassword(cipherText, password, nonSecretPayloadLength); return plainText == null ? null : Encoding.UTF8.GetString(plainText); } /// <summary> /// Simple Encryption(AES) then Authentication (HMAC) for a UTF8 Message. /// </summary> /// <param name="secretMessage">The secret message.</param> /// <param name="cryptKey">The crypt key.</param> /// <param name="authKey">The auth key.</param> /// <param name="nonSecretPayload">(Optional) Non-Secret Payload.</param> /// <returns> /// Encrypted Message /// </returns> /// <remarks> /// Adds overhead of (Optional-Payload + BlockSize(16) + Message-Padded-To-Blocksize + HMac-Tag(32)) * 1.33 Base64 /// </remarks> public static byte[] SimpleEncrypt(byte[] secretMessage, byte[] cryptKey, byte[] authKey, byte[] nonSecretPayload = null) { //User Error Checks if (cryptKey == null || cryptKey.Length != KeyBitSize / 8) throw new ArgumentException(String.Format("Key needs to be {0} bit!", KeyBitSize), "cryptKey"); if (authKey == null || authKey.Length != KeyBitSize / 8) throw new ArgumentException(String.Format("Key needs to be {0} bit!", KeyBitSize), "authKey"); if (secretMessage == null || secretMessage.Length < 1) throw new ArgumentException("Secret Message Required!", "secretMessage"); //non-secret payload optional nonSecretPayload = nonSecretPayload ?? new byte[] { }; byte[] cipherText; byte[] iv; using (var aes = new AesManaged { KeySize = KeyBitSize, BlockSize = BlockBitSize, Mode = CipherMode.CBC, Padding = PaddingMode.PKCS7 }) { //Use random IV aes.GenerateIV(); iv = aes.IV; using (var encrypter = aes.CreateEncryptor(cryptKey, iv)) using (var cipherStream = new MemoryStream()) { using (var cryptoStream = new CryptoStream(cipherStream, encrypter, CryptoStreamMode.Write)) using (var binaryWriter = new BinaryWriter(cryptoStream)) { //Encrypt Data binaryWriter.Write(secretMessage); } cipherText = cipherStream.ToArray(); } } //Assemble encrypted message and add authentication using (var hmac = new HMACSHA256(authKey)) using (var encryptedStream = new MemoryStream()) { using (var binaryWriter = new BinaryWriter(encryptedStream)) { //Prepend non-secret payload if any binaryWriter.Write(nonSecretPayload); //Prepend IV binaryWriter.Write(iv); //Write Ciphertext binaryWriter.Write(cipherText); binaryWriter.Flush(); //Authenticate all data var tag = hmac.ComputeHash(encryptedStream.ToArray()); //Postpend tag binaryWriter.Write(tag); } return encryptedStream.ToArray(); } } /// <summary> /// Simple Authentication (HMAC) then Decryption (AES) for a secrets UTF8 Message. /// </summary> /// <param name="encryptedMessage">The encrypted message.</param> /// <param name="cryptKey">The crypt key.</param> /// <param name="authKey">The auth key.</param> /// <param name="nonSecretPayloadLength">Length of the non secret payload.</param> /// <returns>Decrypted Message</returns> public static byte[] SimpleDecrypt(byte[] encryptedMessage, byte[] cryptKey, byte[] authKey, int nonSecretPayloadLength = 0) { //Basic Usage Error Checks if (cryptKey == null || cryptKey.Length != KeyBitSize / 8) throw new ArgumentException(String.Format("CryptKey needs to be {0} bit!", KeyBitSize), "cryptKey"); if (authKey == null || authKey.Length != KeyBitSize / 8) throw new ArgumentException(String.Format("AuthKey needs to be {0} bit!", KeyBitSize), "authKey"); if (encryptedMessage == null || encryptedMessage.Length == 0) throw new ArgumentException("Encrypted Message Required!", "encryptedMessage"); using (var hmac = new HMACSHA256(authKey)) { var sentTag = new byte[hmac.HashSize / 8]; //Calculate Tag var calcTag = hmac.ComputeHash(encryptedMessage, 0, encryptedMessage.Length - sentTag.Length); var ivLength = (BlockBitSize / 8); //if message length is to small just return null if (encryptedMessage.Length < sentTag.Length + nonSecretPayloadLength + ivLength) return null; //Grab Sent Tag Array.Copy(encryptedMessage, encryptedMessage.Length - sentTag.Length, sentTag, 0, sentTag.Length); //Compare Tag with constant time comparison var compare = 0; for (var i = 0; i < sentTag.Length; i++) compare |= sentTag[i] ^ calcTag[i]; //if message doesn't authenticate return null if (compare != 0) return null; using (var aes = new AesManaged { KeySize = KeyBitSize, BlockSize = BlockBitSize, Mode = CipherMode.CBC, Padding = PaddingMode.PKCS7 }) { //Grab IV from message var iv = new byte[ivLength]; Array.Copy(encryptedMessage, nonSecretPayloadLength, iv, 0, iv.Length); using (var decrypter = aes.CreateDecryptor(cryptKey, iv)) using (var plainTextStream = new MemoryStream()) { using (var decrypterStream = new CryptoStream(plainTextStream, decrypter, CryptoStreamMode.Write)) using (var binaryWriter = new BinaryWriter(decrypterStream)) { //Decrypt Cipher Text from Message binaryWriter.Write( encryptedMessage, nonSecretPayloadLength + iv.Length, encryptedMessage.Length - nonSecretPayloadLength - iv.Length - sentTag.Length ); } //Return Plain Text return plainTextStream.ToArray(); } } } } /// <summary> /// Simple Encryption (AES) then Authentication (HMAC) of a UTF8 message /// using Keys derived from a Password (PBKDF2) /// </summary> /// <param name="secretMessage">The secret message.</param> /// <param name="password">The password.</param> /// <param name="nonSecretPayload">The non secret payload.</param> /// <returns> /// Encrypted Message /// </returns> /// <exception cref="System.ArgumentException">Must have a password of minimum length;password</exception> /// <remarks> /// Significantly less secure than using random binary keys. /// Adds additional non secret payload for key generation parameters. /// </remarks> public static byte[] SimpleEncryptWithPassword(byte[] secretMessage, string password, byte[] nonSecretPayload = null) { nonSecretPayload = nonSecretPayload ?? new byte[] { }; //User Error Checks if (string.IsNullOrWhiteSpace(password) || password.Length < MinPasswordLength) throw new ArgumentException(String.Format("Must have a password of at least {0} characters!", MinPasswordLength), "password"); if (secretMessage == null || secretMessage.Length == 0) throw new ArgumentException("Secret Message Required!", "secretMessage"); var payload = new byte[((SaltBitSize / 8) * 2) + nonSecretPayload.Length]; Array.Copy(nonSecretPayload, payload, nonSecretPayload.Length); int payloadIndex = nonSecretPayload.Length; byte[] cryptKey; byte[] authKey; //Use Random Salt to prevent pre-generated weak password attacks. using (var generator = new Rfc2898DeriveBytes(password, SaltBitSize / 8, Iterations)) { var salt = generator.Salt; //Generate Keys cryptKey = generator.GetBytes(KeyBitSize / 8); //Create Non Secret Payload Array.Copy(salt, 0, payload, payloadIndex, salt.Length); payloadIndex += salt.Length; } //Deriving separate key, might be less efficient than using HKDF, //but now compatible with RNEncryptor which had a very similar wireformat and requires less code than HKDF. using (var generator = new Rfc2898DeriveBytes(password, SaltBitSize / 8, Iterations)) { var salt = generator.Salt; //Generate Keys authKey = generator.GetBytes(KeyBitSize / 8); //Create Rest of Non Secret Payload Array.Copy(salt, 0, payload, payloadIndex, salt.Length); } return SimpleEncrypt(secretMessage, cryptKey, authKey, payload); } /// <summary> /// Simple Authentication (HMAC) and then Descryption (AES) of a UTF8 Message /// using keys derived from a password (PBKDF2). /// </summary> /// <param name="encryptedMessage">The encrypted message.</param> /// <param name="password">The password.</param> /// <param name="nonSecretPayloadLength">Length of the non secret payload.</param> /// <returns> /// Decrypted Message /// </returns> /// <exception cref="System.ArgumentException">Must have a password of minimum length;password</exception> /// <remarks> /// Significantly less secure than using random binary keys. /// </remarks> public static byte[] SimpleDecryptWithPassword(byte[] encryptedMessage, string password, int nonSecretPayloadLength = 0) { //User Error Checks if (string.IsNullOrWhiteSpace(password) || password.Length < MinPasswordLength) throw new ArgumentException(String.Format("Must have a password of at least {0} characters!", MinPasswordLength), "password"); if (encryptedMessage == null || encryptedMessage.Length == 0) throw new ArgumentException("Encrypted Message Required!", "encryptedMessage"); var cryptSalt = new byte[SaltBitSize / 8]; var authSalt = new byte[SaltBitSize / 8]; //Grab Salt from Non-Secret Payload Array.Copy(encryptedMessage, nonSecretPayloadLength, cryptSalt, 0, cryptSalt.Length); Array.Copy(encryptedMessage, nonSecretPayloadLength + cryptSalt.Length, authSalt, 0, authSalt.Length); byte[] cryptKey; byte[] authKey; //Generate crypt key using (var generator = new Rfc2898DeriveBytes(password, cryptSalt, Iterations)) { cryptKey = generator.GetBytes(KeyBitSize / 8); } //Generate auth key using (var generator = new Rfc2898DeriveBytes(password, authSalt, Iterations)) { authKey = generator.GetBytes(KeyBitSize / 8); } return SimpleDecrypt(encryptedMessage, cryptKey, authKey, cryptSalt.Length + authSalt.Length + nonSecretPayloadLength); } }
Test Case Run Summary
WhenEncryptingAndDecrypting_ReturnsTheOriginalMessage
download solution
Try this Solution
Comments
vote buttons
0
0.38
avg time in ms
01 Feb 2015 by K Bonneau
Based on http://webmodelling.com/webbits/aspnet/utility-functions/String-Encrypt.aspx
public class Solution : IStringUtilities { public string Encrypt(string message, string password) { byte[] results; System.Text.UTF8Encoding UTF8 = new System.Text.UTF8Encoding(); var HashProvider = new System.Security.Cryptography.MD5CryptoServiceProvider(); byte[] TDESKey = HashProvider.ComputeHash(UTF8.GetBytes(password)); var TDESAlgorithm = new System.Security.Cryptography.TripleDESCryptoServiceProvider(); TDESAlgorithm.Key = TDESKey; TDESAlgorithm.Mode = System.Security.Cryptography.CipherMode.ECB; TDESAlgorithm.Padding = System.Security.Cryptography.PaddingMode.PKCS7; byte[] dataToEncrypt = UTF8.GetBytes(message); try { var encryptor = TDESAlgorithm.CreateEncryptor(); results = encryptor.TransformFinalBlock(dataToEncrypt, 0, dataToEncrypt.Length); } finally { TDESAlgorithm.Clear(); HashProvider.Clear(); } return Convert.ToBase64String(results); } public string Decrypt(string encryptedMessage, string password) { byte[] results; System.Text.UTF8Encoding UTF8 = new System.Text.UTF8Encoding(); var hashProvider = new System.Security.Cryptography.MD5CryptoServiceProvider(); byte[] tdesKey = hashProvider.ComputeHash(UTF8.GetBytes(password)); var tdesAlgorithm = new System.Security.Cryptography.TripleDESCryptoServiceProvider(); tdesAlgorithm.Key = tdesKey; tdesAlgorithm.Mode = System.Security.Cryptography.CipherMode.ECB; tdesAlgorithm.Padding = System.Security.Cryptography.PaddingMode.PKCS7; byte[] dataToDecrypt = Convert.FromBase64String(encryptedMessage); try { System.Security.Cryptography.ICryptoTransform decryptor = tdesAlgorithm.CreateDecryptor(); results = decryptor.TransformFinalBlock(dataToDecrypt, 0, dataToDecrypt.Length); } finally { tdesAlgorithm.Clear(); hashProvider.Clear(); } return UTF8.GetString(results); } }
Test Case Run Summary
WhenEncryptingAndDecrypting_ReturnsTheOriginalMessage
download solution
Try this Solution
Comments