mirror of
https://github.com/OPCFoundation/UA-.NETStandard.git
synced 2026-10-22 09:37:12 +08:00
PEM support (#33)
This commit is contained in:
@@ -430,7 +430,14 @@ namespace Opc.Ua.Server
|
||||
{
|
||||
foreach (var issuer in updateCertificate.IssuerCollection)
|
||||
{
|
||||
issuerStore.Add(issuer);
|
||||
try
|
||||
{
|
||||
issuerStore.Add(issuer).Wait();
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
// ignore error if issuer cert already exists
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -451,6 +458,9 @@ namespace Opc.Ua.Server
|
||||
{
|
||||
// update Application certificate
|
||||
await m_configuration.CertificateValidator.Update(m_configuration);
|
||||
|
||||
// TODO: call OnUpdateConfiguration
|
||||
|
||||
// force close all sessions
|
||||
foreach (var session in Server.SessionManager.GetSessions())
|
||||
{
|
||||
|
||||
@@ -37,7 +37,7 @@ using Opc.Ua.Gds;
|
||||
using Opc.Ua.Gds.Client;
|
||||
using Opc.Ua.Gds.Test;
|
||||
using Opc.Ua.Test;
|
||||
using System.Threading.Tasks;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
|
||||
namespace NUnit.Opc.Ua.Gds.Test
|
||||
{
|
||||
@@ -81,7 +81,7 @@ namespace NUnit.Opc.Ua.Gds.Test
|
||||
///
|
||||
/// </summary>
|
||||
///
|
||||
[TestFixture]
|
||||
[TestFixture, Category("GDSRegistrationAndPull")]
|
||||
public class ClientTest
|
||||
{
|
||||
#region Test Setup
|
||||
@@ -92,6 +92,7 @@ namespace NUnit.Opc.Ua.Gds.Test
|
||||
protected void OneTimeSetUp()
|
||||
{
|
||||
#if DEBUG
|
||||
// work around travis issue by selecting different ports on every run
|
||||
const int testPort = 60000;
|
||||
#else
|
||||
const int testPort = 60010;
|
||||
@@ -109,6 +110,9 @@ namespace NUnit.Opc.Ua.Gds.Test
|
||||
// good applications test set
|
||||
_goodApplicationTestSet = ApplicationTestSet(goodApplicationsTestCount, false);
|
||||
_invalidApplicationTestSet = ApplicationTestSet(invalidApplicationsTestCount, true);
|
||||
|
||||
_goodRegistrationOk = false;
|
||||
_invalidRegistrationOk = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -147,11 +151,13 @@ namespace NUnit.Opc.Ua.Gds.Test
|
||||
Assert.AreEqual(id.IdType, IdType.Guid);
|
||||
application.ApplicationRecord.ApplicationId = id;
|
||||
}
|
||||
_goodRegistrationOk = true;
|
||||
}
|
||||
|
||||
[Test, Order(101)]
|
||||
public void RegisterGoodApplicationsAuditEvents()
|
||||
{
|
||||
AssertIgnoreTestWithoutGoodRegistration();
|
||||
foreach (var application in _goodApplicationTestSet)
|
||||
{
|
||||
// TODO
|
||||
@@ -166,11 +172,13 @@ namespace NUnit.Opc.Ua.Gds.Test
|
||||
{
|
||||
Assert.That(() => { NodeId id = _gdsClient.GDSClient.RegisterApplication(application.ApplicationRecord); }, Throws.Exception);
|
||||
}
|
||||
_invalidRegistrationOk = true;
|
||||
}
|
||||
|
||||
[Test, Order(111)]
|
||||
public void RegisterInvalidApplicationsAuditEvents()
|
||||
{
|
||||
AssertIgnoreTestWithoutInvalidRegistration();
|
||||
ConnectGDS(true);
|
||||
foreach (var application in _invalidApplicationTestSet)
|
||||
{
|
||||
@@ -181,6 +189,7 @@ namespace NUnit.Opc.Ua.Gds.Test
|
||||
[Test, Order(120)]
|
||||
public void RegisterApplicationAsUser()
|
||||
{
|
||||
AssertIgnoreTestWithoutInvalidRegistration();
|
||||
ConnectGDS(false);
|
||||
foreach (var application in _invalidApplicationTestSet)
|
||||
{
|
||||
@@ -191,6 +200,7 @@ namespace NUnit.Opc.Ua.Gds.Test
|
||||
[Test, Order(200)]
|
||||
public void UpdateGoodApplications()
|
||||
{
|
||||
AssertIgnoreTestWithoutGoodRegistration();
|
||||
ConnectGDS(true);
|
||||
foreach (var application in _goodApplicationTestSet)
|
||||
{
|
||||
@@ -207,6 +217,7 @@ namespace NUnit.Opc.Ua.Gds.Test
|
||||
[Test, Order(201)]
|
||||
public void UpdateGoodApplicationsAuditEvents()
|
||||
{
|
||||
AssertIgnoreTestWithoutGoodRegistration();
|
||||
foreach (var application in _goodApplicationTestSet)
|
||||
{
|
||||
// TODO
|
||||
@@ -216,6 +227,7 @@ namespace NUnit.Opc.Ua.Gds.Test
|
||||
[Test, Order(210)]
|
||||
public void UpdateGoodApplicationsWithNewGuid()
|
||||
{
|
||||
AssertIgnoreTestWithoutGoodRegistration();
|
||||
ConnectGDS(true);
|
||||
foreach (var application in _goodApplicationTestSet)
|
||||
{
|
||||
@@ -228,6 +240,7 @@ namespace NUnit.Opc.Ua.Gds.Test
|
||||
[Test, Order(211)]
|
||||
public void UpdateGoodApplicationsWithNewGuidAuditEvents()
|
||||
{
|
||||
AssertIgnoreTestWithoutGoodRegistration();
|
||||
ConnectGDS(true);
|
||||
foreach (var application in _goodApplicationTestSet)
|
||||
{
|
||||
@@ -238,6 +251,7 @@ namespace NUnit.Opc.Ua.Gds.Test
|
||||
[Test, Order(220)]
|
||||
public void UpdateInvalidApplications()
|
||||
{
|
||||
AssertIgnoreTestWithoutInvalidRegistration();
|
||||
ConnectGDS(true);
|
||||
foreach (var application in _invalidApplicationTestSet)
|
||||
{
|
||||
@@ -248,6 +262,7 @@ namespace NUnit.Opc.Ua.Gds.Test
|
||||
[Test, Order(221)]
|
||||
public void UpdateInvalidApplicationsAuditEvents()
|
||||
{
|
||||
AssertIgnoreTestWithoutInvalidRegistration();
|
||||
ConnectGDS(true);
|
||||
foreach (var application in _invalidApplicationTestSet)
|
||||
{
|
||||
@@ -258,6 +273,7 @@ namespace NUnit.Opc.Ua.Gds.Test
|
||||
[Test, Order(400)]
|
||||
public void FindGoodApplications()
|
||||
{
|
||||
AssertIgnoreTestWithoutGoodRegistration();
|
||||
ConnectGDS(false);
|
||||
foreach (var application in _goodApplicationTestSet)
|
||||
{
|
||||
@@ -270,6 +286,7 @@ namespace NUnit.Opc.Ua.Gds.Test
|
||||
[Test, Order(400)]
|
||||
public void FindInvalidApplications()
|
||||
{
|
||||
AssertIgnoreTestWithoutInvalidRegistration();
|
||||
ConnectGDS(true);
|
||||
foreach (var application in _invalidApplicationTestSet)
|
||||
{
|
||||
@@ -282,6 +299,7 @@ namespace NUnit.Opc.Ua.Gds.Test
|
||||
[Test, Order(400)]
|
||||
public void GetGoodApplications()
|
||||
{
|
||||
AssertIgnoreTestWithoutGoodRegistration();
|
||||
ConnectGDS(false);
|
||||
foreach (var application in _goodApplicationTestSet)
|
||||
{
|
||||
@@ -294,6 +312,7 @@ namespace NUnit.Opc.Ua.Gds.Test
|
||||
[Test, Order(400)]
|
||||
public void GetInvalidApplications()
|
||||
{
|
||||
AssertIgnoreTestWithoutInvalidRegistration();
|
||||
ConnectGDS(true);
|
||||
foreach (var application in _invalidApplicationTestSet)
|
||||
{
|
||||
@@ -304,6 +323,7 @@ namespace NUnit.Opc.Ua.Gds.Test
|
||||
[Test, Order(410)]
|
||||
public void QueryGoodServers()
|
||||
{
|
||||
AssertIgnoreTestWithoutGoodRegistration();
|
||||
ConnectGDS(false);
|
||||
// get all servers
|
||||
var allServers = _gdsClient.GDSClient.QueryServers(0, "", "", "", null);
|
||||
@@ -370,6 +390,7 @@ namespace NUnit.Opc.Ua.Gds.Test
|
||||
[Test, Order(500)]
|
||||
public void StartGoodNewKeyPairRequests()
|
||||
{
|
||||
AssertIgnoreTestWithoutGoodRegistration();
|
||||
ConnectGDS(true);
|
||||
foreach (var application in _goodApplicationTestSet)
|
||||
{
|
||||
@@ -390,6 +411,7 @@ namespace NUnit.Opc.Ua.Gds.Test
|
||||
[Test, Order(501)]
|
||||
public void StartInvalidNewKeyPairRequests()
|
||||
{
|
||||
AssertIgnoreTestWithoutInvalidRegistration();
|
||||
ConnectGDS(true);
|
||||
foreach (var application in _invalidApplicationTestSet)
|
||||
{
|
||||
@@ -411,6 +433,7 @@ namespace NUnit.Opc.Ua.Gds.Test
|
||||
[Test, Order(510)]
|
||||
public void FinishGoodNewKeyPairRequests()
|
||||
{
|
||||
AssertIgnoreTestWithoutGoodRegistration();
|
||||
ConnectGDS(true);
|
||||
bool requestBusy;
|
||||
do
|
||||
@@ -455,8 +478,8 @@ namespace NUnit.Opc.Ua.Gds.Test
|
||||
[Test, Order(511)]
|
||||
public void FinishInvalidNewKeyPairRequests()
|
||||
{
|
||||
AssertIgnoreTestWithoutInvalidRegistration();
|
||||
ConnectGDS(true);
|
||||
|
||||
foreach (var application in _invalidApplicationTestSet)
|
||||
{
|
||||
Assert.That(() =>
|
||||
@@ -474,11 +497,20 @@ namespace NUnit.Opc.Ua.Gds.Test
|
||||
[Test, Order(520)]
|
||||
public void StartGoodSigningRequests()
|
||||
{
|
||||
AssertIgnoreTestWithoutGoodRegistration();
|
||||
ConnectGDS(true);
|
||||
foreach (var application in _goodApplicationTestSet)
|
||||
{
|
||||
Assert.Null(application.CertificateRequestId);
|
||||
var csrCertificate = CertificateFactory.CreateCertificateFromPKCS12(application.PrivateKey, application.PrivateKeyPassword);
|
||||
X509Certificate2 csrCertificate;
|
||||
if (application.PrivateKeyFormat == "PFX")
|
||||
{
|
||||
csrCertificate = CertificateFactory.CreateCertificateFromPKCS12(application.PrivateKey, application.PrivateKeyPassword);
|
||||
}
|
||||
else
|
||||
{
|
||||
csrCertificate = CertificateFactory.CreateCertificateWithPEMPrivateKey(new X509Certificate2(application.Certificate), application.PrivateKey, application.PrivateKeyPassword);
|
||||
}
|
||||
byte[] certificateRequest = CertificateFactory.CreateSigningRequest(csrCertificate, application.DomainNames);
|
||||
NodeId requestId = _gdsClient.GDSClient.StartSigningRequest(
|
||||
application.ApplicationRecord.ApplicationId,
|
||||
@@ -493,8 +525,8 @@ namespace NUnit.Opc.Ua.Gds.Test
|
||||
[Test, Order(521)]
|
||||
public void FinishGoodSigningRequests()
|
||||
{
|
||||
AssertIgnoreTestWithoutGoodRegistration();
|
||||
ConnectGDS(true);
|
||||
|
||||
bool requestBusy;
|
||||
do
|
||||
{
|
||||
@@ -538,6 +570,7 @@ namespace NUnit.Opc.Ua.Gds.Test
|
||||
[Test, Order(600)]
|
||||
public void GetGoodCertificateGroupsNullTests()
|
||||
{
|
||||
AssertIgnoreTestWithoutGoodRegistration();
|
||||
ConnectGDS(true);
|
||||
|
||||
Assert.That(() =>
|
||||
@@ -567,8 +600,8 @@ namespace NUnit.Opc.Ua.Gds.Test
|
||||
[Test, Order(601)]
|
||||
public void GetInvalidCertificateGroupsNullTests()
|
||||
{
|
||||
AssertIgnoreTestWithoutInvalidRegistration();
|
||||
ConnectGDS(true);
|
||||
|
||||
Assert.That(() =>
|
||||
{
|
||||
_gdsClient.GDSClient.GetCertificateGroups(null);
|
||||
@@ -598,6 +631,7 @@ namespace NUnit.Opc.Ua.Gds.Test
|
||||
[Test, Order(610)]
|
||||
public void GetGoodCertificateGroupsAndTrustLists()
|
||||
{
|
||||
AssertIgnoreTestWithoutGoodRegistration();
|
||||
ConnectGDS(true);
|
||||
|
||||
foreach (var application in _goodApplicationTestSet)
|
||||
@@ -615,8 +649,8 @@ namespace NUnit.Opc.Ua.Gds.Test
|
||||
[Test, Order(690)]
|
||||
public void GetGoodCertificateStatus()
|
||||
{
|
||||
AssertIgnoreTestWithoutGoodRegistration();
|
||||
ConnectGDS(true);
|
||||
|
||||
foreach (var application in _goodApplicationTestSet)
|
||||
{
|
||||
var certificateStatus = _gdsClient.GDSClient.GetCertificateStatus(application.ApplicationRecord.ApplicationId, null, null);
|
||||
@@ -626,8 +660,8 @@ namespace NUnit.Opc.Ua.Gds.Test
|
||||
[Test, Order(691)]
|
||||
public void GetInvalidCertificateStatus()
|
||||
{
|
||||
AssertIgnoreTestWithoutInvalidRegistration();
|
||||
ConnectGDS(true);
|
||||
|
||||
foreach (var application in _invalidApplicationTestSet)
|
||||
{
|
||||
Assert.That(() =>
|
||||
@@ -641,7 +675,6 @@ namespace NUnit.Opc.Ua.Gds.Test
|
||||
public void UnregisterGoodApplications()
|
||||
{
|
||||
ConnectGDS(true);
|
||||
|
||||
foreach (var application in _goodApplicationTestSet)
|
||||
{
|
||||
_gdsClient.GDSClient.UnregisterApplication(application.ApplicationRecord.ApplicationId);
|
||||
@@ -748,6 +781,7 @@ namespace NUnit.Opc.Ua.Gds.Test
|
||||
string appName = "UA " + pureAppName;
|
||||
StringCollection domainNames = RandomDomainNames();
|
||||
string localhost = domainNames[0];
|
||||
string privateKeyFormat = _randomSource.NextInt32(1) == 0 ? "PEM" : "PFX";
|
||||
string appUri = ("urn:localhost:opcfoundation.org:" + pureAppUri.ToLower()).Replace("localhost", localhost);
|
||||
string prodUri = "http://opcfoundation.org/UA/" + pureAppUri;
|
||||
StringCollection discoveryUrls = new StringCollection();
|
||||
@@ -779,7 +813,8 @@ namespace NUnit.Opc.Ua.Gds.Test
|
||||
ServerCapabilities = serverCapabilities
|
||||
},
|
||||
DomainNames = domainNames,
|
||||
Subject = String.Format("CN={0},DC={1},O=OPC Foundation", appName, localhost)
|
||||
Subject = String.Format("CN={0},DC={1},O=OPC Foundation", appName, localhost),
|
||||
PrivateKeyFormat = privateKeyFormat
|
||||
};
|
||||
return testData;
|
||||
}
|
||||
@@ -847,7 +882,7 @@ namespace NUnit.Opc.Ua.Gds.Test
|
||||
|
||||
private void ConnectGDS(bool admin)
|
||||
{
|
||||
_gdsClient.GDSClient.AdminCredentials = new UserIdentity(admin?"appadmin":"appuser", "demo");
|
||||
_gdsClient.GDSClient.AdminCredentials = new UserIdentity(admin ? "appadmin" : "appuser", "demo");
|
||||
_gdsClient.GDSClient.Connect(_gdsClient.GDSClient.EndpointUrl).Wait();
|
||||
}
|
||||
|
||||
@@ -856,6 +891,21 @@ namespace NUnit.Opc.Ua.Gds.Test
|
||||
_gdsClient.GDSClient.Disconnect();
|
||||
}
|
||||
|
||||
private void AssertIgnoreTestWithoutGoodRegistration()
|
||||
{
|
||||
if (!_goodRegistrationOk)
|
||||
{
|
||||
Assert.Ignore("Test requires good application registrations.");
|
||||
}
|
||||
}
|
||||
|
||||
private void AssertIgnoreTestWithoutInvalidRegistration()
|
||||
{
|
||||
if (!_invalidRegistrationOk)
|
||||
{
|
||||
Assert.Ignore("Test requires invalid application registration.");
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
#region Private Fields
|
||||
private const int goodApplicationsTestCount = 10;
|
||||
@@ -868,6 +918,8 @@ namespace NUnit.Opc.Ua.Gds.Test
|
||||
private IList<ApplicationTestData> _goodApplicationTestSet;
|
||||
private IList<ApplicationTestData> _invalidApplicationTestSet;
|
||||
private ServerCapabilities _serverCapabilities;
|
||||
private bool _goodRegistrationOk;
|
||||
private bool _invalidRegistrationOk;
|
||||
#endregion
|
||||
}
|
||||
|
||||
|
||||
@@ -68,7 +68,6 @@ namespace Opc.Ua.Gds.Test
|
||||
|
||||
config.CertificateValidator.CertificateValidation += new CertificateValidationEventHandler(CertificateValidator_CertificateValidation);
|
||||
|
||||
// use same server configuration for gds and push tests
|
||||
GlobalDiscoveryClientConfiguration gdsClientConfiguration = application.ApplicationConfiguration.ParseExtension<GlobalDiscoveryClientConfiguration>();
|
||||
_client = new GlobalDiscoveryServerClient(application, gdsClientConfiguration);
|
||||
_client.EndpointUrl = TestUtils.PatchEndpointUrlPort(gdsClientConfiguration.GlobalDiscoveryServerUrl, port);
|
||||
|
||||
@@ -78,6 +78,27 @@ namespace Opc.Ua.Gds.Test
|
||||
TestUtils.CleanupTrustList(config.SecurityConfiguration.RejectedCertificateStore.OpenStore());
|
||||
}
|
||||
|
||||
if (clean)
|
||||
{
|
||||
string thumbprint = config.SecurityConfiguration.ApplicationCertificate.Thumbprint;
|
||||
if (thumbprint != null)
|
||||
{
|
||||
using (var store = config.SecurityConfiguration.ApplicationCertificate.OpenStore())
|
||||
{
|
||||
await store.Delete(thumbprint);
|
||||
}
|
||||
}
|
||||
|
||||
// always start with clean cert store
|
||||
TestUtils.CleanupTrustList(config.SecurityConfiguration.ApplicationCertificate.OpenStore());
|
||||
TestUtils.CleanupTrustList(config.SecurityConfiguration.TrustedIssuerCertificates.OpenStore());
|
||||
TestUtils.CleanupTrustList(config.SecurityConfiguration.TrustedPeerCertificates.OpenStore());
|
||||
TestUtils.CleanupTrustList(config.SecurityConfiguration.RejectedCertificateStore.OpenStore());
|
||||
config = await application.LoadApplicationConfiguration(false);
|
||||
}
|
||||
|
||||
TestUtils.PatchBaseAddressesPorts(config, basePort);
|
||||
|
||||
// check the application certificate.
|
||||
bool haveAppCertificate = await application.CheckApplicationInstanceCertificate(false, 0);
|
||||
if (!haveAppCertificate)
|
||||
|
||||
@@ -27,20 +27,24 @@
|
||||
* http://opcfoundation.org/License/MIT/1.00/
|
||||
* ======================================================================*/
|
||||
|
||||
using NUnit.Framework;
|
||||
using Opc.Ua;
|
||||
using Opc.Ua.Client;
|
||||
using Opc.Ua.Gds;
|
||||
using Opc.Ua.Gds.Client;
|
||||
using Opc.Ua.Gds.Test;
|
||||
using Opc.Ua.Test;
|
||||
using NUnit.Framework;
|
||||
using System;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Linq;
|
||||
|
||||
namespace NUnit.Opc.Ua.Gds.Test
|
||||
{
|
||||
|
||||
[TestFixture, Category("GDS")]
|
||||
[TestFixture, Category("GDSPush")]
|
||||
public class PushTest
|
||||
{
|
||||
#region Test Setup
|
||||
@@ -60,7 +64,7 @@ namespace NUnit.Opc.Ua.Gds.Test
|
||||
_randomSource = new RandomSource(randomStart);
|
||||
_dataGenerator = new DataGenerator(_randomSource);
|
||||
_server = new GlobalDiscoveryTestServer(true);
|
||||
await _server.StartServer(false, testPort);
|
||||
await _server.StartServer(true, testPort);
|
||||
await Task.Delay(1000);
|
||||
|
||||
// load clients
|
||||
@@ -72,6 +76,13 @@ namespace NUnit.Opc.Ua.Gds.Test
|
||||
// connect once
|
||||
await _gdsClient.GDSClient.Connect(_gdsClient.GDSClient.EndpointUrl);
|
||||
await _pushClient.PushClient.Connect(_pushClient.PushClient.EndpointUrl);
|
||||
|
||||
ConnectGDSClient(true);
|
||||
RegisterPushServerApplication(_pushClient.PushClient.EndpointUrl);
|
||||
|
||||
_selfSignedServerCert = new X509Certificate2(_pushClient.PushClient.Session.ConfiguredEndpoint.Description.ServerCertificate);
|
||||
_domainNames = Utils.GetDomainsFromCertficate(_selfSignedServerCert).ToArray();
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -80,6 +91,8 @@ namespace NUnit.Opc.Ua.Gds.Test
|
||||
[OneTimeTearDown]
|
||||
protected void OneTimeTearDown()
|
||||
{
|
||||
ConnectGDSClient(true);
|
||||
UnRegisterPushServerApplication();
|
||||
_gdsClient.DisconnectClient();
|
||||
_gdsClient = null;
|
||||
_pushClient.DisconnectClient();
|
||||
@@ -96,8 +109,8 @@ namespace NUnit.Opc.Ua.Gds.Test
|
||||
DisconnectPushClient();
|
||||
}
|
||||
|
||||
#endregion
|
||||
#region Test Methods
|
||||
#endregion
|
||||
#region Test Methods
|
||||
[Test, Order(100)]
|
||||
public void GetSupportedKeyFormats()
|
||||
{
|
||||
@@ -175,7 +188,15 @@ namespace NUnit.Opc.Ua.Gds.Test
|
||||
Assert.That(() => { _pushClient.PushClient.CreateCertificateRequest(invalidCertGroup, invalidCertType, null, false, null); }, Throws.Exception);
|
||||
byte[] csr = _pushClient.PushClient.CreateCertificateRequest(null, null, null, false, null);
|
||||
Assert.IsNotNull(csr);
|
||||
}
|
||||
|
||||
[Test, Order(400)]
|
||||
public void CreateCertificateRequestWithNewPrivateKey()
|
||||
{
|
||||
ConnectPushClient(true);
|
||||
ConnectGDSClient(true);
|
||||
byte[] csr = _pushClient.PushClient.CreateCertificateRequest(null, null, null, true, Encoding.ASCII.GetBytes("OPCTest"));
|
||||
Assert.IsNotNull(csr);
|
||||
}
|
||||
|
||||
[Test, Order(500)]
|
||||
@@ -207,24 +228,173 @@ namespace NUnit.Opc.Ua.Gds.Test
|
||||
{
|
||||
_pushClient.PushClient.ApplyChanges();
|
||||
}
|
||||
VerifyNewPushServerCert(serverCert.RawData);
|
||||
}
|
||||
|
||||
[Test, Order(510)]
|
||||
public void UpdateCertificateCASigned()
|
||||
{
|
||||
ConnectPushClient(true);
|
||||
ConnectGDSClient(true);
|
||||
byte[] csr = _pushClient.PushClient.CreateCertificateRequest(null, null, null, false, null);
|
||||
Assert.IsNotNull(csr);
|
||||
NodeId requestId = _gdsClient.GDSClient.StartSigningRequest(
|
||||
_applicationRecord.ApplicationId,
|
||||
null,
|
||||
null,
|
||||
csr);
|
||||
Assert.NotNull(requestId);
|
||||
byte[] privateKey;
|
||||
byte[] certificate;
|
||||
byte[][] issuerCertificates;
|
||||
int i = 0;
|
||||
do
|
||||
{
|
||||
Thread.Sleep(500);
|
||||
certificate = _gdsClient.GDSClient.FinishRequest(
|
||||
_applicationRecord.ApplicationId,
|
||||
requestId,
|
||||
out privateKey,
|
||||
out issuerCertificates);
|
||||
Assert.LessOrEqual(i++, 5);
|
||||
} while (certificate == null);
|
||||
Assert.NotNull(issuerCertificates);
|
||||
Assert.IsNull(privateKey);
|
||||
|
||||
var success = _pushClient.PushClient.UpdateCertificate(null, null, certificate, null, null, issuerCertificates);
|
||||
if (success)
|
||||
{
|
||||
_pushClient.PushClient.ApplyChanges();
|
||||
}
|
||||
VerifyNewPushServerCert(certificate);
|
||||
}
|
||||
|
||||
|
||||
[Test, Order(520)]
|
||||
public void UpdateCertificateSelfSignedWithPFXKey()
|
||||
public void UpdateCertificateSelfSignedPFX()
|
||||
{
|
||||
ConnectPushClient(true);
|
||||
UpdateCertificateSelfSigned("PFX");
|
||||
}
|
||||
|
||||
[Test, Order(530)]
|
||||
public void UpdateCertificateSelfSignedWithPEMKey()
|
||||
public void UpdateCertificateSelfSignedPEM()
|
||||
{
|
||||
UpdateCertificateSelfSigned("PEM");
|
||||
}
|
||||
|
||||
public void UpdateCertificateSelfSigned(string keyFormat)
|
||||
{
|
||||
ConnectPushClient(true);
|
||||
var keyFormats = _pushClient.PushClient.GetSupportedKeyFormats();
|
||||
if (!keyFormats.Contains(keyFormat))
|
||||
{
|
||||
Assert.Ignore("Push server doesn't support {0} key update", keyFormat);
|
||||
}
|
||||
|
||||
X509Certificate2 newCert = CertificateFactory.CreateCertificate(
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
_applicationRecord.ApplicationUri,
|
||||
_applicationRecord.ApplicationNames[0].Text,
|
||||
_selfSignedServerCert.Subject,
|
||||
null,
|
||||
CertificateFactory.defaultKeySize,
|
||||
DateTime.UtcNow,
|
||||
CertificateFactory.defaultLifeTime,
|
||||
CertificateFactory.defaultHashSize);
|
||||
|
||||
byte[] privateKey = null;
|
||||
if (keyFormat == "PFX")
|
||||
{
|
||||
Assert.IsTrue(newCert.HasPrivateKey);
|
||||
privateKey = newCert.Export(X509ContentType.Pfx);
|
||||
}
|
||||
else if (keyFormat == "PEM")
|
||||
{
|
||||
Assert.IsTrue(newCert.HasPrivateKey);
|
||||
privateKey = CertificateFactory.ExportPrivateKeyAsPEM(newCert);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.Fail("Testing unsupported key format {0}.", keyFormat);
|
||||
}
|
||||
|
||||
var success = _pushClient.PushClient.UpdateCertificate(
|
||||
null,
|
||||
null,
|
||||
newCert.RawData,
|
||||
keyFormat,
|
||||
privateKey,
|
||||
null);
|
||||
|
||||
if (success)
|
||||
{
|
||||
_pushClient.PushClient.ApplyChanges();
|
||||
}
|
||||
VerifyNewPushServerCert(newCert.RawData);
|
||||
}
|
||||
|
||||
[Test, Order(540)]
|
||||
public void UpdateCertificateNewKeyPairPFX()
|
||||
{
|
||||
UpdateCertificateWithNewKeyPair("PFX");
|
||||
}
|
||||
|
||||
[Test, Order(550)]
|
||||
public void UpdateCertificateNewKeyPairPEM()
|
||||
{
|
||||
UpdateCertificateWithNewKeyPair("PEM");
|
||||
}
|
||||
|
||||
public void UpdateCertificateWithNewKeyPair(string keyFormat)
|
||||
{
|
||||
ConnectPushClient(true);
|
||||
var keyFormats = _pushClient.PushClient.GetSupportedKeyFormats();
|
||||
if (!keyFormats.Contains(keyFormat))
|
||||
{
|
||||
Assert.Ignore("Push server doesn't support {0} key update", keyFormat);
|
||||
}
|
||||
|
||||
NodeId requestId = _gdsClient.GDSClient.StartNewKeyPairRequest(
|
||||
_applicationRecord.ApplicationId,
|
||||
null,
|
||||
null,
|
||||
_selfSignedServerCert.Subject,
|
||||
_domainNames,
|
||||
keyFormat,
|
||||
null);
|
||||
|
||||
Assert.NotNull(requestId);
|
||||
byte[] privateKey;
|
||||
byte[] certificate;
|
||||
byte[][] issuerCertificates;
|
||||
int i = 0;
|
||||
do
|
||||
{
|
||||
Thread.Sleep(500);
|
||||
certificate = _gdsClient.GDSClient.FinishRequest(
|
||||
_applicationRecord.ApplicationId,
|
||||
requestId,
|
||||
out privateKey,
|
||||
out issuerCertificates);
|
||||
Assert.LessOrEqual(i++, 5);
|
||||
} while (certificate == null);
|
||||
Assert.NotNull(issuerCertificates);
|
||||
Assert.NotNull(privateKey);
|
||||
|
||||
var success = _pushClient.PushClient.UpdateCertificate(
|
||||
null,
|
||||
null,
|
||||
certificate,
|
||||
keyFormat,
|
||||
privateKey,
|
||||
issuerCertificates);
|
||||
if (success)
|
||||
{
|
||||
_pushClient.PushClient.ApplyChanges();
|
||||
}
|
||||
VerifyNewPushServerCert(certificate);
|
||||
}
|
||||
|
||||
[Test, Order(600)]
|
||||
@@ -234,7 +404,6 @@ namespace NUnit.Opc.Ua.Gds.Test
|
||||
var collection = _pushClient.PushClient.GetRejectedList();
|
||||
}
|
||||
|
||||
|
||||
[Test, Order(700)]
|
||||
public void ApplyChanges()
|
||||
{
|
||||
@@ -248,13 +417,12 @@ namespace NUnit.Opc.Ua.Gds.Test
|
||||
ConnectPushClient(false);
|
||||
Assert.That(() => { _pushClient.PushClient.ApplyChanges(); }, Throws.Exception);
|
||||
Assert.That(() => { _pushClient.PushClient.GetRejectedList(); }, Throws.Exception);
|
||||
X509Certificate2 serverCert = new X509Certificate2(_pushClient.PushClient.Session.ConfiguredEndpoint.Description.ServerCertificate);
|
||||
Assert.That(() => { _pushClient.PushClient.UpdateCertificate(null, null, serverCert.RawData, null, null, null); }, Throws.Exception);
|
||||
Assert.That(() => { _pushClient.PushClient.UpdateCertificate(null, null, _selfSignedServerCert.RawData, null, null, null); }, Throws.Exception);
|
||||
Assert.That(() => { _pushClient.PushClient.CreateCertificateRequest(null, null, null, false, null); }, Throws.Exception);
|
||||
Assert.That(() => { _pushClient.PushClient.ReadTrustList(); }, Throws.Exception);
|
||||
}
|
||||
#endregion
|
||||
#region Private Methods
|
||||
#endregion
|
||||
#region Private Methods
|
||||
private void ConnectPushClient(bool sysAdmin)
|
||||
{
|
||||
_pushClient.PushClient.AdminCredentials = new UserIdentity(sysAdmin ? "sysadmin" : "appuser", "demo");
|
||||
@@ -286,6 +454,49 @@ namespace NUnit.Opc.Ua.Gds.Test
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void RegisterPushServerApplication(string discoveryUrl)
|
||||
{
|
||||
if (_applicationRecord == null && discoveryUrl != null)
|
||||
{
|
||||
EndpointDescription endpointDescription = CoreClientUtils.SelectEndpoint(discoveryUrl, true);
|
||||
ApplicationDescription description = endpointDescription.Server;
|
||||
_applicationRecord = new ApplicationRecordDataType
|
||||
{
|
||||
ApplicationNames = new LocalizedTextCollection { description.ApplicationName },
|
||||
ApplicationUri = description.ApplicationUri,
|
||||
ApplicationType = description.ApplicationType,
|
||||
ProductUri = description.ProductUri,
|
||||
DiscoveryUrls = description.DiscoveryUrls,
|
||||
ServerCapabilities = new StringCollection { "NA" },
|
||||
};
|
||||
}
|
||||
Assert.IsNotNull(_applicationRecord);
|
||||
Assert.IsNull(_applicationRecord.ApplicationId);
|
||||
NodeId id = _gdsClient.GDSClient.RegisterApplication(_applicationRecord);
|
||||
Assert.IsNotNull(id);
|
||||
_applicationRecord.ApplicationId = id;
|
||||
}
|
||||
|
||||
private void UnRegisterPushServerApplication()
|
||||
{
|
||||
_gdsClient.GDSClient.UnregisterApplication(_applicationRecord.ApplicationId);
|
||||
_applicationRecord.ApplicationId = null;
|
||||
}
|
||||
|
||||
private void VerifyNewPushServerCert(byte[] certificate)
|
||||
{
|
||||
DisconnectPushClient();
|
||||
Thread.Sleep(500);
|
||||
_gdsClient.GDSClient.AdminCredentials = new UserIdentity("appadmin", "demo");
|
||||
_pushClient.PushClient.Connect(_pushClient.PushClient.EndpointUrl).Wait();
|
||||
#if TODO
|
||||
Assert.AreEqual(
|
||||
certificate,
|
||||
_pushClient.PushClient.Session.ConfiguredEndpoint.Description.ServerCertificate
|
||||
);
|
||||
#endif
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Private Fields
|
||||
@@ -296,6 +507,9 @@ namespace NUnit.Opc.Ua.Gds.Test
|
||||
private GlobalDiscoveryTestClient _gdsClient;
|
||||
private ServerConfigurationPushTestClient _pushClient;
|
||||
private ServerCapabilities _serverCapabilities;
|
||||
private ApplicationRecordDataType _applicationRecord;
|
||||
private X509Certificate2 _selfSignedServerCert;
|
||||
private string[] _domainNames;
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -909,8 +909,8 @@ namespace Opc.Ua.Gds.Server
|
||||
try
|
||||
{
|
||||
newCertificate = CertificateFactory.CreateCertificate(
|
||||
CertificateStoreType.Directory,
|
||||
m_configuration.ApplicationCertificatesStorePath,
|
||||
null,
|
||||
null,
|
||||
privateKeyPassword,
|
||||
application.ApplicationUri ?? "urn:ApplicationURI",
|
||||
application.ApplicationNames.Count > 0 ? application.ApplicationNames[0].Text : "ApplicationName",
|
||||
@@ -935,27 +935,32 @@ namespace Opc.Ua.Gds.Server
|
||||
return new ServiceResult(StatusCodes.BadConfigurationError, error.ToString());
|
||||
}
|
||||
|
||||
// since CreateCert() automatically stores the new cert in our cert store,
|
||||
// delete the private key but keep the cert
|
||||
byte[] privateKey;
|
||||
if (privateKeyFormat == "PFX")
|
||||
{
|
||||
privateKey = newCertificate.Export(X509ContentType.Pfx, privateKeyPassword);
|
||||
}
|
||||
else if (privateKeyFormat == "PEM")
|
||||
{
|
||||
privateKey = CertificateFactory.ExportPrivateKeyAsPEM(newCertificate);
|
||||
}
|
||||
else
|
||||
{
|
||||
return new ServiceResult(StatusCodes.BadInvalidArgument, "Invalid private key format");
|
||||
}
|
||||
|
||||
// store only app certificate
|
||||
using (DirectoryCertificateStore store = (DirectoryCertificateStore)CertificateStoreIdentifier.OpenStore(m_configuration.ApplicationCertificatesStorePath))
|
||||
{
|
||||
byte[] privateKeyPFX = null;
|
||||
var privateKeyPath = store.GetPrivateKeyFilePath(newCertificate.Thumbprint);
|
||||
if (privateKeyPath != null)
|
||||
{
|
||||
privateKeyPFX = File.ReadAllBytes(privateKeyPath);
|
||||
}
|
||||
|
||||
// delete private key file
|
||||
File.Delete(privateKeyPath);
|
||||
|
||||
requestId = m_database.CreateCertificateRequest(
|
||||
applicationId,
|
||||
newCertificate.RawData,
|
||||
privateKeyPFX,
|
||||
certificateGroup.Id.Identifier as string);
|
||||
store.Add(new X509Certificate2(newCertificate.RawData)).Wait();
|
||||
}
|
||||
|
||||
requestId = m_database.CreateCertificateRequest(
|
||||
applicationId,
|
||||
newCertificate.RawData,
|
||||
privateKey,
|
||||
certificateGroup.Id.Identifier as string);
|
||||
|
||||
// immediately approve certificate for now.
|
||||
m_database.ApproveCertificateRequest(requestId, false);
|
||||
|
||||
@@ -1262,12 +1267,12 @@ namespace Opc.Ua.Gds.Server
|
||||
handle.Validated = true;
|
||||
return handle.Node;
|
||||
}
|
||||
#endregion
|
||||
#endregion
|
||||
|
||||
#region Overridden Methods
|
||||
#endregion
|
||||
#region Overridden Methods
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
#region Private Methods
|
||||
/// <summary>
|
||||
/// Generates a new node id.
|
||||
/// </summary>
|
||||
@@ -1288,14 +1293,14 @@ namespace Opc.Ua.Gds.Server
|
||||
};
|
||||
return await certIdentifier.LoadPrivateKey(signingKeyPassword);
|
||||
}
|
||||
#endregion
|
||||
#endregion
|
||||
|
||||
#region Private Fields
|
||||
private string m_certificatePassword = null; // TODO: placeholder
|
||||
#region Private Fields
|
||||
private string m_certificatePassword = "";
|
||||
private uint m_nextNodeId;
|
||||
private GlobalDiscoveryServerConfiguration m_configuration;
|
||||
private IApplicationsDatabase m_database;
|
||||
private Dictionary<NodeId, CertificateGroup> m_certificateGroups;
|
||||
#endregion
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,6 +93,11 @@ public class CertificateFactoryX509Name : X509Name
|
||||
{
|
||||
}
|
||||
|
||||
public CertificateFactoryX509Name(bool reverse, string distinguishedName) :
|
||||
base(reverse, ConvertToX509Name(distinguishedName))
|
||||
{
|
||||
}
|
||||
|
||||
private static string ConvertToX509Name(string distinguishedName)
|
||||
{
|
||||
// convert from X509Certificate to bouncy castle DN entries
|
||||
@@ -136,8 +141,9 @@ public class CertificateFactory
|
||||
/// <param name="ensurePrivateKeyAccessible">If true a key container is created for a certificate that must be deleted by calling Cleanup.</param>
|
||||
/// <returns>The cached certificate.</returns>
|
||||
/// <remarks>
|
||||
/// This function is necessary because all private keys used for cryptography operations must be in a key conatiner.
|
||||
/// Private keys stored in a PFX file have no key conatiner by default.
|
||||
/// This function is necessary because all private keys used for cryptography
|
||||
/// operations must be in a key container.
|
||||
/// Private keys stored in a PFX file have no key container by default.
|
||||
/// </remarks>
|
||||
public static X509Certificate2 Load(X509Certificate2 certificate, bool ensurePrivateKeyAccessible)
|
||||
{
|
||||
@@ -560,7 +566,7 @@ public class CertificateFactory
|
||||
new CrlNumber(crlSerialNumber));
|
||||
|
||||
// generate updated CRL
|
||||
Org.BouncyCastle.X509.X509Crl updatedCrl = crlGen.Generate(signatureFactory);
|
||||
X509Crl updatedCrl = crlGen.Generate(signatureFactory);
|
||||
|
||||
// add updated CRL to store
|
||||
X509CRL updatedCRL = new X509CRL(updatedCrl.GetEncoded());
|
||||
@@ -650,7 +656,7 @@ public class CertificateFactory
|
||||
|
||||
Pkcs10CertificationRequest pkcs10CertificationRequest = new Pkcs10CertificationRequest(
|
||||
signatureFactory,
|
||||
new CertificateFactoryX509Name(certificate.Subject),
|
||||
new CertificateFactoryX509Name(false, certificate.Subject),
|
||||
publicKey,
|
||||
attributes,
|
||||
signingKey);
|
||||
@@ -767,6 +773,25 @@ public class CertificateFactory
|
||||
}
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// returns a byte array containing the private key in PEM format.
|
||||
/// </summary>
|
||||
|
||||
public static byte[] ExportPrivateKeyAsPEM(
|
||||
X509Certificate2 certificate
|
||||
)
|
||||
{
|
||||
RsaPrivateCrtKeyParameters keyParameter = GetPrivateKeyParameter(certificate);
|
||||
using (TextWriter textWriter = new StringWriter())
|
||||
{
|
||||
PemWriter pemWriter = new PemWriter(textWriter);
|
||||
pemWriter.WriteObject(keyParameter);
|
||||
pemWriter.Writer.Flush();
|
||||
return Encoding.ASCII.GetBytes(textWriter.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private class Password
|
||||
: IPasswordFinder
|
||||
{
|
||||
@@ -1205,6 +1230,7 @@ public class CertificateFactory
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private static Dictionary<string, X509Certificate2> m_certificates = new Dictionary<string, X509Certificate2>();
|
||||
|
||||
@@ -3064,6 +3064,10 @@ namespace Opc.Ua
|
||||
return false;
|
||||
}
|
||||
|
||||
// sort to ensure similar entries are compared
|
||||
fields1.Sort(StringComparer.OrdinalIgnoreCase);
|
||||
fields2.Sort(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
// compare each.
|
||||
for (int ii = 0; ii < fields1.Count; ii++)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user