Files
mxaccessgw/src/ZB.MOM.WW.MxGateway.Tests/Gateway/GatewayTlsBootstrapTests.cs
T

73 lines
3.2 KiB
C#

using System.Net.Security;
using System.Net.Sockets;
using System.Security.Cryptography.X509Certificates;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.Hosting.Server.Features;
using Microsoft.Extensions.DependencyInjection;
using ZB.MOM.WW.MxGateway.Server;
namespace ZB.MOM.WW.MxGateway.Tests.Gateway;
public sealed class GatewayTlsBootstrapTests
{
/// <summary>
/// Verifies that when a Kestrel HTTPS endpoint is configured without its own certificate,
/// the gateway supplies the generated self-signed certificate as the Kestrel HTTPS default.
/// The host must start and bind, and the certificate served on the TLS handshake must be the
/// gateway's generated cert (subject <c>CN=MxAccessGateway Self-Signed</c>) — not an ambient
/// ASP.NET Core development certificate. On a host with no dev cert installed, starting a
/// cert-less HTTPS endpoint throws "No server certificate was specified"; on a host that has a
/// trusted dev cert, Kestrel would otherwise serve that dev cert (<c>CN=localhost</c>), so the
/// subject assertion is what makes this test fail without the wiring on either kind of host.
/// </summary>
[Fact]
public async Task Host_ServesGeneratedCertificate_WhenHttpsEndpointHasNoCertificate()
{
string certDir = Directory.CreateTempSubdirectory().FullName;
try
{
Environment.SetEnvironmentVariable("Kestrel__Endpoints__Test__Url", "https://127.0.0.1:0");
Environment.SetEnvironmentVariable(
"MxGateway__Tls__SelfSignedCertPath", Path.Combine(certDir, "gw.pfx"));
WebApplication app = GatewayApplication.Build([]);
await app.StartAsync();
try
{
string servedSubject = await ReadServedCertificateSubjectAsync(app);
Assert.Contains("MxAccessGateway Self-Signed", servedSubject, StringComparison.Ordinal);
}
finally
{
await app.StopAsync();
await app.DisposeAsync();
}
}
finally
{
Environment.SetEnvironmentVariable("Kestrel__Endpoints__Test__Url", null);
Environment.SetEnvironmentVariable("MxGateway__Tls__SelfSignedCertPath", null);
Directory.Delete(certDir, recursive: true);
}
}
private static async Task<string> ReadServedCertificateSubjectAsync(WebApplication app)
{
IServerAddressesFeature addresses =
app.Services.GetRequiredService<IServer>().Features.Get<IServerAddressesFeature>()
?? throw new InvalidOperationException("Server addresses feature was not available.");
Uri endpoint = new(addresses.Addresses.First());
using TcpClient client = new();
await client.ConnectAsync(endpoint.Host, endpoint.Port);
using SslStream ssl = new(
client.GetStream(),
leaveInnerStreamOpen: false,
userCertificateValidationCallback: (_, _, _, _) => true);
await ssl.AuthenticateAsClientAsync("127.0.0.1");
return ssl.RemoteCertificate?.Subject ?? "(none)";
}
}