/// <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); }