171 lines
6.9 KiB
C#
171 lines
6.9 KiB
C#
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
|
|
{
|
|
/// <summary>
|
|
/// Password utils
|
|
/// </summary>
|
|
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
|
|
|
|
/// <summary>
|
|
/// Generate a random key of the specified length (source: https://stackoverflow.com/a/31959204/249000)
|
|
/// </summary>
|
|
/// <param name="length">Length of the resulting key</param>
|
|
/// <param name="chars">Allowed characters for the resulting key</param>
|
|
/// <returns></returns>
|
|
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);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Encode the specified password with the specified pepper with the scrypt algorithm
|
|
/// </summary>
|
|
/// <param name="password">Password to encode</param>
|
|
/// <param name="pepper">Pepper to use</param>
|
|
/// <param name="iterationCount">scrypt algorithm iteration count</param>
|
|
/// <param name="blockSize">scrypt algorithm block size</param>
|
|
/// <param name="threadCount">scrypt algorithm thread count</param>
|
|
/// <returns>Encoded password</returns>
|
|
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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Compare the specified encoded password with the specified password with the specified pepper with the scrypt algorithm
|
|
/// </summary>
|
|
/// <param name="hashedPassword">Hashed password</param>
|
|
/// <param name="password">Password to compare</param>
|
|
/// <param name="pepper">Pepper to use</param>
|
|
/// <param name="iterationCount">scrypt algorithm iteration count</param>
|
|
/// <param name="blockSize">scrypt algorithm block size</param>
|
|
/// <param name="threadCount">scrypt algorithm thread count</param>
|
|
/// <returns></returns>
|
|
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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Generate a random password
|
|
/// </summary>
|
|
/// <param name="pepper">Pepper to use</param>
|
|
/// <param name="iterationCount">scrypt algorithm iteration count</param>
|
|
/// <param name="blockSize">scrypt algorithm block size</param>
|
|
/// <param name="threadCount">scrypt algorithm thread count</param>
|
|
/// <returns>Generated password</returns>
|
|
public static string Generate(string pepper = "", int iterationCount = DefaultIterationCount, int blockSize = DefaultBlockSize, int threadCount = DefaultThreadCount)
|
|
{
|
|
return Encode(GetUniqueKey(), pepper, iterationCount, blockSize, threadCount);
|
|
}
|
|
|
|
#endregion
|
|
|
|
/// <summary>
|
|
/// Get a random string of the specified length
|
|
/// </summary>
|
|
/// <param name="length">Number of characters</param>
|
|
/// <returns>Random string of the specified length</returns>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get a random number with the specified maximum number of digits
|
|
/// </summary>
|
|
/// <param name="maxDigits">Maximum number of digits</param>
|
|
/// <returns>Random number with the specified maximum number of digits</returns>
|
|
public static int GetRandomNumber(int maxDigits)
|
|
{
|
|
return s_random.Next((int)Math.Pow(10, maxDigits));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Calculate the MD5 sum of the specified byte array and remove potential dashes
|
|
/// </summary>
|
|
/// <param name="source">Source array</param>
|
|
/// <returns>Source array MD5 sum</returns>
|
|
public static string MD5Sum(byte[] source)
|
|
{
|
|
return BitConverter.ToString(new MD5CryptoServiceProvider().ComputeHash(source)).Replace("-", "").ToLower();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Calculate the MD5 sum of the specified string and remove potential dashes
|
|
/// </summary>
|
|
/// <param name="source">Source string</param>
|
|
/// <returns>Source string MD5 sum</returns>
|
|
public static string MD5Sum(string source)
|
|
{
|
|
return MD5Sum(Encoding.ASCII.GetBytes(source));
|
|
}
|
|
}
|
|
}
|