PEM support (#33)

This commit is contained in:
Martin Regen
2017-11-15 21:08:43 +01:00
committed by GitHub
parent 09fbb05752
commit e156a3c3da
8 changed files with 388 additions and 57 deletions
@@ -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>();
+4
View File
@@ -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++)
{