<PackageReference Include="BouncyCastle.Cryptography" Version="2.3.0" />

OpenBsdBCrypt

public class OpenBsdBCrypt
using Org.BouncyCastle.Utilities; using System; using System.Collections.Generic; using System.IO; using System.Text; namespace Org.BouncyCastle.Crypto.Generators { public class OpenBsdBCrypt { private static readonly byte[] EncodingTable; private static readonly byte[] DecodingTable; private static readonly string DefaultVersion; private static readonly HashSet<string> AllowedVersions; static OpenBsdBCrypt() { EncodingTable = new byte[64] { 46, 47, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57 }; DecodingTable = new byte[128]; DefaultVersion = "2y"; AllowedVersions = new HashSet<string>(); AllowedVersions.Add("2a"); AllowedVersions.Add("2y"); AllowedVersions.Add("2b"); for (int i = 0; i < DecodingTable.Length; i++) { DecodingTable[i] = byte.MaxValue; } for (int j = 0; j < EncodingTable.Length; j++) { DecodingTable[EncodingTable[j]] = (byte)j; } } private static string CreateBcryptString(string version, byte[] password, byte[] salt, int cost) { if (!AllowedVersions.Contains(version)) throw new ArgumentException("Version " + version + " is not accepted by this implementation.", "version"); StringBuilder stringBuilder = new StringBuilder(60); stringBuilder.Append('$'); stringBuilder.Append(version); stringBuilder.Append('$'); stringBuilder.Append((cost < 10) ? ("0" + cost.ToString()) : cost.ToString()); stringBuilder.Append('$'); stringBuilder.Append(EncodeData(salt)); byte[] data = BCrypt.Generate(password, salt, cost); stringBuilder.Append(EncodeData(data)); return stringBuilder.ToString(); } public static string Generate(char[] password, byte[] salt, int cost) { return Generate(DefaultVersion, password, salt, cost); } public static string Generate(string version, char[] password, byte[] salt, int cost) { if (!AllowedVersions.Contains(version)) throw new ArgumentException("Version " + version + " is not accepted by this implementation.", "version"); if (password == null) throw new ArgumentNullException("password"); if (salt == null) throw new ArgumentNullException("salt"); if (salt.Length != 16) throw new DataLengthException("16 byte salt required: " + salt.Length.ToString()); if (cost < 4 || cost > 31) throw new ArgumentException("Invalid cost factor.", "cost"); byte[] array = Strings.ToUtf8ByteArray(password); byte[] array2 = new byte[(array.Length >= 72) ? 72 : (array.Length + 1)]; int length = System.Math.Min(array.Length, array2.Length); Array.Copy(array, 0, array2, 0, length); Array.Clear(array, 0, array.Length); string result = CreateBcryptString(version, array2, salt, cost); Array.Clear(array2, 0, array2.Length); return result; } public static bool CheckPassword(string bcryptString, char[] password) { if (bcryptString.Length != 60) throw new DataLengthException("Bcrypt String length: " + bcryptString.Length.ToString() + ", 60 required."); if (bcryptString[0] != '$' || bcryptString[3] != '$' || bcryptString[6] != '$') throw new ArgumentException("Invalid Bcrypt String format.", "bcryptString"); string text = bcryptString.Substring(1, 2); if (!AllowedVersions.Contains(text)) throw new ArgumentException("Bcrypt version '" + text + "' is not supported by this implementation", "bcryptString"); int num; try { num = int.Parse(bcryptString.Substring(4, 2)); } catch (Exception innerException) { throw new ArgumentException("Invalid cost factor: " + bcryptString.Substring(4, 2), "bcryptString", innerException); } if (num < 4 || num > 31) throw new ArgumentException("Invalid cost factor: " + num.ToString() + ", 4 < cost < 31 expected."); if (password == null) throw new ArgumentNullException("Missing password."); int num2 = bcryptString.LastIndexOf('$') + 1; int num3 = bcryptString.Length - 31; byte[] salt = DecodeSaltString(bcryptString.Substring(num2, num3 - num2)); string value = Generate(text, password, salt, num); return bcryptString.Equals(value); } private static string EncodeData(byte[] data) { if (data.Length != 24 && data.Length != 16) throw new DataLengthException("Invalid length: " + data.Length.ToString() + ", 24 for key or 16 for salt expected"); bool flag = false; if (data.Length == 16) { flag = true; byte[] array = new byte[18]; Array.Copy(data, 0, array, 0, data.Length); data = array; } else data[data.Length - 1] = 0; MemoryStream memoryStream = new MemoryStream(); int num = data.Length; for (int i = 0; i < num; i += 3) { uint num2 = data[i]; uint num3 = data[i + 1]; uint num4 = data[i + 2]; memoryStream.WriteByte(EncodingTable[(num2 >> 2) & 63]); memoryStream.WriteByte(EncodingTable[((num2 << 4) | (num3 >> 4)) & 63]); memoryStream.WriteByte(EncodingTable[((num3 << 2) | (num4 >> 6)) & 63]); memoryStream.WriteByte(EncodingTable[num4 & 63]); } string text = Strings.FromByteArray(memoryStream.ToArray()); int length = flag ? 22 : (text.Length - 1); return text.Substring(0, length); } private static byte[] DecodeSaltString(string saltString) { char[] array = saltString.ToCharArray(); MemoryStream memoryStream = new MemoryStream(16); if (array.Length != 22) throw new DataLengthException("Invalid base64 salt length: " + array.Length.ToString() + " , 22 required."); for (int i = 0; i < array.Length; i++) { int num = array[i]; if (num > 122 || num < 46 || (num > 57 && num < 65)) throw new ArgumentException("Salt string contains invalid character: " + num.ToString(), "saltString"); } char[] array2 = new char[24]; Array.Copy(array, 0, array2, 0, array.Length); array = array2; int num2 = array.Length; for (int j = 0; j < num2; j += 4) { byte b = DecodingTable[array[j]]; byte b2 = DecodingTable[array[j + 1]]; byte b3 = DecodingTable[array[j + 2]]; byte b4 = DecodingTable[array[j + 3]]; memoryStream.WriteByte((byte)((b << 2) | (b2 >> 4))); memoryStream.WriteByte((byte)((b2 << 4) | (b3 >> 2))); memoryStream.WriteByte((byte)((b3 << 6) | b4)); } byte[] sourceArray = memoryStream.ToArray(); byte[] array3 = new byte[16]; Array.Copy(sourceArray, 0, array3, 0, array3.Length); return array3; } } }