using System.Security.Cryptography; using Microsoft.AspNetCore.Identity; using SameBeat.Application.Interfejsy; using SameBeat.Domain.Encje; namespace SameBeat.Infrastructure.Autoryzacja; /// /// Implementacja oparta na tym samym Microsoft.AspNetCore.Identity.PasswordHasher (PBKDF2) co /// UslugaHaszowaniaHasel -- tylko sparametryzowana encja to UczestnikOczekujacy zamiast Uzytkownik. /// Sekret jawny generujemy tu, nie w domenie -- RandomNumberGenerator to szczegol implementacyjny /// infrastruktury, tak samo jak PasswordHasher. /// public class UslugaHaszowaniaSekretowSesji : IUslugaHaszowaniaSekretowSesji { // Min. 32 bajty (256 bitow) losowosci -- wymog bezpieczenstwa dla sekretu sesji uczestnika. private const int DlugoscSekretuBajty = 32; private readonly PasswordHasher _hasher = new(); public string WygenerujSekret() { var bajty = RandomNumberGenerator.GetBytes(DlugoscSekretuBajty); return ZakodujBase64Url(bajty); } public string Haszuj(UczestnikOczekujacy uczestnik, string sekretJawny) => _hasher.HashPassword(uczestnik, sekretJawny); public WynikWeryfikacjiHasla Weryfikuj(UczestnikOczekujacy uczestnik, string sekretHash, string sekretJawny) { var wynik = _hasher.VerifyHashedPassword(uczestnik, sekretHash, sekretJawny); return wynik switch { PasswordVerificationResult.Failed => WynikWeryfikacjiHasla.Niepoprawne, PasswordVerificationResult.Success => WynikWeryfikacjiHasla.Poprawne, PasswordVerificationResult.SuccessRehashNeeded => WynikWeryfikacjiHasla.PoprawneWymagaRehaszowania, _ => WynikWeryfikacjiHasla.Niepoprawne }; } // Base64Url (RFC 4648 ยง5) recznie, zeby nie wciagac dodatkowej zaleznosci -- standardowy Base64 // z '+'/'/' zamienionymi na '-'/'_' i bez wyrownania '='. private static string ZakodujBase64Url(byte[] bajty) => Convert.ToBase64String(bajty).TrimEnd('=').Replace('+', '-').Replace('/', '_'); }