using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using GB5Shared.Telemetry; namespace GB5Shared.DigitalSignature { // Extracted from FrameworkSL/Endpoints/DigitalSignaturePFX/GenerateSignature.cs so BLL callers // (e.g. TMS certificate signing) don't have to call another module's SL endpoint over HTTP — // SL must never call SL. Same RSA/SHA-256/PKCS1 mechanism as before: no behavior change. // // Reads the signing certificate from the Windows certificate store by thumbprint (no password — // the private key is protected by OS-level store permissions, not a PFX password). If signing // certificates are later distributed as .pfx files instead of being pre-imported into the store, // this is the place to switch to Vault-backed .pfx + password loading. public class DigitalSignatureService : IDigitalSignatureService { public async Task SignDataAsync(string thumbprint, byte[] data, CancellationToken ct) { try { using var cert = await GetSigningCertificateAsync(thumbprint, ct).ConfigureAwait(false); using var rsa = cert.GetRSAPrivateKey(); if (rsa == null) throw new InvalidOperationException("Private key not accessible for the signing certificate."); return rsa.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); } catch (Exception ex) { GB5Trace.MarkFailed("digital-signature-sign-failed", ex); throw; } } public async Task SignHashAsync(string thumbprint, byte[] hash, CancellationToken ct) { try { using var cert = await GetSigningCertificateAsync(thumbprint, ct).ConfigureAwait(false); using var rsa = cert.GetRSAPrivateKey(); if (rsa == null) throw new InvalidOperationException("Private key not accessible for the signing certificate."); return rsa.SignHash(hash, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); } catch (Exception ex) { GB5Trace.MarkFailed("digital-signature-signhash-failed", ex); throw; } } public Task GetSigningCertificateAsync(string thumbprint, CancellationToken ct) { using var store = new X509Store(StoreName.My, StoreLocation.CurrentUser); store.Open(OpenFlags.ReadOnly); var certs = store.Certificates.Find(X509FindType.FindByThumbprint, thumbprint, false); if (certs.Count == 0) throw new InvalidOperationException($"Certificate not found for thumbprint {thumbprint}."); return Task.FromResult(certs[0]); } } }