using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; namespace Csob.Credentials; /// /// Resolves the ČSOB credentials and PSU context for the current request, exclusively from HTTP /// headers (this service stores no secrets). Missing required headers produce a 401; a malformed /// certificate or wrong passphrase produces a 400 (via ). /// public sealed class RequestCredentialsProvider { private readonly IHttpContextAccessor _httpContextAccessor; public RequestCredentialsProvider(IHttpContextAccessor httpContextAccessor) { _httpContextAccessor = httpContextAccessor; } /// Reads a single request header, returning null when absent or blank. public string? Header(string name) { var headers = _httpContextAccessor.HttpContext?.Request.Headers; if (headers is not null && headers.TryGetValue(name, out var value)) { var raw = value.ToString(); if (!string.IsNullOrWhiteSpace(raw)) { return raw; } } return null; } /// /// Builds the eIDAS client certificate from the Base64 PFX header (+ optional passphrase). /// Returns null when no certificate header is present. /// /// The header is not valid Base64 or the PFX/passphrase is invalid. public X509Certificate2? TryBuildCertificate() { var base64 = Header(CredentialConstants.CertificateHeader); if (base64 is null) { return null; } byte[] raw; try { raw = Convert.FromBase64String(base64.Trim()); } catch (FormatException ex) { throw new CryptographicException($"{CredentialConstants.CertificateHeader} is not valid Base64.", ex); } var password = Header(CredentialConstants.CertificatePasswordHeader); // EphemeralKeySet keeps the private key in memory only — never written to the machine key store / disk. return new X509Certificate2(raw, password, X509KeyStorageFlags.EphemeralKeySet); } /// /// Resolves the full credential set required for an AISP/PISP/consent call. Throws /// if any required header is absent. /// public CsobCredentials Resolve() { var accessToken = Header(CredentialConstants.AccessTokenHeader); var apiKey = Header(CredentialConstants.ApiKeyHeader); var tppName = Header(CredentialConstants.TppNameHeader); var certificate = TryBuildCertificate(); var missing = new List(); if (string.IsNullOrWhiteSpace(accessToken)) missing.Add(CredentialConstants.AccessTokenHeader); if (string.IsNullOrWhiteSpace(apiKey)) missing.Add(CredentialConstants.ApiKeyHeader); if (string.IsNullOrWhiteSpace(tppName)) missing.Add(CredentialConstants.TppNameHeader); if (certificate is null) missing.Add(CredentialConstants.CertificateHeader); if (missing.Count > 0) { certificate?.Dispose(); throw new MissingCredentialsException(missing); } return new CsobCredentials { AccessToken = accessToken!, ApiKey = apiKey!, TppName = tppName!, Certificate = certificate, UserInvolved = string.Equals(Header(CredentialConstants.UserInvolvedHeader), "true", StringComparison.OrdinalIgnoreCase), UserIpAddress = Header(CredentialConstants.UserIpAddressHeader), }; } }