190 lines
5.7 KiB
C#
190 lines
5.7 KiB
C#
using System.Net.Security;
|
|
using System.Security.Cryptography;
|
|
using System.Security.Cryptography.X509Certificates;
|
|
using System.Text.Json;
|
|
using Shouldly;
|
|
using ZB.MOM.NatsNet.Server;
|
|
|
|
namespace ZB.MOM.NatsNet.Server.Tests;
|
|
|
|
public sealed class NatsServerOcspTests : IDisposable
|
|
{
|
|
private readonly List<string> _tempDirs = [];
|
|
private readonly List<X509Certificate2> _certs = [];
|
|
|
|
[Fact]
|
|
public void SetupOCSPStapleStoreDir_WithStoreDir_CreatesDirectory()
|
|
{
|
|
var dir = MakeTempDir();
|
|
var server = NewServer(new ServerOptions
|
|
{
|
|
StoreDir = dir,
|
|
});
|
|
|
|
var err = server.SetupOCSPStapleStoreDir();
|
|
|
|
err.ShouldBeNull();
|
|
Directory.Exists(Path.Combine(dir, "ocsp")).ShouldBeTrue();
|
|
}
|
|
|
|
[Fact]
|
|
public void ConfigureOCSP_WithTlsConfig_ReturnsClientEntry()
|
|
{
|
|
var cert = CreateSelfSignedCertificate("CN=configure-ocsp");
|
|
var server = NewServer(new ServerOptions
|
|
{
|
|
TlsConfig = new SslServerAuthenticationOptions { ServerCertificate = cert },
|
|
});
|
|
|
|
var configs = server.ConfigureOCSP();
|
|
|
|
configs.Count.ShouldBe(1);
|
|
configs[0].Kind.ShouldBe("client");
|
|
}
|
|
|
|
[Fact]
|
|
public void NewOCSPMonitor_OcspNever_ReturnsNoMonitor()
|
|
{
|
|
var cert = CreateSelfSignedCertificate("CN=monitor-never");
|
|
var server = NewServer(new ServerOptions
|
|
{
|
|
StoreDir = MakeTempDir(),
|
|
OcspConfig = new OcspConfig { Mode = ZB.MOM.NatsNet.Server.OcspMode.Never },
|
|
});
|
|
|
|
var config = new OcspTlsConfig
|
|
{
|
|
Kind = "client",
|
|
TlsConfig = new SslServerAuthenticationOptions { ServerCertificate = cert },
|
|
TlsOptions = null,
|
|
Apply = _ => { },
|
|
};
|
|
|
|
var (_, monitor, err) = server.NewOCSPMonitor(config);
|
|
|
|
err.ShouldBeNull();
|
|
monitor.ShouldBeNull();
|
|
}
|
|
|
|
[Fact]
|
|
public void EnableOCSP_WithAlwaysMode_AddsMonitor()
|
|
{
|
|
var cert = CreateSelfSignedCertificate("CN=enable-ocsp");
|
|
var storeDir = MakeTempDir();
|
|
WriteLocalOcspStatus(storeDir, cert);
|
|
var server = NewServer(new ServerOptions
|
|
{
|
|
StoreDir = storeDir,
|
|
OcspConfig = new OcspConfig
|
|
{
|
|
Mode = ZB.MOM.NatsNet.Server.OcspMode.Always,
|
|
OverrideUrls = ["https://ocsp.example.test"],
|
|
},
|
|
TlsConfig = new SslServerAuthenticationOptions { ServerCertificate = cert },
|
|
});
|
|
|
|
var err = server.EnableOCSP();
|
|
|
|
err.ShouldBeNull();
|
|
server.GetOcspMonitors().Length.ShouldBe(1);
|
|
}
|
|
|
|
[Fact]
|
|
public void StartOCSPMonitoring_NoMonitors_DoesNotThrow()
|
|
{
|
|
var server = NewServer(new ServerOptions());
|
|
|
|
Should.NotThrow(() => server.StartOCSPMonitoring());
|
|
}
|
|
|
|
[Fact]
|
|
public void ReloadOCSP_WithConfiguredTls_ReplacesMonitors()
|
|
{
|
|
var cert = CreateSelfSignedCertificate("CN=reload-ocsp");
|
|
var storeDir = MakeTempDir();
|
|
WriteLocalOcspStatus(storeDir, cert);
|
|
var server = NewServer(new ServerOptions
|
|
{
|
|
StoreDir = storeDir,
|
|
OcspConfig = new OcspConfig
|
|
{
|
|
Mode = ZB.MOM.NatsNet.Server.OcspMode.Always,
|
|
OverrideUrls = ["https://ocsp.example.test"],
|
|
},
|
|
TlsConfig = new SslServerAuthenticationOptions { ServerCertificate = cert },
|
|
});
|
|
server.EnableOCSP().ShouldBeNull();
|
|
server.GetOcspMonitors().Length.ShouldBe(1);
|
|
|
|
var err = server.ReloadOCSP();
|
|
|
|
err.ShouldBeNull();
|
|
server.GetOcspMonitors().Length.ShouldBe(1);
|
|
}
|
|
|
|
[Fact]
|
|
public void HasOCSPStatusRequest_CertificateWithoutExtension_ReturnsFalse()
|
|
{
|
|
var cert = CreateSelfSignedCertificate("CN=no-status-request");
|
|
|
|
OcspHandler.HasOCSPStatusRequest(cert).ShouldBeFalse();
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
foreach (var cert in _certs)
|
|
{
|
|
cert.Dispose();
|
|
}
|
|
|
|
foreach (var dir in _tempDirs)
|
|
{
|
|
try { Directory.Delete(dir, recursive: true); } catch { }
|
|
}
|
|
}
|
|
|
|
private NatsServer NewServer(ServerOptions options)
|
|
{
|
|
var (server, err) = NatsServer.NewServer(options);
|
|
err.ShouldBeNull();
|
|
return server!;
|
|
}
|
|
|
|
private X509Certificate2 CreateSelfSignedCertificate(string subject)
|
|
{
|
|
var req = new CertificateRequest(
|
|
subject,
|
|
RSA.Create(2048),
|
|
HashAlgorithmName.SHA256,
|
|
RSASignaturePadding.Pkcs1);
|
|
req.CertificateExtensions.Add(new X509BasicConstraintsExtension(true, false, 0, true));
|
|
req.CertificateExtensions.Add(new X509SubjectKeyIdentifierExtension(req.PublicKey, false));
|
|
|
|
var cert = req.CreateSelfSigned(DateTimeOffset.UtcNow.AddDays(-1), DateTimeOffset.UtcNow.AddDays(90));
|
|
_certs.Add(cert);
|
|
return cert;
|
|
}
|
|
|
|
private string MakeTempDir()
|
|
{
|
|
var path = Path.Combine(Path.GetTempPath(), "nats-ocsp-" + Path.GetRandomFileName());
|
|
Directory.CreateDirectory(path);
|
|
_tempDirs.Add(path);
|
|
return path;
|
|
}
|
|
|
|
private static void WriteLocalOcspStatus(string storeDir, X509Certificate2 cert)
|
|
{
|
|
var key = Convert.ToHexString(SHA256.HashData(cert.RawData)).ToLowerInvariant();
|
|
var ocspDir = Path.Combine(storeDir, "ocsp");
|
|
Directory.CreateDirectory(ocspDir);
|
|
var payload = JsonSerializer.SerializeToUtf8Bytes(new
|
|
{
|
|
Status = 0,
|
|
ThisUpdate = DateTime.UtcNow.AddMinutes(-5),
|
|
NextUpdate = DateTime.UtcNow.AddHours(6),
|
|
});
|
|
File.WriteAllBytes(Path.Combine(ocspDir, key), payload);
|
|
}
|
|
}
|