using System; using System.Linq; using System.Net.Http; using System.Security.Cryptography; using System.Text; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Options; namespace gehGassi.Pwned { /// public class PwnedPasswordService : IPwnedPasswordService { /// public HttpClient Client { get; } private readonly string _getRange = "range"; private readonly PwnedOptions _options; /// /// Constructor for . /// /// instance passed by DI injection /// instance passed by DI injection. public PwnedPasswordService( HttpClient httpClient, IOptions options) { Client = httpClient ?? throw new ArgumentNullException(nameof(httpClient)); _options = options.Value; var userAgent = $"{nameof(PwnedPasswordService)}-kdcllc"; if (!string.IsNullOrEmpty(_options?.UserAgent)) { userAgent = _options?.UserAgent; } httpClient.DefaultRequestHeaders.Add("Accept", "application/json"); httpClient.DefaultRequestHeaders.Add("User-Agent", userAgent); } /// public async Task<(bool pwned, long count)> IsPasswordPwnedAsync( string password, CancellationToken token = default) { // Compute the SHA1 hash of the string var sha1 = SHA1.Create(); var byteString = Encoding.UTF8.GetBytes(password); var hashBytes = sha1.ComputeHash(byteString); var hashString = ""; var sb = new StringBuilder(); foreach (var b in hashBytes) { sb.Append(b.ToString("X2")); } hashString = sb.ToString(); // Break the SHA1 into two pieces: // 1) the first five characters of the hash // 2) the rest of the hash var hashFirstFive = hashString.Substring(0, 5); var hashLeftover = hashString.Substring(5, hashString.Length - 5); //GET https://api.pwnedpasswords.com/range/{first 5 hash chars} var url = $"{_options.PasswordsApiUrl}{_getRange}/{hashFirstFive}"; var request = new HttpRequestMessage(HttpMethod.Get, url); var response = await Client.SendAsync(request, token).ConfigureAwait(false); response.EnsureSuccessStatusCode(); var result = await response.Content.ReadAsStringAsync().ConfigureAwait(false); var responseContainsHash = result.Contains(hashLeftover); long breachCounts = 0; if (responseContainsHash) { var lines = result.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None).ToList(); foreach (var item in lines) { if (item.Contains(hashLeftover)) { var records = item.Split(':'); long.TryParse(records[1], out var x); breachCounts += x; } } } return (responseContainsHash, breachCounts); } } }