/// <summary> /// Create a X509Certificate2 with a private key by combining /// the new certificate with a private key from an existing certificate /// </summary> public static X509Certificate2 CreateCertificateWithPrivateKey( X509Certificate2 certificate, X509Certificate2 certificateWithPrivateKey) { if (!certificateWithPrivateKey.HasPrivateKey) { throw new NotSupportedException("Need a certificate with a private key."); } if (!X509Utils.VerifyRSAKeyPair(certificate, certificateWithPrivateKey)) { throw new NotSupportedException("The public and the private key pair doesn't match."); } string passcode = X509Utils.GeneratePasscode(); using (RSA rsaPrivateKey = certificateWithPrivateKey.GetRSAPrivateKey()) { byte[] pfxData = CertificateBuilder.CreatePfxWithRSAPrivateKey( certificate, certificate.FriendlyName, rsaPrivateKey, passcode); return(X509Utils.CreateCertificateFromPKCS12(pfxData, passcode)); } }
/// <summary> /// Returns the file name to use for the certificate. /// </summary> private string GetFileName(X509Certificate2 certificate) { // build file name. string commonName = certificate.FriendlyName; List <string> names = X509Utils.ParseDistinguishedName(certificate.Subject); for (int ii = 0; ii < names.Count; ii++) { if (names[ii].StartsWith("CN=")) { commonName = names[ii].Substring(3).Trim(); break; } } StringBuilder fileName = new StringBuilder(); // remove any special characters. for (int ii = 0; ii < commonName.Length; ii++) { char ch = commonName[ii]; if ("<>:\"/\\|?*".IndexOf(ch) != -1) { ch = '+'; } fileName.Append(ch); } fileName.Append(" ["); fileName.Append(certificate.Thumbprint); fileName.Append(']'); return(fileName.ToString()); }
/// <summary> /// Called before the server starts. /// </summary> /// <param name="configuration">The object that stores the configurable configuration information for a UA application.</param> protected virtual void OnServerStarting(ApplicationConfiguration configuration) { // fetch properties and configuration. Configuration = configuration; ServerProperties = LoadServerProperties(); // ensure at least one security policy exists. if (configuration.ServerConfiguration != null) { if (configuration.ServerConfiguration.SecurityPolicies.Count == 0) { configuration.ServerConfiguration.SecurityPolicies.Add(new ServerSecurityPolicy()); } // ensure at least one user token policy exists. if (configuration.ServerConfiguration.UserTokenPolicies.Count == 0) { UserTokenPolicy userTokenPolicy = new UserTokenPolicy(); userTokenPolicy.TokenType = UserTokenType.Anonymous; userTokenPolicy.PolicyId = userTokenPolicy.TokenType.ToString(); configuration.ServerConfiguration.UserTokenPolicies.Add(userTokenPolicy); } } // load the instance certificate. if (configuration.SecurityConfiguration.ApplicationCertificate != null) { InstanceCertificate = configuration.SecurityConfiguration.ApplicationCertificate.Find(true).Result; } if (InstanceCertificate == null) { throw new ServiceResultException( StatusCodes.BadConfigurationError, "Server does not have an instance certificate assigned."); } if (!InstanceCertificate.HasPrivateKey) { throw new ServiceResultException( StatusCodes.BadConfigurationError, "Server does not have access to the private key for the instance certificate."); } // load certificate chain. InstanceCertificateChain = new X509Certificate2Collection(InstanceCertificate); List <CertificateIdentifier> issuers = new List <CertificateIdentifier>(); configuration.CertificateValidator.GetIssuers(InstanceCertificateChain, issuers).Wait(); for (int i = 0; i < issuers.Count; i++) { InstanceCertificateChain.Add(issuers[i].Certificate); } // use the message context from the configuration to ensure the channels are using the same one. MessageContext = configuration.CreateMessageContext(); // assign a unique identifier if none specified. if (String.IsNullOrEmpty(configuration.ApplicationUri)) { configuration.ApplicationUri = X509Utils.GetApplicationUriFromCertificate(InstanceCertificate); if (String.IsNullOrEmpty(configuration.ApplicationUri)) { configuration.ApplicationUri = Utils.Format( "http://{0}/{1}/{2}", Utils.GetHostName(), configuration.ApplicationName, Guid.NewGuid()); } } // initialize namespace table. MessageContext.NamespaceUris = new NamespaceTable(); MessageContext.NamespaceUris.Append(configuration.ApplicationUri); // assign an instance name. if (String.IsNullOrEmpty(configuration.ApplicationName) && InstanceCertificate != null) { configuration.ApplicationName = InstanceCertificate.GetNameInfo(X509NameType.DnsName, false); } // save the certificate validator. CertificateValidator = configuration.CertificateValidator; }
/// <summary> /// Sets the parameters to suitable defaults. /// </summary> private static void SetSuitableDefaults( ref string applicationUri, ref string applicationName, ref string subjectName, ref IList <String> domainNames) { // parse the subject name if specified. List <string> subjectNameEntries = null; if (!String.IsNullOrEmpty(subjectName)) { subjectNameEntries = X509Utils.ParseDistinguishedName(subjectName); } // check the application name. if (String.IsNullOrEmpty(applicationName)) { if (subjectNameEntries == null) { throw new ArgumentNullException(nameof(applicationName), "Must specify a applicationName or a subjectName."); } // use the common name as the application name. for (int ii = 0; ii < subjectNameEntries.Count; ii++) { if (subjectNameEntries[ii].StartsWith("CN=", StringComparison.Ordinal)) { applicationName = subjectNameEntries[ii].Substring(3).Trim(); break; } } } if (String.IsNullOrEmpty(applicationName)) { throw new ArgumentNullException(nameof(applicationName), "Must specify a applicationName or a subjectName."); } // remove special characters from name. StringBuilder buffer = new StringBuilder(); for (int ii = 0; ii < applicationName.Length; ii++) { char ch = applicationName[ii]; if (Char.IsControl(ch) || ch == '/' || ch == ',' || ch == ';') { ch = '+'; } buffer.Append(ch); } applicationName = buffer.ToString(); // ensure at least one host name. if (domainNames == null || domainNames.Count == 0) { domainNames = new List <string>(); domainNames.Add(Utils.GetHostName()); } // create the application uri. if (String.IsNullOrEmpty(applicationUri)) { StringBuilder builder = new StringBuilder(); builder.Append("urn:"); builder.Append(domainNames[0]); builder.Append(':'); builder.Append(applicationName); applicationUri = builder.ToString(); } Uri uri = Utils.ParseUri(applicationUri); if (uri == null) { throw new ArgumentNullException(nameof(applicationUri), "Must specify a valid URL."); } // create the subject name, if (String.IsNullOrEmpty(subjectName)) { subjectName = Utils.Format("CN={0}", applicationName); } if (!subjectName.Contains("CN=")) { subjectName = Utils.Format("CN={0}", subjectName); } if (domainNames != null && domainNames.Count > 0) { if (!subjectName.Contains("DC=") && !subjectName.Contains('=')) { subjectName += Utils.Format(", DC={0}", domainNames[0]); } else { subjectName = Utils.ReplaceDCLocalhost(subjectName, domainNames[0]); } } }
/// <summary> /// Extracts the DNS names specified in the certificate. /// </summary> /// <param name="certificate">The certificate.</param> /// <returns>The DNS names.</returns> public static IList <string> GetDomainsFromCertficate(X509Certificate2 certificate) { List <string> dnsNames = new List <string>(); // extracts the domain from the subject name. List <string> fields = X509Utils.ParseDistinguishedName(certificate.Subject); StringBuilder builder = new StringBuilder(); for (int ii = 0; ii < fields.Count; ii++) { if (fields[ii].StartsWith("DC=")) { if (builder.Length > 0) { builder.Append('.'); } builder.Append(fields[ii].Substring(3)); } } if (builder.Length > 0) { dnsNames.Add(builder.ToString().ToUpperInvariant()); } // extract the alternate domains from the subject alternate name extension. X509SubjectAltNameExtension alternateName = X509Extensions.FindExtension <X509SubjectAltNameExtension>(certificate); if (alternateName != null) { for (int ii = 0; ii < alternateName.DomainNames.Count; ii++) { string hostname = alternateName.DomainNames[ii]; // do not add duplicates to the list. bool found = false; for (int jj = 0; jj < dnsNames.Count; jj++) { if (String.Compare(dnsNames[jj], hostname, StringComparison.OrdinalIgnoreCase) == 0) { found = true; break; } } if (!found) { dnsNames.Add(hostname.ToUpperInvariant()); } } for (int ii = 0; ii < alternateName.IPAddresses.Count; ii++) { string ipAddress = alternateName.IPAddresses[ii]; if (!dnsNames.Contains(ipAddress)) { dnsNames.Add(ipAddress); } } } // return the list. return(dnsNames); }
/// <summary> /// Checks if issuer has revoked the certificate. /// </summary> public StatusCode IsRevoked(X509Certificate2 issuer, X509Certificate2 certificate) { if (issuer == null) { throw new ArgumentNullException(nameof(issuer)); } if (certificate == null) { throw new ArgumentNullException(nameof(certificate)); } // check for CRL. DirectoryInfo info = new DirectoryInfo(this.Directory.FullName + Path.DirectorySeparatorChar + "crl"); if (info.Exists) { bool crlExpired = true; foreach (FileInfo file in info.GetFiles("*.crl")) { X509CRL crl = null; try { crl = new X509CRL(file.FullName); } catch (Exception e) { Utils.LogError(e, "Could not parse CRL file."); continue; } if (!X509Utils.CompareDistinguishedName(crl.Issuer, issuer.Subject)) { continue; } if (!crl.VerifySignature(issuer, false)) { continue; } if (crl.IsRevoked(certificate)) { return(StatusCodes.BadCertificateRevoked); } if (crl.ThisUpdate <= DateTime.UtcNow && (crl.NextUpdate == DateTime.MinValue || crl.NextUpdate >= DateTime.UtcNow)) { crlExpired = false; } } // certificate is fine. if (!crlExpired) { return(StatusCodes.Good); } } // can't find a valid CRL. return(StatusCodes.BadCertificateRevocationUnknown); }
/// <summary> /// Check for self signed certificate if there is match of the Subject/Issuer. /// </summary> /// <param name="certificate">The certificate to test.</param> /// <returns>True if self signed.</returns> public static bool IsSelfSigned(X509Certificate2 certificate) { return(X509Utils.CompareDistinguishedName(certificate.SubjectName, certificate.IssuerName)); }
/// <summary> /// Loads the private key from a PFX file in the certificate store. /// </summary> public X509Certificate2 LoadPrivateKey(string thumbprint, string subjectName, string password) { if (m_certificateSubdir == null || !m_certificateSubdir.Exists) { return(null); } if (string.IsNullOrEmpty(thumbprint) && string.IsNullOrEmpty(subjectName)) { return(null); } foreach (FileInfo file in m_certificateSubdir.GetFiles("*.der")) { try { X509Certificate2 certificate = new X509Certificate2(file.FullName); if (!String.IsNullOrEmpty(thumbprint)) { if (!string.Equals(certificate.Thumbprint, thumbprint, StringComparison.CurrentCultureIgnoreCase)) { continue; } } if (!String.IsNullOrEmpty(subjectName)) { if (!X509Utils.CompareDistinguishedName(subjectName, certificate.Subject)) { if (subjectName.Contains('=')) { continue; } if (!X509Utils.ParseDistinguishedName(certificate.Subject).Any(s => s.Equals("CN=" + subjectName, StringComparison.OrdinalIgnoreCase))) { continue; } } } // skip if not RSA certificate if (X509Utils.GetRSAPublicKeySize(certificate) < 0) { continue; } string fileRoot = file.Name.Substring(0, file.Name.Length - file.Extension.Length); StringBuilder filePath = new StringBuilder() .Append(m_privateKeySubdir.FullName) .Append(Path.DirectorySeparatorChar) .Append(fileRoot); X509KeyStorageFlags[] storageFlags = { X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet, X509KeyStorageFlags.Exportable | X509KeyStorageFlags.UserKeySet }; FileInfo privateKeyFile = new FileInfo(filePath.ToString() + ".pfx"); password = password ?? String.Empty; foreach (var flag in storageFlags) { try { certificate = new X509Certificate2( privateKeyFile.FullName, password, flag); if (X509Utils.VerifyRSAKeyPair(certificate, certificate, true)) { return(certificate); } } catch (Exception) { certificate?.Dispose(); certificate = null; } } } catch (Exception e) { Utils.LogError(e, "Could not load private key for certificate " + subjectName); } } return(null); }
private static ServiceResult CheckChainStatus(X509ChainStatus status, CertificateIdentifier id, CertificateIdentifier issuer, bool isIssuer) { switch (status.Status) { case X509ChainStatusFlags.NotValidForUsage: { return(ServiceResult.Create( (isIssuer) ? StatusCodes.BadCertificateUseNotAllowed : StatusCodes.BadCertificateIssuerUseNotAllowed, "Certificate may not be used as an application instance certificate. {0}: {1}", status.Status, status.StatusInformation)); } case X509ChainStatusFlags.NoError: case X509ChainStatusFlags.OfflineRevocation: case X509ChainStatusFlags.InvalidBasicConstraints: { break; } case X509ChainStatusFlags.PartialChain: case X509ChainStatusFlags.UntrustedRoot: { // self signed cert signature validation // .Net Core ChainStatus returns NotSignatureValid only on Windows, // so we have to do the extra cert signature check on all platforms if (issuer == null && !isIssuer && id.Certificate != null && X509Utils.CompareDistinguishedName(id.Certificate.Subject, id.Certificate.Issuer)) { if (!IsSignatureValid(id.Certificate)) { goto case X509ChainStatusFlags.NotSignatureValid; } } // ignore this error because the root check is done // by looking the certificate up in the trusted issuer stores passed to the validator. // the ChainStatus uses the trusted issuer stores. break; } case X509ChainStatusFlags.RevocationStatusUnknown: { if (issuer != null) { if ((issuer.ValidationOptions & CertificateValidationOptions.SuppressRevocationStatusUnknown) != 0) { break; } } // check for meaning less errors for self-signed certificates. if (id.Certificate != null && X509Utils.CompareDistinguishedName(id.Certificate.Subject, id.Certificate.Subject)) { break; } return(ServiceResult.Create( (isIssuer) ? StatusCodes.BadCertificateIssuerRevocationUnknown : StatusCodes.BadCertificateRevocationUnknown, "Certificate revocation status cannot be verified. {0}: {1}", status.Status, status.StatusInformation)); } case X509ChainStatusFlags.Revoked: { return(ServiceResult.Create( (isIssuer) ? StatusCodes.BadCertificateIssuerRevoked : StatusCodes.BadCertificateRevoked, "Certificate has been revoked. {0}: {1}", status.Status, status.StatusInformation)); } case X509ChainStatusFlags.NotTimeNested: { if (id != null && ((id.ValidationOptions & CertificateValidationOptions.SuppressCertificateExpired) != 0)) { // TODO: add logging break; } return(ServiceResult.Create( StatusCodes.BadCertificateIssuerTimeInvalid, "Issuer Certificate has expired or is not yet valid. {0}: {1}", status.Status, status.StatusInformation)); } case X509ChainStatusFlags.NotTimeValid: { if (id != null && ((id.ValidationOptions & CertificateValidationOptions.SuppressCertificateExpired) != 0)) { // TODO: add logging break; } return(ServiceResult.Create( (isIssuer) ? StatusCodes.BadCertificateIssuerTimeInvalid : StatusCodes.BadCertificateTimeInvalid, "Certificate has expired or is not yet valid. {0}: {1}", status.Status, status.StatusInformation)); } case X509ChainStatusFlags.NotSignatureValid: default: { return(ServiceResult.Create( StatusCodes.BadCertificateInvalid, "Certificate validation failed. {0}: {1}", status.Status, status.StatusInformation)); } } return(null); }
/// <summary> /// Throws an exception if validation fails. /// </summary> /// <param name="certificates">The certificates to be checked.</param> /// <exception cref="ServiceResultException">If certificate[0] cannot be accepted</exception> protected virtual async Task InternalValidate(X509Certificate2Collection certificates) { X509Certificate2 certificate = certificates[0]; // check for previously validated certificate. X509Certificate2 certificate2 = null; if (m_validatedCertificates.TryGetValue(certificate.Thumbprint, out certificate2)) { if (Utils.IsEqual(certificate2.RawData, certificate.RawData)) { return; } } CertificateIdentifier trustedCertificate = await GetTrustedCertificate(certificate); // get the issuers (checks the revocation lists if using directory stores). List <CertificateIdentifier> issuers = new List <CertificateIdentifier>(); bool isIssuerTrusted = await GetIssuers(certificates, issuers); // setup policy chain X509ChainPolicy policy = new X509ChainPolicy(); policy.RevocationFlag = X509RevocationFlag.EntireChain; policy.RevocationMode = X509RevocationMode.NoCheck; policy.VerificationFlags = X509VerificationFlags.NoFlag; foreach (CertificateIdentifier issuer in issuers) { if ((issuer.ValidationOptions & CertificateValidationOptions.SuppressRevocationStatusUnknown) != 0) { policy.VerificationFlags |= X509VerificationFlags.IgnoreCertificateAuthorityRevocationUnknown; policy.VerificationFlags |= X509VerificationFlags.IgnoreCtlSignerRevocationUnknown; policy.VerificationFlags |= X509VerificationFlags.IgnoreEndRevocationUnknown; policy.VerificationFlags |= X509VerificationFlags.IgnoreRootRevocationUnknown; } // we did the revocation check in the GetIssuers call. No need here. policy.RevocationMode = X509RevocationMode.NoCheck; policy.ExtraStore.Add(issuer.Certificate); } // build chain. X509Chain chain = new X509Chain(); chain.ChainPolicy = policy; chain.Build(certificate); // check the chain results. CertificateIdentifier target = trustedCertificate; if (target == null) { target = new CertificateIdentifier(certificate); } ServiceResult sresult = null; for (int ii = 0; ii < chain.ChainElements.Count; ii++) { X509ChainElement element = chain.ChainElements[ii]; CertificateIdentifier issuer = null; if (ii < issuers.Count) { issuer = issuers[ii]; } // check for chain status errors. if (element.ChainElementStatus.Length > 0) { foreach (X509ChainStatus status in element.ChainElementStatus) { ServiceResult result = CheckChainStatus(status, target, issuer, (ii != 0)); if (ServiceResult.IsBad(result)) { sresult = new ServiceResult(result, sresult); } } } if (issuer != null) { target = issuer; } } // check whether the chain is complete (if there is a chain) bool issuedByCA = !X509Utils.CompareDistinguishedName(certificate.Subject, certificate.Issuer); bool chainIncomplete = false; if (issuers.Count > 0) { var rootCertificate = issuers[issuers.Count - 1].Certificate; if (!X509Utils.CompareDistinguishedName(rootCertificate.Subject, rootCertificate.Issuer)) { chainIncomplete = true; } } else { if (issuedByCA) { // no issuer found at all chainIncomplete = true; } } // check if certificate issuer is trusted. if (issuedByCA && !isIssuerTrusted && trustedCertificate == null) { var message = CertificateMessage("Certificate issuer is not trusted.", certificate); sresult = new ServiceResult(StatusCodes.BadCertificateUntrusted, null, null, message, null, sresult ); } // check if certificate is trusted. if (trustedCertificate == null && !isIssuerTrusted) { if (m_applicationCertificate == null || !Utils.IsEqual(m_applicationCertificate.RawData, certificate.RawData)) { var message = CertificateMessage("Certificate is not trusted.", certificate); sresult = new ServiceResult(StatusCodes.BadCertificateUntrusted, null, null, message, null, sresult ); } } // check if certificate is valid for use as app/sw or user cert X509KeyUsageFlags certificateKeyUsage = X509Utils.GetKeyUsage(certificate); if ((certificateKeyUsage & X509KeyUsageFlags.DataEncipherment) == 0) { sresult = new ServiceResult(StatusCodes.BadCertificateUseNotAllowed, null, null, "Usage of certificate is not allowed.", null, sresult ); } // check if minimum requirements are met if (m_rejectSHA1SignedCertificates && IsSHA1SignatureAlgorithm(certificate.SignatureAlgorithm)) { sresult = new ServiceResult(StatusCodes.BadCertificatePolicyCheckFailed, null, null, "SHA1 signed certificates are not trusted.", null, sresult ); } int keySize = X509Utils.GetRSAPublicKeySize(certificate); if (keySize < m_minimumCertificateKeySize) { sresult = new ServiceResult(StatusCodes.BadCertificatePolicyCheckFailed, null, null, "Certificate doesn't meet minimum key length requirement.", null, sresult ); } if (issuedByCA && chainIncomplete) { var message = CertificateMessage("Certificate chain validation incomplete.", certificate); sresult = new ServiceResult(StatusCodes.BadCertificateChainIncomplete, null, null, message, null, sresult); } if (sresult != null) { throw new ServiceResultException(sresult); } }
/// <summary> /// Returns the certificate information for a trusted issuer certificate. /// </summary> private async Task <CertificateIdentifier> GetIssuer( X509Certificate2 certificate, CertificateIdentifierCollection explicitList, CertificateStoreIdentifier certificateStore, bool checkRecovationStatus) { // check if self-signed. if (X509Utils.CompareDistinguishedName(certificate.Subject, certificate.Issuer)) { return(null); } string subjectName = certificate.IssuerName.Name; string keyId = null; string serialNumber = null; // find the authority key identifier. X509AuthorityKeyIdentifierExtension authority = X509Extensions.FindExtension <X509AuthorityKeyIdentifierExtension>(certificate); if (authority != null) { keyId = authority.KeyIdentifier; serialNumber = authority.SerialNumber; } // check in explicit list. if (explicitList != null) { for (int ii = 0; ii < explicitList.Count; ii++) { X509Certificate2 issuer = await explicitList[ii].Find(false); if (issuer != null) { if (!X509Utils.IsIssuerAllowed(issuer)) { continue; } if (Match(issuer, subjectName, serialNumber, keyId)) { // can't check revocation. return(new CertificateIdentifier(issuer, CertificateValidationOptions.SuppressRevocationStatusUnknown)); } } } } // check in certificate store. if (certificateStore != null) { ICertificateStore store = certificateStore.OpenStore(); try { X509Certificate2Collection certificates = await store.Enumerate(); for (int ii = 0; ii < certificates.Count; ii++) { X509Certificate2 issuer = certificates[ii]; if (issuer != null) { if (!X509Utils.IsIssuerAllowed(issuer)) { continue; } if (Match(issuer, subjectName, serialNumber, keyId)) { CertificateValidationOptions options = certificateStore.ValidationOptions; // already checked revocation for file based stores. windows based stores always suppress. options |= CertificateValidationOptions.SuppressRevocationStatusUnknown; if (checkRecovationStatus) { StatusCode status = store.IsRevoked(issuer, certificate); if (StatusCode.IsBad(status) && status != StatusCodes.BadNotSupported) { if (status == StatusCodes.BadCertificateRevocationUnknown) { if (X509Utils.IsCertificateAuthority(certificate)) { status.Code = StatusCodes.BadCertificateIssuerRevocationUnknown; } if (m_rejectUnknownRevocationStatus) { throw new ServiceResultException(status); } } else { throw new ServiceResultException(status); } } } return(new CertificateIdentifier(certificates[ii], options)); } } } } finally { store.Close(); } } // not a trusted issuer. return(null); }
/// <summary> /// Returns if a self signed certificate is properly signed. /// </summary> private static bool IsSignatureValid(X509Certificate2 cert) { return(X509Utils.VerifySelfSigned(cert)); }
/// <summary> /// Loads the private key from a PFX/PEM file in the certificate store. /// </summary> public async Task <X509Certificate2> LoadPrivateKey(string thumbprint, string subjectName, string password) { if (NoPrivateKeys || m_certificateSubdir == null || !m_certificateSubdir.Exists) { return(null); } if (string.IsNullOrEmpty(thumbprint) && string.IsNullOrEmpty(subjectName)) { return(null); } // on some platforms, specifically in virtualized environments, // reloading a previously created and saved private key may fail on the first attempt. const int retryDelay = 100; int retryCounter = 3; while (retryCounter-- > 0) { bool certificateFound = false; Exception importException = null; foreach (FileInfo file in m_certificateSubdir.GetFiles("*.der")) { try { X509Certificate2 certificate = new X509Certificate2(file.FullName); if (!String.IsNullOrEmpty(thumbprint)) { if (!string.Equals(certificate.Thumbprint, thumbprint, StringComparison.OrdinalIgnoreCase)) { continue; } } if (!String.IsNullOrEmpty(subjectName)) { if (!X509Utils.CompareDistinguishedName(subjectName, certificate.Subject)) { if (subjectName.Contains('=')) { continue; } if (!X509Utils.ParseDistinguishedName(certificate.Subject).Any(s => s.Equals("CN=" + subjectName, StringComparison.Ordinal))) { continue; } } } // skip if not RSA certificate if (X509Utils.GetRSAPublicKeySize(certificate) < 0) { continue; } string fileRoot = file.Name.Substring(0, file.Name.Length - file.Extension.Length); StringBuilder filePath = new StringBuilder() .Append(m_privateKeySubdir.FullName) .Append(Path.DirectorySeparatorChar) .Append(fileRoot); X509KeyStorageFlags[] storageFlags = { X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet, X509KeyStorageFlags.Exportable | X509KeyStorageFlags.UserKeySet }; FileInfo privateKeyFilePfx = new FileInfo(filePath + ".pfx"); FileInfo privateKeyFilePem = new FileInfo(filePath + ".pem"); password = password ?? String.Empty; if (privateKeyFilePfx.Exists) { certificateFound = true; foreach (var flag in storageFlags) { try { certificate = new X509Certificate2( privateKeyFilePfx.FullName, password, flag); if (X509Utils.VerifyRSAKeyPair(certificate, certificate, true)) { Utils.LogInfo(Utils.TraceMasks.Security, "Imported the PFX private key for [{0}].", certificate.Thumbprint); return(certificate); } } catch (Exception ex) { importException = ex; certificate?.Dispose(); } } } // if PFX file doesn't exist, check for PEM file. else if (privateKeyFilePem.Exists) { certificateFound = true; try { byte[] pemDataBlob = File.ReadAllBytes(privateKeyFilePem.FullName); certificate = CertificateFactory.CreateCertificateWithPEMPrivateKey(certificate, pemDataBlob, password); if (X509Utils.VerifyRSAKeyPair(certificate, certificate, true)) { Utils.LogInfo(Utils.TraceMasks.Security, "Imported the PEM private key for [{0}].", certificate.Thumbprint); return(certificate); } } catch (Exception exception) { certificate?.Dispose(); importException = exception; } } else { Utils.LogError(Utils.TraceMasks.Security, "A private key for the certificate with thumbprint [{0}] does not exist.", certificate.Thumbprint); continue; } } catch (Exception e) { Utils.LogError(e, "Could not load private key for certificate {0}", subjectName); } } // found a certificate, but some error occurred if (certificateFound) { Utils.LogError(Utils.TraceMasks.Security, "The private key for the certificate with subject {0} failed to import.", subjectName); if (importException != null) { Utils.LogError(importException, "Certificate import failed."); } } else { if (!String.IsNullOrEmpty(thumbprint)) { Utils.LogError(Utils.TraceMasks.Security, "A Private key for the certificate with thumbpint {0} was not found.", thumbprint); } // if no private key was found, no need to retry break; } // retry within a few ms if (retryCounter > 0) { Utils.LogInfo(Utils.TraceMasks.Security, "Retry to import private key after {0} ms.", retryDelay); await Task.Delay(retryDelay).ConfigureAwait(false); } } return(null); }