65 lines
3.6 KiB
C#
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);
|
|
}
|
|
}
|