using System.Security.Cryptography; using System.Text; using System.Diagnostics.CodeAnalysis; using MaksIT.Core.Extensions; namespace MaksIT.Core.Security.JWK; public static class JwkThumbprintUtility { /// /// Returns the key authorization string for ACME challenges. /// public static bool TryGetKeyAuthorization( Jwk jwk, string token, [NotNullWhen(true)] out string? keyAuthorization, [NotNullWhen(false)] out string? errorMessage ) { keyAuthorization = null; errorMessage = null; if (!TryGetSha256Thumbprint(jwk, out var thumbprint, out var thumbprintError)) { errorMessage = $"Failed to compute thumbprint: {thumbprintError}"; return false; } keyAuthorization = $"{token}.{thumbprint}"; return true; } /// /// Computes the RFC7638 JWK SHA-256 thumbprint (Base64Url encoded). /// For thumbprint calculation, always build the JSON string manually or use OrderedJwk for correct property order. /// public static bool TryGetSha256Thumbprint( Jwk jwk, [NotNullWhen(true)] out string? thumbprint, [NotNullWhen(false)] out string? errorMessage ) { thumbprint = null; errorMessage = null; try { if (jwk.RsaExponent == null || jwk.RsaModulus == null) throw new ArgumentException("RSA exponent or modulus is null."); var thumbprintObj = new OrderedJwk { E = jwk.RsaExponent, Kty = JwkKeyType.Rsa.Name, N = jwk.RsaModulus }; var json = thumbprintObj.ToJson(); thumbprint = Base64UrlUtility.Encode(SHA256.HashData(Encoding.UTF8.GetBytes(json))); return true; } catch (Exception ex) { errorMessage = ex.Message; return false; } } }