67 lines
2.2 KiB
C#
67 lines
2.2 KiB
C#
using System;
|
|
using System.Security.Cryptography;
|
|
using System.Text;
|
|
|
|
namespace StellaOps.Auth.Security.Dpop;
|
|
|
|
internal static class DpopNonceUtilities
|
|
{
|
|
private static readonly char[] Base64Padding = { '=' };
|
|
|
|
internal static string GenerateNonce()
|
|
{
|
|
Span<byte> buffer = stackalloc byte[32];
|
|
RandomNumberGenerator.Fill(buffer);
|
|
|
|
return Convert.ToBase64String(buffer)
|
|
.TrimEnd(Base64Padding)
|
|
.Replace('+', '-')
|
|
.Replace('/', '_');
|
|
}
|
|
|
|
internal static byte[] ComputeNonceHash(string nonce)
|
|
{
|
|
ArgumentException.ThrowIfNullOrWhiteSpace(nonce);
|
|
var bytes = Encoding.UTF8.GetBytes(nonce);
|
|
return SHA256.HashData(bytes);
|
|
}
|
|
|
|
internal static string EncodeHash(ReadOnlySpan<byte> hash)
|
|
=> Convert.ToHexString(hash);
|
|
|
|
internal static string ComputeStorageKey(string audience, string clientId, string keyThumbprint)
|
|
{
|
|
ArgumentException.ThrowIfNullOrWhiteSpace(audience);
|
|
ArgumentException.ThrowIfNullOrWhiteSpace(clientId);
|
|
ArgumentException.ThrowIfNullOrWhiteSpace(keyThumbprint);
|
|
|
|
return string.Create(
|
|
"dpop-nonce:".Length + audience.Length + clientId.Length + keyThumbprint.Length + 2,
|
|
(audience.Trim(), clientId.Trim(), keyThumbprint.Trim()),
|
|
static (span, parts) =>
|
|
{
|
|
var index = 0;
|
|
const string Prefix = "dpop-nonce:";
|
|
Prefix.CopyTo(span);
|
|
index += Prefix.Length;
|
|
|
|
index = Append(span, index, parts.Item1);
|
|
span[index++] = ':';
|
|
index = Append(span, index, parts.Item2);
|
|
span[index++] = ':';
|
|
_ = Append(span, index, parts.Item3);
|
|
});
|
|
|
|
static int Append(Span<char> span, int index, string value)
|
|
{
|
|
if (value.Length == 0)
|
|
{
|
|
throw new ArgumentException("Value must not be empty after trimming.");
|
|
}
|
|
|
|
value.AsSpan().CopyTo(span[index..]);
|
|
return index + value.Length;
|
|
}
|
|
}
|
|
}
|