diff --git a/SampleApplications/Samples/NetCoreConsoleClient/Program.cs b/SampleApplications/Samples/NetCoreConsoleClient/Program.cs
index fa856a0595..87473f4709 100644
--- a/SampleApplications/Samples/NetCoreConsoleClient/Program.cs
+++ b/SampleApplications/Samples/NetCoreConsoleClient/Program.cs
@@ -70,6 +70,11 @@ namespace NetCoreConsoleClient
StoreType = "Directory",
StorePath = "./OPC Foundation/CertificateStores/UA Certificate Authorities",
},
+ RejectedCertificateStore = new CertificateTrustList
+ {
+ StoreType = "Directory",
+ StorePath = "./OPC Foundation/CertificateStores/RejectedCertificates",
+ },
NonceLength = 32,
AutoAcceptUntrustedCertificates = true
},
diff --git a/Stack/Opc.Ua.Core/Security/Certificates/CertificateIdentifier.cs b/Stack/Opc.Ua.Core/Security/Certificates/CertificateIdentifier.cs
index e75fda64c5..753521bebd 100644
--- a/Stack/Opc.Ua.Core/Security/Certificates/CertificateIdentifier.cs
+++ b/Stack/Opc.Ua.Core/Security/Certificates/CertificateIdentifier.cs
@@ -371,7 +371,7 @@ namespace Opc.Ua
{
if (certificates == null || certificates.Count == 0)
{
- throw new Exception("Primary certificate has not been provided.");
+ throw new CryptographicException("Primary certificate has not been provided.");
}
// copy the primary certificate.
@@ -425,7 +425,7 @@ namespace Opc.Ua
{
if (!IsValidCertificateBlob(encodedData))
{
- throw new Exception("Primary certificate in blob is not valid.");
+ throw new CryptographicException("Primary certificate in blob is not valid.");
}
X509Certificate2Collection collection = new X509Certificate2Collection();
@@ -447,7 +447,7 @@ namespace Opc.Ua
if (!IsValidCertificateBlob(buffer))
{
- throw new Exception("Supporting certificate in blob is not valid.");
+ throw new CryptographicException("Supporting certificate in blob is not valid.");
}
X509Certificate2 issuerCertificate = CertificateFactory.Create(buffer, true);
@@ -468,8 +468,7 @@ namespace Opc.Ua
public ICertificateStore OpenStore()
{
ICertificateStore store = CertificateStoreIdentifier.CreateStore(this.StoreType);
- Task t = Task.Run(() => store.Open(this.StorePath));
- t.Wait();
+ store.Open(this.StorePath);
return store;
}
diff --git a/Stack/Opc.Ua.Core/Security/Certificates/CertificateStoreIdentifier.cs b/Stack/Opc.Ua.Core/Security/Certificates/CertificateStoreIdentifier.cs
index 916d68a0be..eebb1bd112 100644
--- a/Stack/Opc.Ua.Core/Security/Certificates/CertificateStoreIdentifier.cs
+++ b/Stack/Opc.Ua.Core/Security/Certificates/CertificateStoreIdentifier.cs
@@ -148,8 +148,7 @@ namespace Opc.Ua
public ICertificateStore OpenStore()
{
ICertificateStore store = CreateStore(this.StoreType);
- Task t = Task.Run(() => store.Open(this.StorePath));
- t.Wait();
+ store.Open(this.StorePath);
return store;
}
diff --git a/Stack/Opc.Ua.Core/Security/Certificates/CertificateValidator.cs b/Stack/Opc.Ua.Core/Security/Certificates/CertificateValidator.cs
index 978c42c083..7ea961d71b 100644
--- a/Stack/Opc.Ua.Core/Security/Certificates/CertificateValidator.cs
+++ b/Stack/Opc.Ua.Core/Security/Certificates/CertificateValidator.cs
@@ -150,12 +150,9 @@ namespace Opc.Ua
/// Validates the specified certificate against the trust list.
///
/// The certificate.
- public virtual void Validate(X509Certificate2Collection chain)
+ public virtual void Validate(X509Certificate2 certificate)
{
- foreach (X509Certificate2 certificate in chain)
- {
- Validate(certificate);
- }
+ Validate(new X509Certificate2Collection() { certificate });
}
///
@@ -177,18 +174,20 @@ namespace Opc.Ua
///
/// The validator may be configured to ignore the application trust list and/or trust chain.
///
- public virtual void Validate(X509Certificate2 certificate)
+ public virtual void Validate(X509Certificate2Collection chain)
{
+ X509Certificate2 certificate = chain[0];
+
try
{
- Task.Run(async () =>
- {
- await InternalValidate(certificate);
- }).Wait();
-
- // add to list of validated certificates.
lock (m_lock)
- {
+ {
+ Task.Run(async () =>
+ {
+ await InternalValidate(chain);
+ }).Wait();
+
+ // add to list of validated certificates.
m_validatedCertificates[certificate.Thumbprint] = certificate;
}
}
@@ -335,6 +334,9 @@ namespace Opc.Ua
return null;
}
+ ///
+ /// Returns the authority key identifier in the certificate.
+ ///
private X509AuthorityKeyIdentifierExtension FindAuthorityKeyIdentifier(X509Certificate2 certificate)
{
for (int ii = 0; ii < certificate.Extensions.Count; ii++)
@@ -469,13 +471,11 @@ namespace Opc.Ua
issuer = await GetIssuer(certificate, collection, null, true);
}
}
- else
- {
- isTrusted = true;
- }
if (issuer != null)
{
+ isTrusted = true;
+
issuers.Add(issuer);
certificate = await issuer.Find(false);
@@ -485,6 +485,10 @@ namespace Opc.Ua
break;
}
}
+ else
+ {
+ isTrusted = false;
+ }
}
while (issuer != null);
@@ -541,7 +545,7 @@ namespace Opc.Ua
CertificateStoreIdentifier certificateStore,
bool checkRecovationStatus)
{
- string subjectName = certificate.IssuerName.ToString();
+ string subjectName = certificate.IssuerName.Name;
string keyId = null;
string serialNumber = null;
@@ -635,10 +639,12 @@ namespace Opc.Ua
///
/// Throws an exception if validation fails.
///
- /// The certificate to be checked.
- /// If certificate cannot be accepted
- protected async virtual Task InternalValidate(X509Certificate2 certificate)
+ /// The certificates to be checked.
+ /// If certificate[0] cannot be accepted
+ protected virtual async Task InternalValidate(X509Certificate2Collection certificates)
{
+ X509Certificate2 certificate = certificates[0];
+
// check for previously validated certificate.
X509Certificate2 certificate2 = null;
@@ -654,7 +660,7 @@ namespace Opc.Ua
// get the issuers (checks the revocation lists if using directory stores).
List issuers = new List();
- bool isIssuerTrusted = await GetIssuers(certificate, issuers);
+ bool isIssuerTrusted = await GetIssuers(certificates, issuers);
// setup policy chain
X509ChainPolicy policy = new X509ChainPolicy();
@@ -709,6 +715,12 @@ namespace Opc.Ua
if (ServiceResult.IsBad(result))
{
+ // check untrusted certificates.
+ if (trustedCertificate == null)
+ {
+ throw new ServiceResultException(StatusCodes.BadSecurityChecksFailed);
+ }
+
throw new ServiceResultException(result);
}
}
@@ -719,6 +731,21 @@ namespace Opc.Ua
}
}
+ bool issuedByCA = !Utils.CompareDistinguishedName(certificate.Subject, certificate.Issuer);
+
+ // check if certificate issuer is trusted.
+ if (issuedByCA && !isIssuerTrusted)
+ {
+ if (m_applicationCertificate == null || !Utils.IsEqual(m_applicationCertificate.RawData, certificate.RawData))
+ {
+ throw ServiceResultException.Create(
+ StatusCodes.BadCertificateUntrusted,
+ "Certificate issuer is not trusted.\r\nSubjectName: {0}\r\nIssuerName: {1}",
+ certificate.SubjectName.Name,
+ certificate.IssuerName.Name);
+ }
+ }
+
// check if certificate is trusted.
if (trustedCertificate == null && !isIssuerTrusted)
{
@@ -726,13 +753,14 @@ namespace Opc.Ua
{
throw ServiceResultException.Create(
StatusCodes.BadCertificateUntrusted,
- "Certificate is not trusted.\r\nSubjectName: {0}\r\nIssuerName: {1}",
- certificate.SubjectName.Name,
- certificate.IssuerName);
+ "Certificate is not trusted.\r\nSubjectName: {0}\r\nIssuerName: {1}",
+ certificate.SubjectName.Name,
+ certificate.IssuerName.Name);
}
}
}
+
///
/// Returns an object that can be used with WCF channel.
///
diff --git a/Stack/Opc.Ua.Core/Security/Certificates/DirectoryCertificateStore.cs b/Stack/Opc.Ua.Core/Security/Certificates/DirectoryCertificateStore.cs
index 0cc9fa0846..983739df0a 100644
--- a/Stack/Opc.Ua.Core/Security/Certificates/DirectoryCertificateStore.cs
+++ b/Stack/Opc.Ua.Core/Security/Certificates/DirectoryCertificateStore.cs
@@ -395,6 +395,11 @@ namespace Opc.Ua
return null;
}
+ if (string.IsNullOrEmpty(thumbprint) && string.IsNullOrEmpty(subjectName))
+ {
+ return null;
+ }
+
foreach (FileInfo file in m_certificateSubdir.GetFiles("*.der"))
{
try
@@ -403,7 +408,7 @@ namespace Opc.Ua
if (!String.IsNullOrEmpty(thumbprint))
{
- if (certificate.Thumbprint != thumbprint)
+ if (!string.Equals(certificate.Thumbprint, thumbprint, StringComparison.CurrentCultureIgnoreCase))
{
continue;
}
@@ -442,6 +447,7 @@ namespace Opc.Ua
byte[] bytes2 = rsa.Decrypt(bytes1, RSAEncryptionPadding.OaepSHA1);
if (bytes2 != null)
{
+ // Utils.Trace(1, "RSA: {0}", certificate.Thumbprint);
return certificate;
}
}
diff --git a/Stack/Opc.Ua.Core/Security/Certificates/X509AuthorityKeyIdentifierExtension.cs b/Stack/Opc.Ua.Core/Security/Certificates/X509AuthorityKeyIdentifierExtension.cs
index 1ebfa3b14b..8816980465 100644
--- a/Stack/Opc.Ua.Core/Security/Certificates/X509AuthorityKeyIdentifierExtension.cs
+++ b/Stack/Opc.Ua.Core/Security/Certificates/X509AuthorityKeyIdentifierExtension.cs
@@ -160,7 +160,6 @@ namespace Opc.Ua
public string KeyId
{
get { return m_keyId; }
- set { m_keyId = value; }
}
///
@@ -169,7 +168,6 @@ namespace Opc.Ua
public string[] AuthorityNames
{
get { return m_authorityNames; }
- set { m_authorityNames = value; }
}
///
@@ -178,7 +176,6 @@ namespace Opc.Ua
public string SerialNumber
{
get { return m_serialNumber; }
- set { m_serialNumber = value; }
}
#endregion
diff --git a/Stack/Opc.Ua.Core/Security/Certificates/X509CRL.cs b/Stack/Opc.Ua.Core/Security/Certificates/X509CRL.cs
index c8909a100d..830dfe4188 100644
--- a/Stack/Opc.Ua.Core/Security/Certificates/X509CRL.cs
+++ b/Stack/Opc.Ua.Core/Security/Certificates/X509CRL.cs
@@ -29,8 +29,11 @@ using System.Runtime.InteropServices;
using System.Security.Cryptography.X509Certificates;
using System.IO;
+using Org.BouncyCastle.X509;
+
namespace Opc.Ua
{
+
///
/// Provides access to an X509 CRL object.
///
@@ -110,9 +113,9 @@ namespace Opc.Ua
///
public bool VerifySignature(X509Certificate2 issuer, bool throwOnError)
{
- //TODO
-
m_issuer = issuer;
+ Org.BouncyCastle.X509.X509Certificate bccert = new X509CertificateParser().ReadCertificate(issuer.RawData);
+ m_crl.Verify(bccert.GetPublicKey());
return true;
}
@@ -127,22 +130,25 @@ namespace Opc.Ua
throw new ServiceResultException(StatusCodes.BadCertificateInvalid, "Certificate was not created by the CRL issuer.");
}
- // TODO: get the cert info for the target certificate and check revocation.
-
- // not revoked.
- return false;
+ Org.BouncyCastle.X509.X509Certificate bccert = new X509CertificateParser().ReadCertificate(certificate.RawData);
+ return m_crl.IsRevoked(bccert);
}
#endregion
#region Private Methods
private void Initialize(byte[] crl)
{
-
- }
+ X509CrlParser parser = new X509CrlParser();
+ m_crl = parser.ReadCrl(crl);
+ UpdateTime = (m_crl.ThisUpdate == null) ? DateTime.MinValue : m_crl.ThisUpdate;
+ NextUpdateTime = (m_crl.NextUpdate == null) ? DateTime.MinValue : m_crl.NextUpdate.Value;
+ Issuer = m_crl.IssuerDN.ToString();
+ }
#endregion
#region Private Fields
- private X509Certificate2 m_issuer;
- #endregion
+ X509Certificate2 m_issuer;
+ private X509Crl m_crl;
+ #endregion
}
}
diff --git a/Stack/Opc.Ua.Core/project.json b/Stack/Opc.Ua.Core/project.json
index e8147fd6fe..be6eb77546 100644
--- a/Stack/Opc.Ua.Core/project.json
+++ b/Stack/Opc.Ua.Core/project.json
@@ -1,13 +1,14 @@
{
"version": "1.0.0-*",
- "dependencies": {
- "NETStandard.Library": "1.6.0",
- "Newtonsoft.Json": "9.0.1",
- "System.Data.Common": "4.1.0",
- "System.Private.ServiceModel": "4.1.0",
- "System.ServiceModel.Primitives": "4.1.0"
- },
+ "dependencies": {
+ "NETStandard.Library": "1.6.0",
+ "Newtonsoft.Json": "9.0.1",
+ "Portable.BouncyCastle": "1.8.1.1",
+ "System.Data.Common": "4.1.0",
+ "System.Private.ServiceModel": "4.1.0",
+ "System.ServiceModel.Primitives": "4.1.0"
+ },
"frameworks": {
"net46": {