(refactor): jwk base64 encode core usage

This commit is contained in:
Maksym Sadovnychyy 2025-11-12 23:12:39 +01:00
parent 85d72b7b28
commit e5f400749e
2 changed files with 20 additions and 31 deletions

View File

@ -18,8 +18,6 @@ public interface IJwsService {
JwsMessage Encode(ACMEJwsHeader protectedHeader); JwsMessage Encode(ACMEJwsHeader protectedHeader);
JwsMessage Encode<TPayload>(TPayload payload, ACMEJwsHeader protectedHeader); JwsMessage Encode<TPayload>(TPayload payload, ACMEJwsHeader protectedHeader);
string GetKeyAuthorization(string token); string GetKeyAuthorization(string token);
string Base64UrlEncoded(string s);
string Base64UrlEncoded(byte[] arg);
} }
public class JwsService : IJwsService { public class JwsService : IJwsService {
@ -36,9 +34,9 @@ public class JwsService : IJwsService {
var mod = publicParameters.Modulus ?? throw new ArgumentNullException(nameof(publicParameters.Modulus)); var mod = publicParameters.Modulus ?? throw new ArgumentNullException(nameof(publicParameters.Modulus));
_jwk = new Jwk() { _jwk = new Jwk() {
KeyType = "RSA", KeyType = JwkKeyType.Rsa.Name,
RsaExponent = Base64UrlEncoded(exp), RsaExponent = Base64UrlUtility.Encode(exp),
RsaModulus = Base64UrlEncoded(mod), RsaModulus = Base64UrlUtility.Encode(mod),
}; };
} }
@ -51,7 +49,7 @@ public class JwsService : IJwsService {
public JwsMessage Encode<T>(T? payload, ACMEJwsHeader protectedHeader) { public JwsMessage Encode<T>(T? payload, ACMEJwsHeader protectedHeader) {
protectedHeader.Algorithm = "RS256"; protectedHeader.Algorithm = JwkAlgorithm.Rs256.Name;
if (_jwk.KeyId != null) { if (_jwk.KeyId != null) {
protectedHeader.KeyId = _jwk.KeyId; protectedHeader.KeyId = _jwk.KeyId;
} }
@ -61,17 +59,17 @@ public class JwsService : IJwsService {
var message = new JwsMessage { var message = new JwsMessage {
Payload = "", Payload = "",
Protected = Base64UrlEncoded(protectedHeader.ToJson()) Protected = Base64UrlUtility.Encode(protectedHeader.ToJson())
}; };
if (payload != null) { if (payload != null) {
if (payload is string stringPayload) if (payload is string stringPayload)
message.Payload = Base64UrlEncoded(stringPayload); message.Payload = Base64UrlUtility.Encode(stringPayload);
else else
message.Payload = Base64UrlEncoded(payload.ToJson()); message.Payload = Base64UrlUtility.Encode(payload.ToJson());
} }
message.Signature = Base64UrlEncoded( message.Signature = Base64UrlUtility.Encode(
_rsa.SignData(Encoding.ASCII.GetBytes($"{message.Protected}.{message.Payload}"), _rsa.SignData(Encoding.ASCII.GetBytes($"{message.Protected}.{message.Payload}"),
HashAlgorithmName.SHA256, HashAlgorithmName.SHA256,
RSASignaturePadding.Pkcs1)); RSASignaturePadding.Pkcs1));
@ -91,16 +89,6 @@ public class JwsService : IJwsService {
}; };
var json = "{\"e\":\"" + _jwk.RsaExponent + "\",\"kty\":\"RSA\",\"n\":\"" + _jwk.RsaModulus + "\"}"; var json = "{\"e\":\"" + _jwk.RsaExponent + "\",\"kty\":\"RSA\",\"n\":\"" + _jwk.RsaModulus + "\"}";
return Base64UrlEncoded(SHA256.HashData(Encoding.UTF8.GetBytes(json))); return Base64UrlUtility.Encode(SHA256.HashData(Encoding.UTF8.GetBytes(json)));
} }
public string Base64UrlEncoded(string s) =>
Base64UrlEncoded(Encoding.UTF8.GetBytes(s));
// https://tools.ietf.org/html/rfc4648#section-5
public string Base64UrlEncoded(byte[] bytes) =>
Convert.ToBase64String(bytes) // Regular base64 encoder
.Split('=').First() // Remove any trailing '='s
.Replace('+', '-') // 62nd char of encoding
.Replace('/', '_'); // 63rd char of encoding
} }

