Files
lmxopcua/tests/Client/ZB.MOM.WW.OtOpcUa.Client.Shared.Tests/Adapters/DefaultApplicationConfigurationFactoryTests.cs
T

98 lines
5.1 KiB
C#

using Opc.Ua;
using Opc.Ua.Configuration;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.Client.Shared.Adapters;
using ZB.MOM.WW.OtOpcUa.Client.Shared.Models;
namespace ZB.MOM.WW.OtOpcUa.Client.Shared.Tests.Adapters;
/// <summary>Uses the real configuration factory so fake sessions cannot conceal SDK initialization failures.</summary>
public sealed class DefaultApplicationConfigurationFactoryTests
{
[Theory]
[InlineData(false)]
[InlineData(true)]
public async Task New_certificates_use_the_default_lifetime_and_existing_private_keys_are_reused(bool seedLongerCertificate)
{
var ct = TestContext.Current.CancellationToken;
var root = Path.Combine(Path.GetTempPath(), $"otopcua-certificate-lifetime-{Guid.NewGuid():N}");
var settings = new ConnectionSettings
{
EndpointUrl = "opc.tcp://localhost:4840", CertificateStorePath = root,
SecurityMode = SecurityMode.None, AutoAcceptCertificates = false,
};
var factory = new DefaultApplicationConfigurationFactory();
ApplicationConfiguration? current = null;
try
{
if (seedLongerCertificate)
{
var seed = await factory.CreateAsync(settings, ct);
await using var application = new ApplicationInstance(DefaultTelemetry.Create(_ => { })) { ApplicationConfiguration = seed };
(await application.CheckApplicationInstanceCertificatesAsync(true, lifeTimeInMonths: 60, ct)).ShouldBeTrue();
}
settings.SecurityMode = SecurityMode.SignAndEncrypt;
current = await factory.CreateAsync(settings, ct);
var certPath = Directory.GetFiles(Path.Combine(root, "own", "certs"), "*.der").ShouldHaveSingleItem();
var privatePath = Directory.GetFiles(Path.Combine(root, "own", "private"), "*.pfx").ShouldHaveSingleItem();
using var certificate = X509CertificateLoader.LoadCertificateFromFile(certPath);
using var privateCertificate = X509CertificateLoader.LoadPkcs12FromFile(privatePath, null, OperatingSystem.IsMacOS() ? X509KeyStorageFlags.DefaultKeySet : X509KeyStorageFlags.EphemeralKeySet);
privateCertificate.HasPrivateKey.ShouldBeTrue();
certificate.Thumbprint.ShouldBe(privateCertificate.Thumbprint);
using var rsa = certificate.GetRSAPublicKey();
rsa.ShouldNotBeNull().KeySize.ShouldBeGreaterThanOrEqualTo(2048);
if (seedLongerCertificate)
certificate.NotAfter.ToUniversalTime().ShouldBeGreaterThan(DateTime.UtcNow.AddYears(4));
else
{
certificate.NotAfter.ToUniversalTime().ShouldBeGreaterThan(DateTime.UtcNow.AddMonths(CertificateFactory.DefaultLifeTime - 1));
certificate.NotAfter.ToUniversalTime().ShouldBeLessThan(DateTime.UtcNow.AddMonths(CertificateFactory.DefaultLifeTime).AddDays(2));
}
var originalKeyHash = SHA256.HashData(await File.ReadAllBytesAsync(privatePath, ct));
(current.CertificateManager as IDisposable)?.Dispose(); current = null;
current = await factory.CreateAsync(settings, ct);
using var reused = X509CertificateLoader.LoadCertificateFromFile(certPath);
reused.Thumbprint.ShouldBe(certificate.Thumbprint);
SHA256.HashData(await File.ReadAllBytesAsync(privatePath, ct)).ShouldBe(originalKeyHash,
"reinitializing with a shorter creation default must not rotate an existing certificate or private key");
Directory.GetFiles(Path.Combine(root, "own", "certs"), "*.der").Length.ShouldBe(1);
}
finally
{
(current?.CertificateManager as IDisposable)?.Dispose();
if (Directory.Exists(root)) Directory.Delete(root, true);
}
}
[Theory]
[InlineData(SecurityMode.None)]
[InlineData(SecurityMode.SignAndEncrypt)]
public async Task Real_configuration_initializes_its_certificate_manager(SecurityMode mode)
{
var root = Path.Combine(Path.GetTempPath(), $"otopcua-real-config-{Guid.NewGuid():N}");
ApplicationConfiguration? config = null;
try
{
config = await new DefaultApplicationConfigurationFactory().CreateAsync(new ConnectionSettings
{
EndpointUrl = "opc.tcp://localhost:4840",
CertificateStorePath = root,
SecurityMode = mode,
AutoAcceptCertificates = false,
}, TestContext.Current.CancellationToken);
config.CertificateManager.ShouldNotBeNull();
config.SecurityConfiguration.AutoAcceptUntrustedCertificates.ShouldBeFalse();
if (mode != SecurityMode.None)
Directory.EnumerateFiles(Path.Combine(root, "own"), "*", SearchOption.AllDirectories).ShouldNotBeEmpty();
}
finally
{
(config?.CertificateManager as IDisposable)?.Dispose();
if (Directory.Exists(root)) Directory.Delete(root, recursive: true);
}
}
}