//-------------------------------------------------------------------------------------------------------------------------------------------------------------- /// <summary> /// Tests the encryption methods /// </summary> public static bool Test() { bool success = false; //----------------------------------------------------------------------------------------------------------------------------------------------------------- // Test the general encryption stuff ... SecureString tempKey = MGLEncryption.GenerateKey(); Console.WriteLine(tempKey); MGLEncryption.SetCryptKey = tempKey; StringBuilder testStr = new StringBuilder("What a lovely __ time of it!"); StringBuilder encryptedStr = MGLEncryption.Encrypt(testStr); StringBuilder decryptedStr = MGLEncryption.Decrypt(encryptedStr); bool theSame = MGLEncryption.Compare(testStr, encryptedStr); bool theSameStr = AreEqual(testStr, decryptedStr); bool notTheSame = MGLEncryption.Compare(new StringBuilder("Oh dear"), encryptedStr); StringBuilder htmledStr = MGLEncryption.HTMLifyString(encryptedStr); StringBuilder deHtmledStr = MGLEncryption.DeHTMLifyString(htmledStr); bool htmlWorking = MGLEncryption.AreEqual(deHtmledStr, encryptedStr); // Now test the password stuff ... SecureString tempKey2 = MGLEncryption.GenerateKey(); Console.WriteLine(tempKey2); success = (theSame == true) && (theSameStr == true) && (notTheSame == false) && (htmlWorking == true); return(success); }
//--------------------------------------------------------------------------------------------------------------------------------------------------------------- /// <summary> /// Generates a long key from a set of input characters /// </summary> public static StringBuilder GetSalt(int saltLength) { /* * Old simple approach to generating a random string - the Random class in C# is fundamentally flawed and will always * produce the same set of "random" numbers in the same sequence!!! * Here are our seed characters: * string chars = "abcdefghijklmnopqrstuvwxyz_ABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789_"; * Random random = new Random(); * string result = new string( * Enumerable.Repeat(chars, saltLength) * .Select(s => s[random.Next(s.Length)]) * .ToArray()); */ // New approach using dedicated random cryptographic class - Build a random list of characters to the specified length ... // use a Cryptographically Secure Pseudo-Random Number Generator (CSPRNG) RNGCryptoServiceProvider csprng = new RNGCryptoServiceProvider(); byte[] salt = new byte[saltLength]; csprng.GetBytes(salt); StringBuilder base64Str = new StringBuilder(Convert.ToBase64String(salt)); if (base64Str.Length > saltLength) { base64Str = MGLEncryption.SubstringStringBuilder(base64Str, 0, saltLength); } return(base64Str); }
//--------------------------------------------------------------------------------------------------------------------------------------------------------------- /// <summary> /// Compares two passwords - note that this method includes a Threading element so that hackers cannot tell how similar strings are /// by using code to time the comparison - e.g. a match on the first and second chars will be marginally slower than one that does not /// match on the first character /// /// The test password is UNencrypted, while the current password is fully encrypted ... /// </summary> public static bool Compare(StringBuilder testPassword, StringBuilder currentPassword) { bool success = false; string[] bits = null; try { // get the attributes from the current Password // have to conver it to a string unfortunately to split on the colons .... bits = currentPassword.ToString().Split(new string[] { ":" }, StringSplitOptions.None); int numIterations = 0; int.TryParse(bits[3], out numIterations); // Encrypt the test password StringBuilder encryptedTestPassword = Encrypt(testPassword, bits[1], bits[2], numIterations); // now do basic string comparison to compare the two strings! if (MGLEncryption.AreEqual(encryptedTestPassword, currentPassword) == true) { success = true; } } catch (Exception ex) { Logger.LogError(9, "MGLPasswordHash - error occurred while comparing two passwords: " + ex.ToString()); } finally { // always kill the array of bits bits = null; } // Now introduce a random response element, delaying by up to 100 ms. (0.1 seconds) Thread.Sleep(new Random().Next(0, 100)); return(success); }
////--------------------------------------------------------------------------------------------------------------------------------------------------------------- ///// <summary> ///// Generates a long key from a set of input characters ///// 13-Oct-2015 - Use the MGLEncryption method instead ... ///// </summary> //public static string GetSalt(int saltLength) { // // Use a dedicated random cryptographic class to build a random list of characters to the specified length ... // // use a Cryptographically Secure Pseudo-Random Number Generator (CSPRNG) // RNGCryptoServiceProvider csprng = new RNGCryptoServiceProvider(); // byte[] salt = new byte[saltLength]; // csprng.GetBytes(salt); // string base64Str = Convert.ToBase64String(salt); // return base64Str; //} //-------------------------------------------------------------------------------------------------------------------------------------------------------------- private static bool TestEncryption() { bool success = false; //----------------------------------------------------------------------------------------------------------------------------------------------------------- // Test the general encryption stuff ... StringBuilder tempKey = MGLEncryption.GetSalt(30); StringBuilder tempKey2 = MGLEncryption.GetSalt(30); // Test the mgl encryption 2 ... StringBuilder testPword2 = MGLPasswordHash.EncryptPassword(tempKey); StringBuilder testPword3 = MGLPasswordHash.EncryptPassword(tempKey2); bool theSame3 = MGLPasswordHash.Compare(tempKey, testPword2); bool theSame4 = MGLPasswordHash.Compare(tempKey, testPword3); success = theSame3 == true && theSame4 == false; return(success); }
//--------------------------------------------------------------------------------------------------------------------------------------------------------------- /// <summary> /// Encrypts the given string /// </summary> public static StringBuilder EncryptPassword(StringBuilder password) { StringBuilder encryptedPassword = null; try { // First we need to turn the input string into a byte array. // 5-Jul-15 - by adding a random padding of 8 chars at the start, we ensure that a password of "Hello World" will not be // the same twice when encrypted StringBuilder randomPaddingSalt = MGLEncryption.GetSalt(SaltLength); // Turn the password into Key and IV. We are using salt to make it harder to guess our key // using a dictionary attack - trying to guess a password by enumerating all possible words. // and generate a password specific salt that we will append to the end of the string ... StringBuilder randomAlgSaltStr = MGLEncryption.GetSalt(SaltLength); encryptedPassword = Encrypt(password, randomPaddingSalt.ToString(), randomAlgSaltStr.ToString(), SaltIterations); } catch (Exception ex) { Logger.LogError(9, "Error trying to encrypt a password. " + ex.StackTrace); } return(encryptedPassword); }
//--------------------------------------------------------------------------------------------------------------------------------------------------------- /// <summary> /// Compares the two secure strings and returns true if they are equivalent textually, both null or both empty /// if isCaseSensitive == true, we also compare the case sensitivity of the information - but this needs a string comparison /// so will result in strings popping into memory and is probably a little bit slower ... /// </summary> public static bool AreEqual(SecureString ss1, SecureString ss2, bool isCaseSensitive) { return(MGLEncryption.AreEqual(SecureStringWrapper.Decrypt(ss1), SecureStringWrapper.Decrypt(ss2), isCaseSensitive)); }