public async Task VerifyAppCertX509Store(string storePath) { var appCertificate = GetTestCert(); Assert.NotNull(appCertificate); Assert.True(appCertificate.HasPrivateKey); appCertificate.AddToStore( CertificateStoreType.X509Store, storePath ); using (var publicKey = new X509Certificate2(appCertificate.RawData)) { Assert.NotNull(publicKey); Assert.False(publicKey.HasPrivateKey); var id = new CertificateIdentifier() { Thumbprint = publicKey.Thumbprint, StorePath = storePath, StoreType = CertificateStoreType.X509Store }; var privateKey = await id.LoadPrivateKey(null).ConfigureAwait(false); Assert.NotNull(privateKey); Assert.True(privateKey.HasPrivateKey); X509Utils.VerifyRSAKeyPair(publicKey, privateKey, true); using (var x509Store = new X509CertificateStore()) { x509Store.Open(storePath); await x509Store.Delete(publicKey.Thumbprint).ConfigureAwait(false); } } }
/// <summary> /// load the authority signing key. /// </summary> public virtual async Task <X509Certificate2> LoadSigningKeyAsync(X509Certificate2 signingCertificate, string signingKeyPassword) { CertificateIdentifier certIdentifier = new CertificateIdentifier(signingCertificate) { StorePath = m_authoritiesStorePath, StoreType = m_authoritiesStoreType }; return(await certIdentifier.LoadPrivateKey(signingKeyPassword)); }
/// <summary> /// Creates a new certificate for application. /// </summary> /// <param name="application">The application.</param> private static async Task <X509Certificate2> CreateCertificateForApplication(InstalledApplication application) { // build list of domains. List <string> domains = new List <string>(); if (application.BaseAddresses != null) { foreach (string baseAddress in application.BaseAddresses) { Uri uri = Utils.ParseUri(baseAddress); if (uri != null) { string domain = uri.DnsSafeHost; if (String.Compare(domain, "localhost", StringComparison.OrdinalIgnoreCase) == 0) { domain = Utils.GetHostName(); } if (!Utils.FindStringIgnoreCase(domains, domain)) { domains.Add(domain); } } } } // must at least of the localhost. if (domains.Count == 0) { domains.Add(Utils.GetHostName()); } // create the certificate. X509Certificate2 certificate = await Opc.Ua.CertificateFactory.CreateCertificate( application.ApplicationCertificate.StoreType, application.ApplicationCertificate.StorePath, application.ApplicationUri, application.ApplicationName, Utils.Format("CN={0}/DC={1}", application.ApplicationName, domains[0]), domains, 1024, 300); CertificateIdentifier applicationCertificate = Opc.Ua.Security.SecuredApplication.FromCertificateIdentifier(application.ApplicationCertificate); return(await applicationCertificate.LoadPrivateKey(null)); }
/// <summary> /// Revoke the CA signed certificate. /// The issuer CA public key, the private key and the crl reside in the storepath. /// The CRL number is increased by one and existing CRL for the issuer are deleted from the store. /// </summary> public static async Task <X509CRL> RevokeCertificateAsync( string storePath, X509Certificate2 certificate, string issuerKeyFilePassword = null ) { X509CRL updatedCRL = null; try { string subjectName = certificate.IssuerName.Name; string keyId = null; string serialNumber = null; // caller may want to create empty CRL using the CA cert itself bool isCACert = IsCertificateAuthority(certificate); // find the authority key identifier. X509AuthorityKeyIdentifierExtension authority = FindAuthorityKeyIdentifier(certificate); if (authority != null) { keyId = authority.KeyId; serialNumber = authority.SerialNumber; } else { throw new ArgumentException("Certificate does not contain an Authority Key"); } if (!isCACert) { if (serialNumber == certificate.SerialNumber || Utils.CompareDistinguishedName(certificate.Subject, certificate.Issuer)) { throw new ServiceResultException(StatusCodes.BadCertificateInvalid, "Cannot revoke self signed certificates"); } } X509Certificate2 certCA = null; using (ICertificateStore store = CertificateStoreIdentifier.OpenStore(storePath)) { if (store == null) { throw new ArgumentException("Invalid store path/type"); } certCA = await FindIssuerCABySerialNumberAsync(store, certificate.Issuer, serialNumber); if (certCA == null) { throw new ServiceResultException(StatusCodes.BadCertificateInvalid, "Cannot find issuer certificate in store."); } if (!certCA.HasPrivateKey) { throw new ServiceResultException(StatusCodes.BadCertificateInvalid, "Issuer certificate has no private key, cannot revoke certificate."); } CertificateIdentifier certCAIdentifier = new CertificateIdentifier(certCA); certCAIdentifier.StorePath = storePath; certCAIdentifier.StoreType = CertificateStoreIdentifier.DetermineStoreType(storePath); X509Certificate2 certCAWithPrivateKey = await certCAIdentifier.LoadPrivateKey(issuerKeyFilePassword); if (certCAWithPrivateKey == null) { throw new ServiceResultException(StatusCodes.BadCertificateInvalid, "Failed to load issuer private key. Is the password correct?"); } List <X509CRL> certCACrl = store.EnumerateCRLs(certCA, false); using (var cfrg = new CertificateFactoryRandomGenerator()) { // cert generators SecureRandom random = new SecureRandom(cfrg); BigInteger crlSerialNumber = BigInteger.Zero; Org.BouncyCastle.X509.X509Certificate bcCertCA = new X509CertificateParser().ReadCertificate(certCA.RawData); AsymmetricKeyParameter signingKey = GetPrivateKeyParameter(certCAWithPrivateKey); ISignatureFactory signatureFactory = new Asn1SignatureFactory(GetRSAHashAlgorithm(defaultHashSize), signingKey, random); X509V2CrlGenerator crlGen = new X509V2CrlGenerator(); crlGen.SetIssuerDN(bcCertCA.IssuerDN); crlGen.SetThisUpdate(DateTime.UtcNow); crlGen.SetNextUpdate(DateTime.UtcNow.AddMonths(12)); // merge all existing revocation list X509CrlParser parser = new X509CrlParser(); foreach (X509CRL caCrl in certCACrl) { X509Crl crl = parser.ReadCrl(caCrl.RawData); crlGen.AddCrl(crl); var crlVersion = GetCrlNumber(crl); if (crlVersion.IntValue > crlSerialNumber.IntValue) { crlSerialNumber = crlVersion; } } if (isCACert) { // add a dummy revoked cert crlGen.AddCrlEntry(BigInteger.One, DateTime.UtcNow, CrlReason.Superseded); } else { // add the revoked cert crlGen.AddCrlEntry(GetSerialNumber(certificate), DateTime.UtcNow, CrlReason.PrivilegeWithdrawn); } crlGen.AddExtension(X509Extensions.AuthorityKeyIdentifier, false, new AuthorityKeyIdentifierStructure(bcCertCA)); // set new serial number crlSerialNumber = crlSerialNumber.Add(BigInteger.One); crlGen.AddExtension(X509Extensions.CrlNumber, false, new CrlNumber(crlSerialNumber)); // generate updated CRL X509Crl updatedCrl = crlGen.Generate(signatureFactory); // add updated CRL to store updatedCRL = new X509CRL(updatedCrl.GetEncoded()); store.AddCRL(updatedCRL); // delete outdated CRLs from store foreach (X509CRL caCrl in certCACrl) { store.DeleteCRL(caCrl); } } store.Close(); } } catch (Exception e) { throw e; } return(updatedCRL); }
private async void CertificateRequestTimer_Tick(object sender, EventArgs e) { try { NodeId requestId = NodeId.Parse(m_application.CertificateRequestId); byte[] privateKeyPFX = null; byte[][] issuerCertificates = null; byte[] certificate = m_gds.FinishRequest( m_application.ApplicationId, requestId, out privateKeyPFX, out issuerCertificates); if (certificate == null) { // request not done yet, try again in a few seconds return; } CertificateRequestTimer.Enabled = false; RequestProgressLabel.Visible = false; if (m_application.RegistrationType != RegistrationType.ServerPush) { X509Certificate2 newCert = new X509Certificate2(certificate); if (!String.IsNullOrEmpty(m_application.CertificateStorePath) && !String.IsNullOrEmpty(m_application.CertificateSubjectName)) { CertificateIdentifier cid = new CertificateIdentifier() { StorePath = m_application.CertificateStorePath, StoreType = CertificateStoreIdentifier.DetermineStoreType(m_application.CertificateStorePath), SubjectName = m_application.CertificateSubjectName.Replace("localhost", Utils.GetHostName()) }; // update store using (var store = CertificateStoreIdentifier.OpenStore(m_application.CertificateStorePath)) { // if we used a CSR, we already have a private key and therefore didn't request one from the GDS // in this case, privateKey is null if (privateKeyPFX == null) { X509Certificate2 oldCertificate = await cid.Find(true); if (oldCertificate != null && oldCertificate.HasPrivateKey) { oldCertificate = await cid.LoadPrivateKey(string.Empty); newCert = CertificateFactory.CreateCertificateWithPrivateKey(newCert, oldCertificate); await store.Delete(oldCertificate.Thumbprint); } else { throw new ServiceResultException("Failed to merge signed certificate with the private key."); } } else { newCert = new X509Certificate2(privateKeyPFX, string.Empty, X509KeyStorageFlags.Exportable); newCert = CertificateFactory.Load(newCert, true); } await store.Add(newCert); } } else { DialogResult result = DialogResult.Yes; string absoluteCertificatePublicKeyPath = Utils.GetAbsoluteFilePath(m_application.CertificatePublicKeyPath, true, false, false) ?? m_application.CertificatePublicKeyPath; FileInfo file = new FileInfo(absoluteCertificatePublicKeyPath); if (file.Exists) { result = MessageBox.Show( Parent, "Replace certificate " + absoluteCertificatePublicKeyPath + "?", Parent.Text, MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation); } if (result == DialogResult.Yes) { byte[] exportedCert; if (string.Compare(file.Extension, ".PEM", true) == 0) { exportedCert = CertificateFactory.ExportCertificateAsPEM(newCert); } else { exportedCert = newCert.Export(X509ContentType.Cert); } File.WriteAllBytes(absoluteCertificatePublicKeyPath, exportedCert); } // if we provided a PFX or P12 with the private key, we need to merge the new cert with the private key if (m_application.GetPrivateKeyFormat(m_server?.GetSupportedKeyFormats()) == "PFX") { string absoluteCertificatePrivateKeyPath = Utils.GetAbsoluteFilePath(m_application.CertificatePrivateKeyPath, true, false, false) ?? m_application.CertificatePrivateKeyPath; file = new FileInfo(absoluteCertificatePrivateKeyPath); if (file.Exists) { result = MessageBox.Show( Parent, "Replace private key " + absoluteCertificatePrivateKeyPath + "?", Parent.Text, MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation); } if (result == DialogResult.Yes) { if (file.Exists) { byte[] pkcsData = File.ReadAllBytes(absoluteCertificatePrivateKeyPath); X509Certificate2 oldCertificate = CertificateFactory.CreateCertificateFromPKCS12(pkcsData, m_certificatePassword); newCert = CertificateFactory.CreateCertificateWithPrivateKey(newCert, oldCertificate); pkcsData = newCert.Export(X509ContentType.Pfx, m_certificatePassword); File.WriteAllBytes(absoluteCertificatePrivateKeyPath, pkcsData); if (privateKeyPFX != null) { throw new ServiceResultException("Did not expect a private key for this operation."); } } else { File.WriteAllBytes(absoluteCertificatePrivateKeyPath, privateKeyPFX); } } } } // update trust list. if (!String.IsNullOrEmpty(m_application.TrustListStorePath)) { using (ICertificateStore store = CertificateStoreIdentifier.OpenStore(m_application.TrustListStorePath)) { foreach (byte[] issuerCertificate in issuerCertificates) { X509Certificate2 x509 = new X509Certificate2(issuerCertificate); X509Certificate2Collection certs = await store.FindByThumbprint(x509.Thumbprint); if (certs.Count == 0) { await store.Add(new X509Certificate2(issuerCertificate)); } } } } m_certificate = newCert; } else { if (privateKeyPFX != null && privateKeyPFX.Length > 0) { var x509 = new X509Certificate2(privateKeyPFX, m_certificatePassword, X509KeyStorageFlags.Exportable); privateKeyPFX = x509.Export(X509ContentType.Pfx); } bool applyChanges = m_server.UpdateCertificate( null, m_server.ApplicationCertificateType, certificate, (privateKeyPFX != null) ? "PFX" : null, privateKeyPFX, issuerCertificates); if (applyChanges) { MessageBox.Show( Parent, "The certificate was updated, however, the apply changes command must be sent before the server will use the new certificate.", Parent.Text, MessageBoxButtons.OK, MessageBoxIcon.Information); ApplyChangesButton.Enabled = true; } } CertificateControl.ShowValue(null, "Application Certificate", new CertificateWrapper() { Certificate = m_certificate }, true); } catch (Exception exception) { if (exception is ServiceResultException sre && sre.StatusCode == StatusCodes.BadNothingToDo) { return; } RequestProgressLabel.Visible = false; CertificateRequestTimer.Enabled = false; Opc.Ua.Client.Controls.ExceptionDlg.Show(Text, exception); } }
private async Task RequestNewCertificatePullMode(object sender, EventArgs e) { try { // check if we already have a private key NodeId requestId = null; if (!string.IsNullOrEmpty(m_application.CertificateStorePath)) { CertificateIdentifier id = new CertificateIdentifier { StoreType = CertificateStoreIdentifier.DetermineStoreType(m_application.CertificateStorePath), StorePath = m_application.CertificateStorePath, SubjectName = m_application.CertificateSubjectName.Replace("localhost", Utils.GetHostName()) }; m_certificate = await id.Find(true); if (m_certificate != null && m_certificate.HasPrivateKey) { m_certificate = await id.LoadPrivateKey(m_certificatePassword); } } bool hasPrivateKeyFile = false; if (!string.IsNullOrEmpty(m_application.CertificatePrivateKeyPath)) { FileInfo file = new FileInfo(m_application.CertificatePrivateKeyPath); hasPrivateKeyFile = file.Exists; } var domainNames = m_application.GetDomainNames(m_certificate); if (m_certificate == null) { // no private key requestId = m_gds.StartNewKeyPairRequest( m_application.ApplicationId, null, null, m_application.CertificateSubjectName.Replace("localhost", Utils.GetHostName()), domainNames, "PFX", m_certificatePassword); } else { X509Certificate2 csrCertificate = null; if (m_certificate.HasPrivateKey) { csrCertificate = m_certificate; } else { string absoluteCertificatePrivateKeyPath = Utils.GetAbsoluteFilePath(m_application.CertificatePrivateKeyPath, true, false, false); byte[] pkcsData = File.ReadAllBytes(absoluteCertificatePrivateKeyPath); if (m_application.GetPrivateKeyFormat(m_server?.GetSupportedKeyFormats()) == "PFX") { csrCertificate = CertificateFactory.CreateCertificateFromPKCS12(pkcsData, m_certificatePassword); } else { csrCertificate = CertificateFactory.CreateCertificateWithPEMPrivateKey(m_certificate, pkcsData, m_certificatePassword); } } byte[] certificateRequest = CertificateFactory.CreateSigningRequest(csrCertificate, domainNames); requestId = m_gds.StartSigningRequest(m_application.ApplicationId, null, null, certificateRequest); } m_application.CertificateRequestId = requestId.ToString(); CertificateRequestTimer.Enabled = true; RequestProgressLabel.Visible = true; WarningLabel.Visible = false; } catch (Exception ex) { Opc.Ua.Client.Controls.ExceptionDlg.Show(Text, ex); } }
public async Task VerifyAppCertDirectoryStore() { var appCertificate = GetTestCert(); Assert.NotNull(appCertificate); Assert.True(appCertificate.HasPrivateKey); string password = Guid.NewGuid().ToString(); // pki directory root for app cert var pkiRoot = Path.GetTempPath() + Path.GetRandomFileName() + Path.DirectorySeparatorChar; var storePath = pkiRoot + "own"; var storeType = CertificateStoreType.Directory; appCertificate.AddToStore( storeType, storePath, password ); using (var publicKey = new X509Certificate2(appCertificate.RawData)) { Assert.NotNull(publicKey); Assert.False(publicKey.HasPrivateKey); var id = new CertificateIdentifier() { Thumbprint = publicKey.Thumbprint, StorePath = storePath, StoreType = storeType }; { // check no password fails to load var nullKey = await id.LoadPrivateKey(null).ConfigureAwait(false); Assert.IsNull(nullKey); } { // check invalid password fails to load var nullKey = await id.LoadPrivateKey("123").ConfigureAwait(false); Assert.IsNull(nullKey); } { // check invalid password fails to load var nullKey = await id.LoadPrivateKeyEx(new CertificatePasswordProvider("123")).ConfigureAwait(false); Assert.IsNull(nullKey); } var privateKey = await id.LoadPrivateKeyEx(new CertificatePasswordProvider(password)).ConfigureAwait(false); Assert.NotNull(privateKey); Assert.True(privateKey.HasPrivateKey); X509Utils.VerifyRSAKeyPair(publicKey, privateKey, true); using (ICertificateStore store = Opc.Ua.CertificateStoreIdentifier.CreateStore(storeType)) { store.Open(storePath); await store.Delete(publicKey.Thumbprint).ConfigureAwait(false); } } }
/// <summary> /// Revoke the CA signed certificate. /// The issuer CA public key, the private key and the crl reside in the storepath. /// The CRL number is increased by one and existing CRL for the issuer are deleted from the store. /// </summary> public static async Task <X509CRL> RevokeCertificateAsync( string storePath, X509Certificate2 certificate, string issuerKeyFilePassword = null ) { X509CRL updatedCRL = null; string subjectName = certificate.IssuerName.Name; string keyId = null; string serialNumber = null; // caller may want to create empty CRL using the CA cert itself bool isCACert = X509Utils.IsCertificateAuthority(certificate); // find the authority key identifier. X509AuthorityKeyIdentifierExtension authority = X509Extensions.FindExtension <X509AuthorityKeyIdentifierExtension>(certificate); if (authority != null) { keyId = authority.KeyIdentifier; serialNumber = authority.SerialNumber; } else { throw new ArgumentException("Certificate does not contain an Authority Key"); } if (!isCACert) { if (serialNumber == certificate.SerialNumber || X509Utils.CompareDistinguishedName(certificate.Subject, certificate.Issuer)) { throw new ServiceResultException(StatusCodes.BadCertificateInvalid, "Cannot revoke self signed certificates"); } } X509Certificate2 certCA = null; using (ICertificateStore store = CertificateStoreIdentifier.OpenStore(storePath)) { if (store == null) { throw new ArgumentException("Invalid store path/type"); } certCA = await X509Utils.FindIssuerCABySerialNumberAsync(store, certificate.Issuer, serialNumber).ConfigureAwait(false); if (certCA == null) { throw new ServiceResultException(StatusCodes.BadCertificateInvalid, "Cannot find issuer certificate in store."); } if (!certCA.HasPrivateKey) { throw new ServiceResultException(StatusCodes.BadCertificateInvalid, "Issuer certificate has no private key, cannot revoke certificate."); } CertificateIdentifier certCAIdentifier = new CertificateIdentifier(certCA) { StorePath = storePath, StoreType = CertificateStoreIdentifier.DetermineStoreType(storePath) }; X509Certificate2 certCAWithPrivateKey = await certCAIdentifier.LoadPrivateKey(issuerKeyFilePassword).ConfigureAwait(false); if (certCAWithPrivateKey == null) { throw new ServiceResultException(StatusCodes.BadCertificateInvalid, "Failed to load issuer private key. Is the password correct?"); } List <X509CRL> certCACrl = store.EnumerateCRLs(certCA, false); var certificateCollection = new X509Certificate2Collection() { }; if (!isCACert) { certificateCollection.Add(certificate); } updatedCRL = CertificateFactory.RevokeCertificate(certCAWithPrivateKey, certCACrl, certificateCollection); store.AddCRL(updatedCRL); // delete outdated CRLs from store foreach (X509CRL caCrl in certCACrl) { store.DeleteCRL(caCrl); } store.Close(); } return(updatedCRL); }