View File

@ -3,14 +3,8 @@
* https://datatracker.ietf.org/doc/html/draft-ietf-acme-acme-12 * https://datatracker.ietf.org/doc/html/draft-ietf-acme-acme-12
*/ */
using System.Text;
using System.Net.Http.Headers;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Logging;
using MaksIT.Results;
using MaksIT.Core.Extensions; using MaksIT.Core.Extensions;
using MaksIT.Core.Security.JWK;
using MaksIT.LetsEncrypt.Entities; using MaksIT.LetsEncrypt.Entities;
using MaksIT.LetsEncrypt.Entities.Jws; using MaksIT.LetsEncrypt.Entities.Jws;
using MaksIT.LetsEncrypt.Entities.LetsEncrypt; using MaksIT.LetsEncrypt.Entities.LetsEncrypt;
@ -18,6 +12,13 @@ using MaksIT.LetsEncrypt.Exceptions;
using MaksIT.LetsEncrypt.Models.Interfaces; using MaksIT.LetsEncrypt.Models.Interfaces;
using MaksIT.LetsEncrypt.Models.Requests; using MaksIT.LetsEncrypt.Models.Requests;
using MaksIT.LetsEncrypt.Models.Responses; using MaksIT.LetsEncrypt.Models.Responses;
using MaksIT.Results;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Logging;
using System.Net.Http.Headers;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
namespace MaksIT.LetsEncrypt.Services; namespace MaksIT.LetsEncrypt.Services;
@ -354,7 +355,7 @@ public class LetsEncryptService : ILetsEncryptService {
case "dns-01": case "dns-01":
using (var sha256 = SHA256.Create()) { using (var sha256 = SHA256.Create()) {
var dnsToken = state.JwsService != null var dnsToken = state.JwsService != null
? state.JwsService.Base64UrlEncoded(sha256.ComputeHash(Encoding.UTF8.GetBytes(keyToken ?? string.Empty))) ? Base64UrlUtility.Encode(sha256.ComputeHash(Encoding.UTF8.GetBytes(keyToken ?? string.Empty)))
: string.Empty; : string.Empty;
results[challengeResponse.Result?.Identifier?.Value ?? string.Empty] = dnsToken; results[challengeResponse.Result?.Identifier?.Value ?? string.Empty] = dnsToken;
@ -482,7 +483,7 @@ public class LetsEncryptService : ILetsEncryptService {
csr.CertificateExtensions.Add(san.Build()); csr.CertificateExtensions.Add(san.Build());
var letsEncryptOrder = new FinalizeRequest { var letsEncryptOrder = new FinalizeRequest {
Csr = state.JwsService!.Base64UrlEncoded(csr.CreateSigningRequest()) Csr = Base64UrlUtility.Encode(csr.CreateSigningRequest())
}; };
Uri? certificateUrl = default; Uri? certificateUrl = default;
@ -606,7 +607,7 @@ public class LetsEncryptService : ILetsEncryptService {
var derEncodedCert = certificate.Export(X509ContentType.Cert); var derEncodedCert = certificate.Export(X509ContentType.Cert);
var base64UrlEncodedCert = state.JwsService!.Base64UrlEncoded(derEncodedCert); var base64UrlEncodedCert = Base64UrlUtility.Encode(derEncodedCert);
var revokeRequest = new RevokeRequest { var revokeRequest = new RevokeRequest {
Certificate = base64UrlEncodedCert, Certificate = base64UrlEncodedCert,