38 lines
1.8 KiB
C#
38 lines
1.8 KiB
C#
using System.Security.Cryptography;
|
|
using System.Text;
|
|
|
|
namespace MijnBesluit.Application.Security;
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
public static class TokenGenerator
|
|
{
|
|
/// <summary>Nieuw token als URL-veilige base64-tekst.</summary>
|
|
public static string Create(int byteLength = 32) =>
|
|
Convert.ToBase64String(RandomNumberGenerator.GetBytes(byteLength))
|
|
.Replace('+', '-').Replace('/', '_').TrimEnd('=');
|
|
|
|
/// <summary>SHA-256 van het token, zoals het in de database wordt bewaard.</summary>
|
|
public static byte[] HashToken(string token) =>
|
|
SHA256.HashData(System.Text.Encoding.UTF8.GetBytes(token));
|
|
|
|
/// <summary>Vergelijking in constante tijd van een aangeboden token met een opgeslagen hash.</summary>
|
|
public static bool Matches(string token, byte[] storedHash) =>
|
|
storedHash.Length == 32 && CryptographicOperations.FixedTimeEquals(HashToken(token), storedHash);
|
|
|
|
/// <summary>
|
|
/// Leidt uit het token zelf een 256-bit sleutel af, los van <see cref="HashToken"/>.
|
|
///
|
|
/// 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. <paramref name="doel"/>
|
|
/// zorgt er bovendien voor dat twee sleutels voor hetzelfde token, voor iets anders
|
|
/// gebruikt, elkaar niet gelijk zijn.
|
|
/// </summary>
|
|
public static byte[] DeriveKey(string token, string doel) =>
|
|
HMACSHA256.HashData(Encoding.UTF8.GetBytes(token), Encoding.UTF8.GetBytes(doel));
|
|
}
|