/// <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 = Guid.NewGuid().ToString(); RSA rsaPrivateKey = null; try { rsaPrivateKey = certificateWithPrivateKey.GetRSAPrivateKey(); byte[] pfxData = CertificateBuilder.CreatePfxWithRSAPrivateKey( certificate, certificate.FriendlyName, rsaPrivateKey, passcode); return(X509Utils.CreateCertificateFromPKCS12(pfxData, passcode)); } finally { RsaUtils.RSADispose(rsaPrivateKey); } }
/// <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."); } return(certificate.CopyWithPrivateKey(certificateWithPrivateKey.GetRSAPrivateKey())); }
/// <summary> /// Loads the cached version of a certificate. /// </summary> /// <param name="certificate">The certificate to load.</param> /// <param name="ensurePrivateKeyAccessible">If true a key container is created for a certificate that must be deleted by calling Cleanup.</param> /// <returns>The cached certificate.</returns> /// <remarks> /// This function is necessary because all private keys used for cryptography /// operations must be in a key container. /// Private keys stored in a PFX file have no key container by default. /// </remarks> public static X509Certificate2 Load(X509Certificate2 certificate, bool ensurePrivateKeyAccessible) { if (certificate == null) { return(null); } lock (m_certificatesLock) { X509Certificate2 cachedCertificate = null; // check for existing cached certificate. if (m_certificates.TryGetValue(certificate.Thumbprint, out cachedCertificate)) { return(cachedCertificate); } // nothing more to do if no private key or dont care about accessibility. if (!certificate.HasPrivateKey || !ensurePrivateKeyAccessible) { return(certificate); } if (ensurePrivateKeyAccessible) { if (!X509Utils.VerifyRSAKeyPair(certificate, certificate)) { Utils.LogWarning("Trying to add certificate to cache with invalid private key."); return(null); } } // update the cache. m_certificates[certificate.Thumbprint] = certificate; if (m_certificates.Count > 100) { Utils.LogWarning("Certificate cache has {0} certificates in it.", m_certificates.Count); } } return(certificate); }
/// <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> /// 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); }
/// <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); }