Files
lmxopcua/tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests/Sdk2CertificateLifecycleTests.cs
T

65 lines
3.6 KiB
C#

using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using Microsoft.Extensions.Logging.Abstractions;
using Opc.Ua;
using Opc.Ua.Identity;
using Shouldly;
using Xunit;
namespace ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests;
public sealed class Sdk2CertificateLifecycleTests
{
[Fact]
public async Task Restart_reuses_the_application_certificate_and_its_private_key()
{
var pki = Path.Combine(Path.GetTempPath(), $"otua-sdk2-cert-{Guid.NewGuid():N}");
try
{
string? thumbprint = null;
for (var restart = 0; restart < 2; restart++)
{
var started = await OpcUaHostStart.StartAsync(port => new OpcUaApplicationHost(new OpcUaApplicationHostOptions
{
ApplicationName = "SDK2.CertReuse", ApplicationUri = "urn:SDK2.CertReuse",
PublicHostname = "localhost", OpcUaPort = port, PkiStoreRoot = pki, AllowAnonymous = false,
}, NullLogger<OpcUaApplicationHost>.Instance), () => new OtOpcUaSdkServer(), TestContext.Current.CancellationToken);
await using var host = started.Host;
var keyFile = Directory.GetFiles(Path.Combine(pki, "own"), "*.pfx", SearchOption.AllDirectories).ShouldHaveSingleItem();
using var certificate = X509CertificateLoader.LoadPkcs12FromFile(keyFile, null);
certificate.HasPrivateKey.ShouldBeTrue();
if (thumbprint is null) thumbprint = certificate.Thumbprint;
else certificate.Thumbprint.ShouldBe(thumbprint);
using var key = certificate.GetRSAPrivateKey();
using var publicKey = certificate.GetRSAPublicKey();
var signature = key!.SignData([1, 2, 3], HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
publicKey!.VerifyData(new byte[] { 1, 2, 3 }, signature, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1).ShouldBeTrue();
var registry = started.Server.CurrentInstance.IdentityRegistry;
var context = new AuthenticationContext(new AnonymousIdentityTokenHandler(),
new UserTokenPolicy(UserTokenType.Anonymous), new EndpointDescription(), started.Server.MessageContext);
(await registry.AuthenticateAsync(context, TestContext.Current.CancellationToken)).Outcome.ShouldBe(AuthenticationOutcome.Rejected);
var userContext = context with
{
TokenHandler = new UserNameIdentityTokenHandler("apikey", "invalid"u8),
UserTokenPolicy = new UserTokenPolicy(UserTokenType.UserName),
};
(await registry.AuthenticateAsync(userContext, TestContext.Current.CancellationToken)).Outcome.ShouldBe(AuthenticationOutcome.Rejected);
}
}
finally { if (Directory.Exists(pki)) Directory.Delete(pki, true); }
}
[Fact]
public async Task A_canceled_start_disposes_its_application_owner()
{
var pki = Path.Combine(Path.GetTempPath(), $"otua-sdk2-canceled-{Guid.NewGuid():N}");
await using var host = new OpcUaApplicationHost(new OpcUaApplicationHostOptions { PkiStoreRoot = pki }, NullLogger<OpcUaApplicationHost>.Instance);
using var server = new OtOpcUaSdkServer();
using var cancellation = new CancellationTokenSource();
cancellation.Cancel();
await Should.ThrowAsync<OperationCanceledException>(() => host.StartAsync(server, cancellation.Token));
host.ApplicationInstance.ShouldBeNull();
if (Directory.Exists(pki)) Directory.Delete(pki, true);
}
}