diff --git a/SampleApplications/Samples/GDS/ClientTest/ClientTest.cs b/SampleApplications/Samples/GDS/ClientTest/ClientTest.cs index 783d23e562..edc193b92a 100644 --- a/SampleApplications/Samples/GDS/ClientTest/ClientTest.cs +++ b/SampleApplications/Samples/GDS/ClientTest/ClientTest.cs @@ -31,13 +31,13 @@ using System; using System.Collections.Generic; using System.Text.RegularExpressions; using System.Threading; +using System.Security.Cryptography.X509Certificates; using NUnit.Framework; using Opc.Ua; using Opc.Ua.Gds; using Opc.Ua.Gds.Client; using Opc.Ua.Gds.Test; using Opc.Ua.Test; -using System.Security.Cryptography.X509Certificates; namespace NUnit.Opc.Ua.Gds.Test { diff --git a/Stack/Opc.Ua.Core/Security/Certificates/CertificateFactory.cs b/Stack/Opc.Ua.Core/Security/Certificates/CertificateFactory.cs index 289de97480..2b383fa65b 100644 --- a/Stack/Opc.Ua.Core/Security/Certificates/CertificateFactory.cs +++ b/Stack/Opc.Ua.Core/Security/Certificates/CertificateFactory.cs @@ -697,7 +697,7 @@ public class CertificateFactory /// public static X509Certificate2 CreateCertificateWithPEMPrivateKey( X509Certificate2 certificate, - byte [] pemDataBlob, + byte[] pemDataBlob, string password = null) { AsymmetricKeyParameter privateKey = null; @@ -727,7 +727,7 @@ public class CertificateFactory AsymmetricCipherKeyPair keypair = pemObject as AsymmetricCipherKeyPair; if (keypair != null) - { + { privateKey = keypair.Private; break; } @@ -791,24 +791,6 @@ public class CertificateFactory } } - - private class Password - : IPasswordFinder - { - private readonly char[] password; - - public Password( - char[] word) - { - this.password = (char[])word.Clone(); - } - - public char[] GetPassword() - { - return (char[])password.Clone(); - } - } - /// /// Verify the signature of a self signed certificate. /// @@ -825,7 +807,66 @@ public class CertificateFactory } return true; } -#endregion + + public static X509KeyUsageFlags GetKeyUsage(X509Certificate2 cert) + { + X509KeyUsageFlags allFlags = X509KeyUsageFlags.None; + foreach (X509KeyUsageExtension ext in cert.Extensions.OfType()) + { + allFlags |= ext.KeyUsages; + } + return allFlags; + } + + /// + /// Verify RSA key pair of two certificates. + /// + public static bool VerifyRSAKeyPair( + X509Certificate2 certWithPublicKey, + X509Certificate2 certWithPrivateKey, + bool throwOnError = false) + { + bool result = false; + try + { + // verify the public and private key match + using (RSA rsaPrivateKey = certWithPrivateKey.GetRSAPrivateKey()) + { + using (RSA rsaPublicKey = certWithPublicKey.GetRSAPublicKey()) + { + X509KeyUsageFlags keyUsage = GetKeyUsage(certWithPublicKey); + if ((keyUsage & X509KeyUsageFlags.DataEncipherment) != 0) + { + result = VerifyRSAKeyPairCrypt(rsaPublicKey, rsaPrivateKey); + } + else if ((keyUsage & X509KeyUsageFlags.DigitalSignature) != 0) + { + result = VerifyRSAKeyPairSign(rsaPublicKey, rsaPrivateKey); + } + else + { + throw new CryptographicException("Don't know how to verify the public/private key pair."); + } + } + } + } + catch (Exception e) + { + if (throwOnError) + { + throw e; + } + } + finally + { + if (!result && throwOnError) + { + throw new CryptographicException("The public/private key pair in the certficates do not match."); + } + } + return result; + } + #endregion #region Private Methods /// @@ -962,10 +1003,10 @@ public class CertificateFactory /// /// private static List CreateSubjectAlternateNameDomains(IList domainNames) - { + { // subject alternate name List generalNames = new List(); - for (int i = 0; i private static async Task FindIssuerCABySerialNumberAsync( - ICertificateStore store, - string issuer, + ICertificateStore store, + string issuer, string serialnumber) { X509Certificate2Collection certificates = await store.Enumerate(); @@ -1185,50 +1226,52 @@ public class CertificateFactory return string.Empty; } - /// - /// Verify RSA key pair of two certificates. - /// - private static bool VerifyRSAKeyPair( - X509Certificate2 certWithPublicKey, - X509Certificate2 certWithPrivateKey, - bool throwOnError = false) + + private static bool VerifyRSAKeyPairCrypt( + RSA rsaPublicKey, + RSA rsaPrivateKey) { - bool result = false; - try + Opc.Ua.Test.RandomSource randomSource = new Opc.Ua.Test.RandomSource(); + int blockSize = RsaUtils.GetPlainTextBlockSize(rsaPrivateKey, true); + byte[] testBlock = new byte[blockSize]; + randomSource.NextBytes(testBlock, 0, blockSize); + byte[] encryptedBlock = rsaPublicKey.Encrypt(testBlock, RSAEncryptionPadding.OaepSHA1); + byte[] decryptedBlock = rsaPrivateKey.Decrypt(encryptedBlock, RSAEncryptionPadding.OaepSHA1); + if (decryptedBlock != null) { - // verify the public and private key match - using (RSA rsaPrivateKey = certWithPrivateKey.GetRSAPrivateKey()) - { - using (RSA rsaPublicKey = certWithPublicKey.GetRSAPublicKey()) - { - Opc.Ua.Test.RandomSource randomSource = new Opc.Ua.Test.RandomSource(); - int blockSize = RsaUtils.GetPlainTextBlockSize(rsaPrivateKey, true); - byte[] testBlock = new byte[blockSize]; - randomSource.NextBytes(testBlock, 0, blockSize); - byte[] encryptedBlock = rsaPublicKey.Encrypt(testBlock, RSAEncryptionPadding.OaepSHA1); - byte[] decryptedBlock = rsaPrivateKey.Decrypt(encryptedBlock, RSAEncryptionPadding.OaepSHA1); - if (decryptedBlock != null) - { - result = Utils.IsEqual(testBlock, decryptedBlock); - } - } - } + return Utils.IsEqual(testBlock, decryptedBlock); } - catch (Exception e) + return false; + } + + private static bool VerifyRSAKeyPairSign( + RSA rsaPublicKey, + RSA rsaPrivateKey) + { + Opc.Ua.Test.RandomSource randomSource = new Opc.Ua.Test.RandomSource(); + int blockSize = RsaUtils.GetPlainTextBlockSize(rsaPrivateKey, true); + byte[] testBlock = new byte[blockSize]; + randomSource.NextBytes(testBlock, 0, blockSize); + byte[] signature = rsaPrivateKey.SignData(testBlock, HashAlgorithmName.SHA256, RSASignaturePadding.Pss); + return rsaPublicKey.VerifyData(testBlock, signature, HashAlgorithmName.SHA256, RSASignaturePadding.Pss); + } + + + private class Password + : IPasswordFinder + { + private readonly char[] password; + + public Password( + char[] word) { - if (throwOnError) - { - throw e; - } + this.password = (char[])word.Clone(); } - finally - { - if (!result && throwOnError) - { - throw new CryptographicException("The public/private key pair in the certficates do not match."); - } + + public char[] GetPassword() + { + return (char[])password.Clone(); } - return result; } #endregion diff --git a/Stack/Opc.Ua.Core/Security/Certificates/DirectoryCertificateStore.cs b/Stack/Opc.Ua.Core/Security/Certificates/DirectoryCertificateStore.cs index 499da901a4..ee4bdc05c7 100644 --- a/Stack/Opc.Ua.Core/Security/Certificates/DirectoryCertificateStore.cs +++ b/Stack/Opc.Ua.Core/Security/Certificates/DirectoryCertificateStore.cs @@ -310,7 +310,6 @@ namespace Opc.Ua filePath.Append(fileRoot); FileInfo privateKeyFile = new FileInfo(filePath.ToString() + ".pfx"); - RSA rsa = null; password = password ?? String.Empty; try { @@ -318,7 +317,10 @@ namespace Opc.Ua privateKeyFile.FullName, password, X509KeyStorageFlags.Exportable | X509KeyStorageFlags.UserKeySet); - rsa = certificate.GetRSAPrivateKey(); + if (CertificateFactory.VerifyRSAKeyPair(certificate, certificate, true)) + { + return certificate; + } } catch (Exception) { @@ -326,17 +328,8 @@ namespace Opc.Ua privateKeyFile.FullName, password, X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet); - rsa = certificate.GetRSAPrivateKey(); - } - if (rsa != null) - { - int inputBlockSize = RsaUtils.GetPlainTextBlockSize(rsa, true); - byte[] bytes1 = rsa.Encrypt(new byte[inputBlockSize], RSAEncryptionPadding.OaepSHA1); - byte[] bytes2 = rsa.Decrypt(bytes1, RSAEncryptionPadding.OaepSHA1); - rsa.Dispose(); - if (bytes2 != null) + if (CertificateFactory.VerifyRSAKeyPair(certificate, certificate, true)) { - // Utils.Trace(1, "RSA: {0}", certificate.Thumbprint); return certificate; } }