feat: add OCSP peer verification and stapling support
Wire OcspPeerVerify into the client-cert validation callback in TlsHelper so revocation is checked online when the flag is set. Add TlsHelper.BuildCertificateContext to build an SslStreamCertificateContext with offline:false, enabling the runtime to fetch and staple OCSP responses during the TLS handshake. NatsServer applies the context at startup when OcspConfig.Mode is not Never. Ten unit tests cover the config defaults, mode ordinals, and the null-return invariants of BuildCertificateContext.
This commit is contained in:
@@ -33,6 +33,10 @@ public static class TlsHelper
|
||||
|
||||
if (opts.TlsVerify && opts.TlsCaCert != null)
|
||||
{
|
||||
var revocationMode = opts.OcspPeerVerify
|
||||
? X509RevocationMode.Online
|
||||
: X509RevocationMode.NoCheck;
|
||||
|
||||
var caCerts = LoadCaCertificates(opts.TlsCaCert);
|
||||
authOpts.RemoteCertificateValidationCallback = (_, cert, chain, errors) =>
|
||||
{
|
||||
@@ -41,7 +45,19 @@ public static class TlsHelper
|
||||
chain2.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust;
|
||||
foreach (var ca in caCerts)
|
||||
chain2.ChainPolicy.CustomTrustStore.Add(ca);
|
||||
chain2.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
|
||||
chain2.ChainPolicy.RevocationMode = revocationMode;
|
||||
var cert2 = cert as X509Certificate2 ?? X509CertificateLoader.LoadCertificate(cert.GetRawCertData());
|
||||
return chain2.Build(cert2);
|
||||
};
|
||||
}
|
||||
else if (opts.OcspPeerVerify)
|
||||
{
|
||||
// No custom CA — still enable online revocation checking against the system store
|
||||
authOpts.RemoteCertificateValidationCallback = (_, cert, chain, errors) =>
|
||||
{
|
||||
if (cert == null) return false;
|
||||
using var chain2 = new X509Chain();
|
||||
chain2.ChainPolicy.RevocationMode = X509RevocationMode.Online;
|
||||
var cert2 = cert as X509Certificate2 ?? X509CertificateLoader.LoadCertificate(cert.GetRawCertData());
|
||||
return chain2.Build(cert2);
|
||||
};
|
||||
@@ -50,6 +66,25 @@ public static class TlsHelper
|
||||
return authOpts;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds an <see cref="SslStreamCertificateContext"/> for OCSP stapling.
|
||||
/// Returns null when TLS is not configured or OCSP mode is Never.
|
||||
/// When <paramref name="offline"/> is false the runtime will contact the
|
||||
/// certificate's OCSP responder to obtain a fresh stapled response.
|
||||
/// </summary>
|
||||
public static SslStreamCertificateContext? BuildCertificateContext(NatsOptions opts, bool offline = false)
|
||||
{
|
||||
if (!opts.HasTls) return null;
|
||||
if (opts.OcspConfig is null || opts.OcspConfig.Mode == OcspMode.Never) return null;
|
||||
|
||||
var cert = LoadCertificate(opts.TlsCert!, opts.TlsKey);
|
||||
var chain = new X509Certificate2Collection();
|
||||
if (!string.IsNullOrEmpty(opts.TlsCaCert))
|
||||
chain.ImportFromPemFile(opts.TlsCaCert);
|
||||
|
||||
return SslStreamCertificateContext.Create(cert, chain, offline: offline);
|
||||
}
|
||||
|
||||
public static string GetCertificateHash(X509Certificate2 cert)
|
||||
{
|
||||
var spki = cert.PublicKey.ExportSubjectPublicKeyInfo();
|
||||
|
||||
Reference in New Issue
Block a user