public void RemoveAllCertificates(CertificatePurpose purpose, StoreName storeName, StoreLocation storeLocation, string subject = null) { var certificates = ListCertificates(purpose, storeName, storeLocation, isValid: false); var certificatesWithName = subject == null ? certificates : certificates.Where(c => c.Subject == subject); RemoveAllCertificatesCore(certificatesWithName, storeName, storeLocation); DisposeCertificates(certificates); }
public void RemoveAllCertificates(CertificatePurpose purpose, StoreName storeName, StoreLocation storeLocation, string subject = null) { var certificates = RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? ListCertificates(purpose, StoreName.My, StoreLocation.CurrentUser, isValid: false) : ListCertificates(purpose, storeName, storeLocation, isValid: false); var certificatesWithName = subject == null ? certificates : certificates.Where(c => c.Subject == subject); var removeLocation = storeName == StoreName.My ? RemoveLocations.Local : RemoveLocations.Trusted; foreach (var certificate in certificates) { RemoveCertificate(certificate, removeLocation); } DisposeCertificates(certificates); }
public void CleanupCertificates(CertificatePurpose purpose, string subject) { // On OS X we don't have a good way to manage trusted certificates in the system keychain // so we do everything by invoking the native toolchain. // This has some limitations, like for example not being able to identify our custom OID extension. For that // matter, when we are cleaning up certificates on the machine, we start by removing the trusted certificates. // To do this, we list the certificates that we can identify on the current user personal store and we invoke // the native toolchain to remove them from the sytem keychain. Once we have removed the trusted certificates, // we remove the certificates from the local user store to finish up the cleanup. var certificates = ListCertificates(purpose, StoreName.My, StoreLocation.CurrentUser, isValid: false); foreach (var certificate in certificates) { RemoveCertificate(certificate, RemoveLocations.All); } }
public DetailedEnsureCertificateResult EnsureValidCertificateExists( DateTimeOffset notBefore, DateTimeOffset notAfter, CertificatePurpose purpose, string path, bool trust, bool includePrivateKey, string password, string subject) { if (purpose == CertificatePurpose.All) { throw new ArgumentException("The certificate must have a specific purpose."); } var result = new DetailedEnsureCertificateResult(); var certificates = ListCertificates(purpose, StoreName.My, StoreLocation.CurrentUser, isValid: true, requireExportable: true, result.Diagnostics).Concat( ListCertificates(purpose, StoreName.My, StoreLocation.LocalMachine, isValid: true, requireExportable: true, result.Diagnostics)); var filteredCertificates = subject == null ? certificates : certificates.Where(c => c.Subject == subject); if (subject != null) { var excludedCertificates = certificates.Except(filteredCertificates); result.Diagnostics.Debug($"Filtering found certificates to those with a subject equal to '{subject}'"); result.Diagnostics.Debug(result.Diagnostics.DescribeCertificates(filteredCertificates)); result.Diagnostics.Debug($"Listing certificates excluded from consideration."); result.Diagnostics.Debug(result.Diagnostics.DescribeCertificates(excludedCertificates)); } else { result.Diagnostics.Debug("Skipped filtering certificates by subject."); } certificates = filteredCertificates; result.ResultCode = EnsureCertificateResult.Succeeded; X509Certificate2 certificate = null; if (certificates.Count() > 0) { result.Diagnostics.Debug("Found valid certificates present on the machine."); result.Diagnostics.Debug(result.Diagnostics.DescribeCertificates(certificates)); certificate = certificates.First(); result.Diagnostics.Debug("Selected certificate"); result.Diagnostics.Debug(result.Diagnostics.DescribeCertificates(certificate)); result.ResultCode = EnsureCertificateResult.ValidCertificatePresent; } else { result.Diagnostics.Debug("No valid certificates present on this machine. Trying to create one."); try { switch (purpose) { case CertificatePurpose.All: throw new InvalidOperationException("The certificate must have a specific purpose."); case CertificatePurpose.HTTPS: certificate = CreateAspNetCoreHttpsDevelopmentCertificate(notBefore, notAfter, subject, result.Diagnostics); break; default: throw new InvalidOperationException("The certificate must have a purpose."); } } catch (Exception e) { result.Diagnostics.Error("Error creating the certificate.", e); result.ResultCode = EnsureCertificateResult.ErrorCreatingTheCertificate; return(result); } try { certificate = SaveCertificateInStore(certificate, StoreName.My, StoreLocation.CurrentUser, result.Diagnostics); } catch (Exception e) { result.Diagnostics.Error($"Error saving the certificate in the certificate store '{StoreLocation.CurrentUser}\\{StoreName.My}'.", e); result.ResultCode = EnsureCertificateResult.ErrorSavingTheCertificateIntoTheCurrentUserPersonalStore; return(result); } } if (path != null) { result.Diagnostics.Debug("Trying to export the certificate."); result.Diagnostics.Debug(result.Diagnostics.DescribeCertificates(certificate)); try { ExportCertificate(certificate, path, includePrivateKey, password, result.Diagnostics); } catch (Exception e) { result.Diagnostics.Error("An error ocurred exporting the certificate.", e); result.ResultCode = EnsureCertificateResult.ErrorExportingTheCertificate; return(result); } } if ((RuntimeInformation.IsOSPlatform(OSPlatform.Windows) || RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) && trust) { try { result.Diagnostics.Debug("Trying to export the certificate."); TrustCertificate(certificate, result.Diagnostics); } catch (UserCancelledTrustException) { result.Diagnostics.Error("The user cancelled trusting the certificate.", null); result.ResultCode = EnsureCertificateResult.UserCancelledTrustStep; return(result); } catch (Exception e) { result.Diagnostics.Error("There was an error trusting the certificate.", e); result.ResultCode = EnsureCertificateResult.FailedToTrustTheCertificate; return(result); } } return(result); }
public static IList <X509Certificate2> ListCertificates( CertificatePurpose purpose, StoreName storeName, StoreLocation location, bool isValid, bool requireExportable = true, DiagnosticInformation diagnostics = null) { diagnostics?.Debug($"Listing '{purpose.ToString()}' certificates on '{location}\\{storeName}'."); var certificates = new List <X509Certificate2>(); try { using (var store = new X509Store(storeName, location)) { store.Open(OpenFlags.ReadOnly); certificates.AddRange(store.Certificates.OfType <X509Certificate2>()); IEnumerable <X509Certificate2> matchingCertificates = certificates; switch (purpose) { case CertificatePurpose.All: matchingCertificates = matchingCertificates .Where(c => HasOid(c, AspNetHttpsOid)); break; case CertificatePurpose.HTTPS: matchingCertificates = matchingCertificates .Where(c => HasOid(c, AspNetHttpsOid)); break; default: break; } diagnostics?.Debug(diagnostics.DescribeCertificates(matchingCertificates)); if (isValid) { // Ensure the certificate hasn't expired, has a private key and its exportable // (for container/unix scenarios). diagnostics?.Debug("Checking certificates for validity."); var now = DateTimeOffset.Now; var validCertificates = matchingCertificates .Where(c => c.NotBefore <= now && now <= c.NotAfter && (!requireExportable || !RuntimeInformation.IsOSPlatform(OSPlatform.Windows) || IsExportable(c)) && MatchesVersion(c)) .ToArray(); var invalidCertificates = matchingCertificates.Except(validCertificates); diagnostics?.Debug("Listing valid certificates"); diagnostics?.Debug(diagnostics.DescribeCertificates(validCertificates)); diagnostics?.Debug("Listing invalid certificates"); diagnostics?.Debug(diagnostics.DescribeCertificates(invalidCertificates)); matchingCertificates = validCertificates; } // We need to enumerate the certificates early to prevent dispoisng issues. matchingCertificates = matchingCertificates.ToList(); var certificatesToDispose = certificates.Except(matchingCertificates); DisposeCertificates(certificatesToDispose); store.Close(); return((IList <X509Certificate2>)matchingCertificates); } } catch { DisposeCertificates(certificates); certificates.Clear(); return(certificates); } bool HasOid(X509Certificate2 certificate, string oid) => certificate.Extensions.OfType <X509Extension>() .Any(e => string.Equals(oid, e.Oid.Value, StringComparison.Ordinal)); bool MatchesVersion(X509Certificate2 c) { var byteArray = c.Extensions.OfType <X509Extension>() .Where(e => string.Equals(AspNetHttpsOid, e.Oid.Value, StringComparison.Ordinal)) .Single() .RawData; if ((byteArray.Length == AspNetHttpsOidFriendlyName.Length && byteArray[0] == (byte)'A') || byteArray.Length == 0) { // No Version set, default to 0 return(0 >= AspNetHttpsCertificateVersion); } else { // Version is in the only byte of the byte array. return(byteArray[0] >= AspNetHttpsCertificateVersion); } } #if !XPLAT bool IsExportable(X509Certificate2 c) => ((c.GetRSAPrivateKey() is RSACryptoServiceProvider rsaPrivateKey && rsaPrivateKey.CspKeyContainerInfo.Exportable) || (c.GetRSAPrivateKey() is RSACng cngPrivateKey && cngPrivateKey.Key.ExportPolicy == CngExportPolicies.AllowExport)); #else // Only check for RSA CryptoServiceProvider and do not fail in XPlat tooling as // System.Security.Cryptography.Cng is not part of the shared framework and we don't // want to bring the dependency in on CLI scenarios. This functionality will be used // on CLI scenarios as part of the first run experience, so checking the exportability // of the certificate is not important. bool IsExportable(X509Certificate2 c) => ((c.GetRSAPrivateKey() is RSACryptoServiceProvider rsaPrivateKey && rsaPrivateKey.CspKeyContainerInfo.Exportable) || !(c.GetRSAPrivateKey() is RSACryptoServiceProvider)); #endif }
public DetailedEnsureCertificateResult EnsureValidCertificateExists( DateTimeOffset notBefore, DateTimeOffset notAfter, CertificatePurpose purpose, string path = null, bool trust = false, bool includePrivateKey = false, string password = null, string subjectOverride = null, bool isInteractive = true) { if (purpose == CertificatePurpose.All) { throw new ArgumentException("The certificate must have a specific purpose."); } var result = new DetailedEnsureCertificateResult(); var certificates = ListCertificates(purpose, StoreName.My, StoreLocation.CurrentUser, isValid: true, requireExportable: true, result.Diagnostics).Concat( ListCertificates(purpose, StoreName.My, StoreLocation.LocalMachine, isValid: true, requireExportable: true, result.Diagnostics)); var filteredCertificates = subjectOverride == null ? certificates : certificates.Where(c => c.Subject == subjectOverride); if (subjectOverride != null) { var excludedCertificates = certificates.Except(filteredCertificates); result.Diagnostics.Debug($"Filtering found certificates to those with a subject equal to '{subjectOverride}'"); result.Diagnostics.Debug(result.Diagnostics.DescribeCertificates(filteredCertificates)); result.Diagnostics.Debug($"Listing certificates excluded from consideration."); result.Diagnostics.Debug(result.Diagnostics.DescribeCertificates(excludedCertificates)); } else { result.Diagnostics.Debug("Skipped filtering certificates by subject."); } if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { foreach (var cert in filteredCertificates) { if (!CanAccessCertificateKeyAcrossPartitions(cert)) { if (!isInteractive) { // If the process is not interactive (first run experience) bail out. We will simply create a certificate // in case there is none or report success during the first run experience. break; } try { // The command we run handles making keys for all localhost certificates accessible across partitions. If it can not run the // command safely (because there are other localhost certificates that were not created by asp.net core, it will throw. MakeCertificateKeyAccessibleAcrossPartitions(cert); break; } catch (Exception ex) { result.Diagnostics.Error("Failed to make certificate key accessible", ex); result.ResultCode = EnsureCertificateResult.FailedToMakeKeyAccessible; return(result); } } } } certificates = filteredCertificates; result.ResultCode = EnsureCertificateResult.Succeeded; X509Certificate2 certificate = null; if (certificates.Count() > 0) { result.Diagnostics.Debug("Found valid certificates present on the machine."); result.Diagnostics.Debug(result.Diagnostics.DescribeCertificates(certificates)); certificate = certificates.First(); result.Diagnostics.Debug("Selected certificate"); result.Diagnostics.Debug(result.Diagnostics.DescribeCertificates(certificate)); result.ResultCode = EnsureCertificateResult.ValidCertificatePresent; } else { result.Diagnostics.Debug("No valid certificates present on this machine. Trying to create one."); try { switch (purpose) { case CertificatePurpose.All: throw new InvalidOperationException("The certificate must have a specific purpose."); case CertificatePurpose.HTTPS: certificate = CreateAspNetCoreHttpsDevelopmentCertificate(notBefore, notAfter, subjectOverride, result.Diagnostics); break; default: throw new InvalidOperationException("The certificate must have a purpose."); } } catch (Exception e) { result.Diagnostics.Error("Error creating the certificate.", e); result.ResultCode = EnsureCertificateResult.ErrorCreatingTheCertificate; return(result); } try { certificate = SaveCertificateInStore(certificate, StoreName.My, StoreLocation.CurrentUser, result.Diagnostics); } catch (Exception e) { result.Diagnostics.Error($"Error saving the certificate in the certificate store '{StoreLocation.CurrentUser}\\{StoreName.My}'.", e); result.ResultCode = EnsureCertificateResult.ErrorSavingTheCertificateIntoTheCurrentUserPersonalStore; return(result); } if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX) && isInteractive) { MakeCertificateKeyAccessibleAcrossPartitions(certificate); } if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX) && isInteractive) { MakeCertificateKeyAccessibleAcrossPartitions(certificate); } } if (path != null) { result.Diagnostics.Debug("Trying to export the certificate."); result.Diagnostics.Debug(result.Diagnostics.DescribeCertificates(certificate)); try { ExportCertificate(certificate, path, includePrivateKey, password, result.Diagnostics); } catch (Exception e) { result.Diagnostics.Error("An error ocurred exporting the certificate.", e); result.ResultCode = EnsureCertificateResult.ErrorExportingTheCertificate; return(result); } } if ((RuntimeInformation.IsOSPlatform(OSPlatform.Windows) || RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) && trust) { try { result.Diagnostics.Debug("Trying to export the certificate."); TrustCertificate(certificate, result.Diagnostics); } catch (UserCancelledTrustException) { result.Diagnostics.Error("The user cancelled trusting the certificate.", null); result.ResultCode = EnsureCertificateResult.UserCancelledTrustStep; return(result); } catch (Exception e) { result.Diagnostics.Error("There was an error trusting the certificate.", e); result.ResultCode = EnsureCertificateResult.FailedToTrustTheCertificate; return(result); } } return(result); }
public EnsureCertificateResult EnsureValidCertificateExists( DateTimeOffset notBefore, DateTimeOffset notAfter, CertificatePurpose purpose, string path = null, bool trust = false, bool includePrivateKey = false, string password = null, string subjectOverride = null) { if (purpose == CertificatePurpose.All) { throw new ArgumentException("The certificate must have a specific purpose."); } var certificates = ListCertificates(purpose, StoreName.My, StoreLocation.CurrentUser, isValid: true).Concat( ListCertificates(purpose, StoreName.My, StoreLocation.LocalMachine, isValid: true)); certificates = subjectOverride == null ? certificates : certificates.Where(c => c.Subject == subjectOverride); var result = EnsureCertificateResult.Succeeded; X509Certificate2 certificate = null; if (certificates.Count() > 0) { certificate = certificates.FirstOrDefault(); result = EnsureCertificateResult.ValidCertificatePresent; } else { try { switch (purpose) { case CertificatePurpose.All: throw new InvalidOperationException("The certificate must have a specific purpose."); case CertificatePurpose.HTTPS: certificate = CreateAspNetCoreHttpsDevelopmentCertificate(notBefore, notAfter, subjectOverride); break; case CertificatePurpose.Signing: certificate = CreateApplicationTokenSigningDevelopmentCertificate(notBefore, notAfter, subjectOverride); break; default: throw new InvalidOperationException("The certificate must have a purpose."); } } catch { return(EnsureCertificateResult.ErrorCreatingTheCertificate); } try { certificate = SaveCertificateInStore(certificate, StoreName.My, StoreLocation.CurrentUser); } catch { return(EnsureCertificateResult.ErrorSavingTheCertificateIntoTheCurrentUserPersonalStore); } } if (path != null) { try { ExportCertificate(certificate, path, includePrivateKey, password); } catch { return(EnsureCertificateResult.ErrorExportingTheCertificate); } } if ((RuntimeInformation.IsOSPlatform(OSPlatform.Windows) || RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) && trust) { try { TrustCertificate(certificate); } catch { return(EnsureCertificateResult.FailedToTrustTheCertificate); } } return(result); }
public IList <X509Certificate2> ListCertificates( CertificatePurpose purpose, StoreName storeName, StoreLocation location, bool isValid, bool requireExportable = true) { var certificates = new List <X509Certificate2>(); try { using (var store = new X509Store(storeName, location)) { store.Open(OpenFlags.ReadOnly); certificates.AddRange(store.Certificates.OfType <X509Certificate2>()); IEnumerable <X509Certificate2> matchingCertificates = certificates; switch (purpose) { case CertificatePurpose.All: matchingCertificates = matchingCertificates .Where(c => HasOid(c, AspNetHttpsOid) || HasOid(c, AspNetIdentityOid)); break; case CertificatePurpose.HTTPS: matchingCertificates = matchingCertificates .Where(c => HasOid(c, AspNetHttpsOid)); break; case CertificatePurpose.Signing: matchingCertificates = matchingCertificates .Where(c => HasOid(c, AspNetIdentityOid)); break; default: break; } if (isValid) { // Ensure the certificate hasn't expired, has a private key and its exportable // (for container/unix scenarios). var now = DateTimeOffset.Now; matchingCertificates = matchingCertificates .Where(c => c.NotBefore <= now && now <= c.NotAfter && (!requireExportable || !RuntimeInformation.IsOSPlatform(OSPlatform.Windows) || IsExportable(c))); } // We need to enumerate the certificates early to prevent dispoisng issues. matchingCertificates = matchingCertificates.ToList(); var certificatesToDispose = certificates.Except(matchingCertificates); DisposeCertificates(certificatesToDispose); store.Close(); return((IList <X509Certificate2>)matchingCertificates); } } catch { DisposeCertificates(certificates); certificates.Clear(); return(certificates); } bool HasOid(X509Certificate2 certificate, string oid) => certificate.Extensions.OfType <X509Extension>() .Any(e => string.Equals(oid, e.Oid.Value, StringComparison.Ordinal)); bool IsExportable(X509Certificate2 c) => ((c.GetRSAPrivateKey() is RSACryptoServiceProvider rsaPrivateKey && rsaPrivateKey.CspKeyContainerInfo.Exportable) || (c.GetRSAPrivateKey() is RSACng cngPrivateKey && cngPrivateKey.Key.ExportPolicy == CngExportPolicies.AllowExport)); }
public IList <X509Certificate2> ListCertificates( CertificatePurpose purpose, StoreName storeName, StoreLocation location, bool isValid, bool requireExportable = true) { var certificates = new List <X509Certificate2>(); try { using (var store = new X509Store(storeName, location)) { store.Open(OpenFlags.ReadOnly); certificates.AddRange(store.Certificates.OfType <X509Certificate2>()); IEnumerable <X509Certificate2> matchingCertificates = certificates; switch (purpose) { case CertificatePurpose.All: matchingCertificates = matchingCertificates .Where(c => HasOid(c, AspNetHttpsOid) || HasOid(c, AspNetIdentityOid)); break; case CertificatePurpose.HTTPS: matchingCertificates = matchingCertificates .Where(c => HasOid(c, AspNetHttpsOid)); break; case CertificatePurpose.Signing: matchingCertificates = matchingCertificates .Where(c => HasOid(c, AspNetIdentityOid)); break; default: break; } if (isValid) { // Ensure the certificate hasn't expired, has a private key and its exportable // (for container/unix scenarios). var now = DateTimeOffset.Now; matchingCertificates = matchingCertificates .Where(c => c.NotBefore <= now && now <= c.NotAfter && (!requireExportable || !RuntimeInformation.IsOSPlatform(OSPlatform.Windows) || IsExportable(c))); } // We need to enumerate the certificates early to prevent dispoisng issues. matchingCertificates = matchingCertificates.ToList(); var certificatesToDispose = certificates.Except(matchingCertificates); DisposeCertificates(certificatesToDispose); store.Close(); return((IList <X509Certificate2>)matchingCertificates); } } catch { DisposeCertificates(certificates); certificates.Clear(); return(certificates); } bool HasOid(X509Certificate2 certificate, string oid) => certificate.Extensions.OfType <X509Extension>() .Any(e => string.Equals(oid, e.Oid.Value, StringComparison.Ordinal)); #if !XPLAT bool IsExportable(X509Certificate2 c) => ((c.GetRSAPrivateKey() is RSACryptoServiceProvider rsaPrivateKey && rsaPrivateKey.CspKeyContainerInfo.Exportable) || (c.GetRSAPrivateKey() is RSACng cngPrivateKey && cngPrivateKey.Key.ExportPolicy == CngExportPolicies.AllowExport)); #else // Only check for RSA CryptoServiceProvider and do not fail in XPlat tooling as // System.Security.Cryptography.Cng is not pat of the shared framework and we don't // want to bring the dependency in on CLI scenarios. This functionality will be used // on CLI scenarios as part of the first run experience, so checking the exportability // of the certificate is not important. bool IsExportable(X509Certificate2 c) => ((c.GetRSAPrivateKey() is RSACryptoServiceProvider rsaPrivateKey && rsaPrivateKey.CspKeyContainerInfo.Exportable) || !(c.GetRSAPrivateKey() is RSACryptoServiceProvider)); #endif }
internal CertificateFilterInfo(CertificatePurpose p) => this.purpose = p;