using System.Security.Cryptography; using System.Text; namespace MijnBesluit.Application.Security; /// /// Genereert uitnodigings- en sessietokens. Alleen de hash gaat de database in; /// het token zelf bestaat maar één keer, in de e-mail of bij de client. /// public static class TokenGenerator { /// Nieuw token als URL-veilige base64-tekst. public static string Create(int byteLength = 32) => Convert.ToBase64String(RandomNumberGenerator.GetBytes(byteLength)) .Replace('+', '-').Replace('/', '_').TrimEnd('='); /// SHA-256 van het token, zoals het in de database wordt bewaard. public static byte[] HashToken(string token) => SHA256.HashData(System.Text.Encoding.UTF8.GetBytes(token)); /// Vergelijking in constante tijd van een aangeboden token met een opgeslagen hash. public static bool Matches(string token, byte[] storedHash) => storedHash.Length == 32 && CryptographicOperations.FixedTimeEquals(HashToken(token), storedHash); /// /// Leidt uit het token zelf een 256-bit sleutel af, los van . /// /// Dat "los van" is de reden dat dit een aparte methode is en geen hergebruik van de hash: /// die hash staat in de database om het token te herkennen. Was de afgeleide sleutel /// daaraan gelijk, dan lag de sleutel waarmee een bericht is versleuteld gewoon naast de /// hash in diezelfde database - dat maakt de versleuteling zinloos. /// zorgt er bovendien voor dat twee sleutels voor hetzelfde token, voor iets anders /// gebruikt, elkaar niet gelijk zijn. /// public static byte[] DeriveKey(string token, string doel) => HMACSHA256.HashData(Encoding.UTF8.GetBytes(token), Encoding.UTF8.GetBytes(doel)); }