using Scrypt; using System; using System.Collections.Generic; using System.Linq; using System.Security.Cryptography; using System.Text; using System.Text.RegularExpressions; namespace ManagerService.Helpers { /// /// Password utils /// public static class PasswordUtils { private const int DefaultIterationCount = 16384; private const int DefaultBlockSize = 8; private const int DefaultThreadCount = 1; private static readonly Random s_random = new Random((int)(DateTimeOffset.Now.Ticks % 100000)); #region Secure password management /// /// Generate a random key of the specified length (source: https://stackoverflow.com/a/31959204/249000) /// /// Length of the resulting key /// Allowed characters for the resulting key /// public static string GetUniqueKey(int length = 32, string chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890") { using (var crypto = new RNGCryptoServiceProvider()) { var data = new byte[length]; // If chars.Length isn't a power of 2 then there is a bias if // we simply use the modulus operator. The first characters of // chars will be more probable than the last ones. // buffer used if we encounter an unusable random byte. We will // regenerate it in this buffer byte[] smallBuffer = null; // Maximum random number that can be used without introducing a // bias int maxRandom = byte.MaxValue - ((byte.MaxValue + 1) % chars.Length); crypto.GetBytes(data); var result = new char[length]; for (int i = 0; i < length; i++) { byte v = data[i]; while (v > maxRandom) { if (smallBuffer == null) { smallBuffer = new byte[1]; } crypto.GetBytes(smallBuffer); v = smallBuffer[0]; } result[i] = chars[v % chars.Length]; } return new string(result); } } /// /// Encode the specified password with the specified pepper with the scrypt algorithm /// /// Password to encode /// Pepper to use /// scrypt algorithm iteration count /// scrypt algorithm block size /// scrypt algorithm thread count /// Encoded password public static string Encode(string password, string pepper = "", int iterationCount = DefaultIterationCount, int blockSize = DefaultBlockSize, int threadCount = DefaultThreadCount) { var encoder = new ScryptEncoder(iterationCount, blockSize, threadCount); return encoder.Encode(pepper + password); } /// /// Compare the specified encoded password with the specified password with the specified pepper with the scrypt algorithm /// /// Hashed password /// Password to compare /// Pepper to use /// scrypt algorithm iteration count /// scrypt algorithm block size /// scrypt algorithm thread count /// public static bool Compare(string hashedPassword, string password, string pepper = "", int iterationCount = DefaultIterationCount, int blockSize = DefaultBlockSize, int threadCount = DefaultThreadCount) { var encoder = new ScryptEncoder(iterationCount, blockSize, threadCount); return encoder.Compare(pepper + password, hashedPassword); } /// /// Generate a random password /// /// Pepper to use /// scrypt algorithm iteration count /// scrypt algorithm block size /// scrypt algorithm thread count /// Generated password public static string Generate(string pepper = "", int iterationCount = DefaultIterationCount, int blockSize = DefaultBlockSize, int threadCount = DefaultThreadCount) { return Encode(GetUniqueKey(), pepper, iterationCount, blockSize, threadCount); } #endregion /// /// Get a random string of the specified length /// /// Number of characters /// Random string of the specified length public static string GetRandomString(int length) { string pass = ""; for (int i = 0; i < length; i++) { int r = s_random.Next(10 + 26 + 26); char c; if (r < 10) c = (char)(r + 48); else if (r < 10 + 26) c = (char)((r - 10) + 65); else c = (char)((r - 10 - 26) + 97); pass += c; } return pass; } /// /// Get a random number with the specified maximum number of digits /// /// Maximum number of digits /// Random number with the specified maximum number of digits public static int GetRandomNumber(int maxDigits) { return s_random.Next((int)Math.Pow(10, maxDigits)); } /// /// Calculate the MD5 sum of the specified byte array and remove potential dashes /// /// Source array /// Source array MD5 sum public static string MD5Sum(byte[] source) { return BitConverter.ToString(new MD5CryptoServiceProvider().ComputeHash(source)).Replace("-", "").ToLower(); } /// /// Calculate the MD5 sum of the specified string and remove potential dashes /// /// Source string /// Source string MD5 sum public static string MD5Sum(string source) { return MD5Sum(Encoding.ASCII.GetBytes(source)); } } }