From 2d001bb58319e4aac567caae36d08d2606f91b1e Mon Sep 17 00:00:00 2001 From: Nate McMaster Date: Thu, 13 Dec 2018 10:33:47 -0800 Subject: [PATCH] Stop producing Microsoft.AspNetCore.Certificates.Generation.Sources This was only used in the aspnet/AspNetCore repo. so the source for this project is moving to that repo \n\nCommit migrated from https://github.com/dotnet/extensions/commit/9290dc1dbccdb6c6063970a0c1ac5f384cc95785 --- .../CertificateManager.cs | 720 ------------------ .../CertificatePurpose.cs | 12 - .../Directory.Build.props | 7 - .../EnsureCertificateResult.cs | 20 - .../Shared.Tests/CertificateManagerTests.cs | 304 -------- 5 files changed, 1063 deletions(-) delete mode 100644 src/Shared/CertificateGeneration/CertificateManager.cs delete mode 100644 src/Shared/CertificateGeneration/CertificatePurpose.cs delete mode 100644 src/Shared/CertificateGeneration/Directory.Build.props delete mode 100644 src/Shared/CertificateGeneration/EnsureCertificateResult.cs delete mode 100644 src/Shared/test/Shared.Tests/CertificateManagerTests.cs diff --git a/src/Shared/CertificateGeneration/CertificateManager.cs b/src/Shared/CertificateGeneration/CertificateManager.cs deleted file mode 100644 index 4e2a0a9964..0000000000 --- a/src/Shared/CertificateGeneration/CertificateManager.cs +++ /dev/null @@ -1,720 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.X509Certificates; -using System.Text; -using System.Text.RegularExpressions; - -namespace Microsoft.AspNetCore.Certificates.Generation -{ - internal class CertificateManager - { - public const string AspNetHttpsOid = "1.3.6.1.4.1.311.84.1.1"; - public const string AspNetHttpsOidFriendlyName = "ASP.NET Core HTTPS development certificate"; - - public const string AspNetIdentityOid = "1.3.6.1.4.1.311.84.1.2"; - public const string AspNetIdentityOidFriendlyName = "ASP.NET Core Identity Json Web Token signing development certificate"; - - private const string ServerAuthenticationEnhancedKeyUsageOid = "1.3.6.1.5.5.7.3.1"; - private const string ServerAuthenticationEnhancedKeyUsageOidFriendlyName = "Server Authentication"; - - private const string LocalhostHttpsDnsName = "localhost"; - private const string LocalhostHttpsDistinguishedName = "CN=" + LocalhostHttpsDnsName; - - private const string IdentityDistinguishedName = "CN=Microsoft.AspNetCore.Identity.Signing"; - - public const int RSAMinimumKeySizeInBits = 2048; - - private static readonly TimeSpan MaxRegexTimeout = TimeSpan.FromMinutes(1); - private const string CertificateSubjectRegex = "CN=(.*[^,]+).*"; - private const string MacOSSystemKeyChain = "/Library/Keychains/System.keychain"; - private static readonly string MacOSUserKeyChain = Environment.GetEnvironmentVariable("HOME") + "/Library/Keychains/login.keychain-db"; - private const string MacOSFindCertificateCommandLine = "security"; -#if NETCOREAPP2_0 || NETCOREAPP2_1 - private static readonly string MacOSFindCertificateCommandLineArgumentsFormat = "find-certificate -c {0} -a -Z -p " + MacOSSystemKeyChain; -#endif - private const string MacOSFindCertificateOutputRegex = "SHA-1 hash: ([0-9A-Z]+)"; - private const string MacOSRemoveCertificateTrustCommandLine = "sudo"; - private const string MacOSRemoveCertificateTrustCommandLineArgumentsFormat = "security remove-trusted-cert -d {0}"; - private const string MacOSDeleteCertificateCommandLine = "sudo"; - private const string MacOSDeleteCertificateCommandLineArgumentsFormat = "security delete-certificate -Z {0} {1}"; - private const string MacOSTrustCertificateCommandLine = "sudo"; -#if NETCOREAPP2_0 || NETCOREAPP2_1 - private static readonly string MacOSTrustCertificateCommandLineArguments = "security add-trusted-cert -d -r trustRoot -k " + MacOSSystemKeyChain + " "; -#endif - private const int UserCancelledErrorCode = 1223; - - public IList ListCertificates( - CertificatePurpose purpose, - StoreName storeName, - StoreLocation location, - bool isValid, - bool requireExportable = true) - { - var certificates = new List(); - try - { - using (var store = new X509Store(storeName, location)) - { - store.Open(OpenFlags.ReadOnly); - certificates.AddRange(store.Certificates.OfType()); - IEnumerable 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)matchingCertificates; - } - } - catch - { - DisposeCertificates(certificates); - certificates.Clear(); - return certificates; - } - - bool HasOid(X509Certificate2 certificate, string oid) => - certificate.Extensions.OfType() - .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 - } - - private void DisposeCertificates(IEnumerable disposables) - { - foreach (var disposable in disposables) - { - try - { - disposable.Dispose(); - } - catch - { - } - } - } - -#if NETCOREAPP2_0 || NETCOREAPP2_1 - - public X509Certificate2 CreateAspNetCoreHttpsDevelopmentCertificate(DateTimeOffset notBefore, DateTimeOffset notAfter, string subjectOverride) - { - var subject = new X500DistinguishedName(subjectOverride ?? LocalhostHttpsDistinguishedName); - var extensions = new List(); - var sanBuilder = new SubjectAlternativeNameBuilder(); - sanBuilder.AddDnsName(LocalhostHttpsDnsName); - - var keyUsage = new X509KeyUsageExtension(X509KeyUsageFlags.KeyEncipherment, critical: true); - var enhancedKeyUsage = new X509EnhancedKeyUsageExtension( - new OidCollection() { - new Oid( - ServerAuthenticationEnhancedKeyUsageOid, - ServerAuthenticationEnhancedKeyUsageOidFriendlyName) - }, - critical: true); - - var basicConstraints = new X509BasicConstraintsExtension( - certificateAuthority: false, - hasPathLengthConstraint: false, - pathLengthConstraint: 0, - critical: true); - - var aspNetHttpsExtension = new X509Extension( - new AsnEncodedData( - new Oid(AspNetHttpsOid, AspNetHttpsOidFriendlyName), - Encoding.ASCII.GetBytes(AspNetHttpsOidFriendlyName)), - critical: false); - - extensions.Add(basicConstraints); - extensions.Add(keyUsage); - extensions.Add(enhancedKeyUsage); - extensions.Add(sanBuilder.Build(critical: true)); - extensions.Add(aspNetHttpsExtension); - - var certificate = CreateSelfSignedCertificate(subject, extensions, notBefore, notAfter); - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - certificate.FriendlyName = AspNetHttpsOidFriendlyName; - } - - return certificate; - } - - public X509Certificate2 CreateApplicationTokenSigningDevelopmentCertificate(DateTimeOffset notBefore, DateTimeOffset notAfter, string subjectOverride) - { - var subject = new X500DistinguishedName(subjectOverride ?? IdentityDistinguishedName); - var extensions = new List(); - - var keyUsage = new X509KeyUsageExtension(X509KeyUsageFlags.DigitalSignature, critical: true); - var enhancedKeyUsage = new X509EnhancedKeyUsageExtension( - new OidCollection() { - new Oid( - ServerAuthenticationEnhancedKeyUsageOid, - ServerAuthenticationEnhancedKeyUsageOidFriendlyName) - }, - critical: true); - - var basicConstraints = new X509BasicConstraintsExtension( - certificateAuthority: false, - hasPathLengthConstraint: false, - pathLengthConstraint: 0, - critical: true); - - var aspNetIdentityExtension = new X509Extension( - new AsnEncodedData( - new Oid(AspNetIdentityOid, AspNetIdentityOidFriendlyName), - Encoding.ASCII.GetBytes(AspNetIdentityOidFriendlyName)), - critical: false); - - extensions.Add(basicConstraints); - extensions.Add(keyUsage); - extensions.Add(enhancedKeyUsage); - extensions.Add(aspNetIdentityExtension); - - var certificate = CreateSelfSignedCertificate(subject, extensions, notBefore, notAfter); - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - certificate.FriendlyName = AspNetIdentityOidFriendlyName; - } - - return certificate; - } - - public X509Certificate2 CreateSelfSignedCertificate( - X500DistinguishedName subject, - IEnumerable extensions, - DateTimeOffset notBefore, - DateTimeOffset notAfter) - { - var key = CreateKeyMaterial(RSAMinimumKeySizeInBits); - - var request = new CertificateRequest(subject, key, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); - foreach (var extension in extensions) - { - request.CertificateExtensions.Add(extension); - } - - return request.CreateSelfSigned(notBefore, notAfter); - - RSA CreateKeyMaterial(int minimumKeySize) - { - var rsa = RSA.Create(minimumKeySize); - if (rsa.KeySize < minimumKeySize) - { - throw new InvalidOperationException($"Failed to create a key with a size of {minimumKeySize} bits"); - } - - return rsa; - } - } - - public X509Certificate2 SaveCertificateInStore(X509Certificate2 certificate, StoreName name, StoreLocation location) - { - var imported = certificate; - if (!RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) - { - // On non OSX systems we need to export the certificate and import it so that the transient - // key that we generated gets persisted. - var export = certificate.Export(X509ContentType.Pkcs12, ""); - imported = new X509Certificate2(export, "", X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable); - Array.Clear(export, 0, export.Length); - } - - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - imported.FriendlyName = certificate.FriendlyName; - } - - using (var store = new X509Store(name, location)) - { - store.Open(OpenFlags.ReadWrite); - store.Add(imported); - store.Close(); - }; - - return imported; - } - - public void ExportCertificate(X509Certificate2 certificate, string path, bool includePrivateKey, string password) - { - if (Path.GetDirectoryName(path) != "") - { - Directory.CreateDirectory(Path.GetDirectoryName(path)); - } - - if (includePrivateKey) - { - var bytes = certificate.Export(X509ContentType.Pkcs12, password); - try - { - File.WriteAllBytes(path, bytes); - } - finally - { - Array.Clear(bytes, 0, bytes.Length); - } - } - else - { - var bytes = certificate.Export(X509ContentType.Cert); - File.WriteAllBytes(path, bytes); - } - } - - public void TrustCertificate(X509Certificate2 certificate) - { - // Strip certificate of the private key if any. - var publicCertificate = new X509Certificate2(certificate.Export(X509ContentType.Cert)); - - if (!IsTrusted(publicCertificate)) - { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - TrustCertificateOnWindows(certificate, publicCertificate); - } - else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) - { - TrustCertificateOnMac(publicCertificate); - } - } - } - - private void TrustCertificateOnMac(X509Certificate2 publicCertificate) - { - var tmpFile = Path.GetTempFileName(); - try - { - ExportCertificate(publicCertificate, tmpFile, includePrivateKey: false, password: null); - using (var process = Process.Start(MacOSTrustCertificateCommandLine, MacOSTrustCertificateCommandLineArguments + tmpFile)) - { - process.WaitForExit(); - if (process.ExitCode != 0) - { - throw new InvalidOperationException("There was an error trusting the certificate."); - } - } - } - finally - { - try - { - if (File.Exists(tmpFile)) - { - File.Delete(tmpFile); - } - } - catch - { - // We don't care if we can't delete the temp file. - } - } - } - - private static void TrustCertificateOnWindows(X509Certificate2 certificate, X509Certificate2 publicCertificate) - { - publicCertificate.FriendlyName = certificate.FriendlyName; - - using (var store = new X509Store(StoreName.Root, StoreLocation.CurrentUser)) - { - store.Open(OpenFlags.ReadWrite); - try - { - store.Add(publicCertificate); - } - catch (CryptographicException exception) when (exception.HResult == UserCancelledErrorCode) - { - throw new UserCancelledTrustException(); - } - store.Close(); - }; - } - - public bool IsTrusted(X509Certificate2 certificate) - { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - return ListCertificates(CertificatePurpose.HTTPS, StoreName.Root, StoreLocation.CurrentUser, isValid: true, requireExportable: false) - .Any(c => c.Thumbprint == certificate.Thumbprint); - } - else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) - { - var subjectMatch = Regex.Match(certificate.Subject, CertificateSubjectRegex, RegexOptions.Singleline, MaxRegexTimeout); - if (!subjectMatch.Success) - { - throw new InvalidOperationException($"Can't determine the subject for the certificate with subject '{certificate.Subject}'."); - } - var subject = subjectMatch.Groups[1].Value; - using (var checkTrustProcess = Process.Start(new ProcessStartInfo( - MacOSFindCertificateCommandLine, - string.Format(MacOSFindCertificateCommandLineArgumentsFormat, subject)) - { - RedirectStandardOutput = true - })) - { - var output = checkTrustProcess.StandardOutput.ReadToEnd(); - checkTrustProcess.WaitForExit(); - var matches = Regex.Matches(output, MacOSFindCertificateOutputRegex, RegexOptions.Multiline, MaxRegexTimeout); - var hashes = matches.OfType().Select(m => m.Groups[1].Value).ToList(); - return hashes.Any(h => string.Equals(h, certificate.Thumbprint, StringComparison.Ordinal)); - } - } - else - { - return false; - } - } - - public void CleanupHttpsCertificates(string subject = LocalhostHttpsDistinguishedName) - { - CleanupCertificates(CertificatePurpose.HTTPS, subject); - } - - 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 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); - } - - private void RemoveCertificate(X509Certificate2 certificate, RemoveLocations locations) - { - switch (locations) - { - case RemoveLocations.Undefined: - throw new InvalidOperationException($"'{nameof(RemoveLocations.Undefined)}' is not a valid location."); - case RemoveLocations.Local: - RemoveCertificateFromUserStore(certificate); - break; - case RemoveLocations.Trusted when !RuntimeInformation.IsOSPlatform(OSPlatform.Linux): - RemoveCertificateFromTrustedRoots(certificate); - break; - case RemoveLocations.All: - if (!RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - { - RemoveCertificateFromTrustedRoots(certificate); - } - RemoveCertificateFromUserStore(certificate); - break; - default: - throw new InvalidOperationException("Invalid location."); - } - } - - private static void RemoveCertificateFromUserStore(X509Certificate2 certificate) - { - using (var store = new X509Store(StoreName.My, StoreLocation.CurrentUser)) - { - store.Open(OpenFlags.ReadWrite); - var matching = store.Certificates - .OfType() - .Single(c => c.SerialNumber == certificate.SerialNumber); - - store.Remove(matching); - store.Close(); - } - } - - private void RemoveCertificateFromTrustedRoots(X509Certificate2 certificate) - { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - using (var store = new X509Store(StoreName.Root, StoreLocation.CurrentUser)) - { - store.Open(OpenFlags.ReadWrite); - var matching = store.Certificates - .OfType() - .Single(c => c.SerialNumber == certificate.SerialNumber); - - store.Remove(matching); - store.Close(); - } - } - else - { - if (IsTrusted(certificate)) // On OSX this check just ensures its on the system keychain - { - try - { - RemoveCertificateTrustRule(certificate); - } - catch - { - // We don't care if we fail to remove the trust rule if - // for some reason the certificate became untrusted. - // The delete command will fail if the certificate is - // trusted. - } - RemoveCertificateFromKeyChain(MacOSSystemKeyChain, certificate); - } - } - } - - private static void RemoveCertificateTrustRule(X509Certificate2 certificate) - { - var certificatePath = Path.GetTempFileName(); - try - { - var certBytes = certificate.Export(X509ContentType.Cert); - File.WriteAllBytes(certificatePath, certBytes); - var processInfo = new ProcessStartInfo( - MacOSRemoveCertificateTrustCommandLine, - string.Format( - MacOSRemoveCertificateTrustCommandLineArgumentsFormat, - certificatePath - )); - using (var process = Process.Start(processInfo)) - { - process.WaitForExit(); - } - } - finally - { - try - { - if (File.Exists(certificatePath)) - { - File.Delete(certificatePath); - } - } - catch - { - // We don't care about failing to do clean-up on a temp file. - } - } - } - - private static void RemoveCertificateFromKeyChain(string keyChain, X509Certificate2 certificate) - { - var processInfo = new ProcessStartInfo( - MacOSDeleteCertificateCommandLine, - string.Format( - MacOSDeleteCertificateCommandLineArgumentsFormat, - certificate.Thumbprint.ToUpperInvariant(), - keyChain - )) - { - RedirectStandardOutput = true, - RedirectStandardError = true - }; - - using (var process = Process.Start(processInfo)) - { - var output = process.StandardOutput.ReadToEnd() + process.StandardError.ReadToEnd(); - process.WaitForExit(); - - if (process.ExitCode != 0) - { - throw new InvalidOperationException($@"There was an error removing the certificate with thumbprint '{certificate.Thumbprint}'. - -{output}"); - } - } - } - - public EnsureCertificateResult EnsureAspNetCoreHttpsDevelopmentCertificate( - DateTimeOffset notBefore, - DateTimeOffset notAfter, - string path = null, - bool trust = false, - bool includePrivateKey = false, - string password = null, - string subject = LocalhostHttpsDistinguishedName) - { - return EnsureValidCertificateExists(notBefore, notAfter, CertificatePurpose.HTTPS, path, trust, includePrivateKey, password, subject); - } - - public EnsureCertificateResult EnsureAspNetCoreApplicationTokensDevelopmentCertificate( - DateTimeOffset notBefore, - DateTimeOffset notAfter, - string path = null, - bool trust = false, - bool includePrivateKey = false, - string password = null, - string subject = IdentityDistinguishedName) - { - return EnsureValidCertificateExists(notBefore, notAfter, CertificatePurpose.Signing, path, trust, includePrivateKey, password, subject); - } - - 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 (UserCancelledTrustException) - { - return EnsureCertificateResult.UserCancelledTrustStep; - } - catch - { - return EnsureCertificateResult.FailedToTrustTheCertificate; - } - } - - return result; - } - - private class UserCancelledTrustException : Exception - { - } - - private enum RemoveLocations - { - Undefined, - Local, - Trusted, - All - } -#endif - } -} \ No newline at end of file diff --git a/src/Shared/CertificateGeneration/CertificatePurpose.cs b/src/Shared/CertificateGeneration/CertificatePurpose.cs deleted file mode 100644 index 1ad1a6d79b..0000000000 --- a/src/Shared/CertificateGeneration/CertificatePurpose.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -namespace Microsoft.AspNetCore.Certificates.Generation -{ - internal enum CertificatePurpose - { - All, - HTTPS, - Signing - } -} \ No newline at end of file diff --git a/src/Shared/CertificateGeneration/Directory.Build.props b/src/Shared/CertificateGeneration/Directory.Build.props deleted file mode 100644 index f3f6fc4b35..0000000000 --- a/src/Shared/CertificateGeneration/Directory.Build.props +++ /dev/null @@ -1,7 +0,0 @@ - - - - - Microsoft.AspNetCore.Certificates.Generation.Sources - - diff --git a/src/Shared/CertificateGeneration/EnsureCertificateResult.cs b/src/Shared/CertificateGeneration/EnsureCertificateResult.cs deleted file mode 100644 index d3c86ce05d..0000000000 --- a/src/Shared/CertificateGeneration/EnsureCertificateResult.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -#if NETCOREAPP2_0 || NETCOREAPP2_1 - -namespace Microsoft.AspNetCore.Certificates.Generation -{ - internal enum EnsureCertificateResult - { - Succeeded = 1, - ValidCertificatePresent, - ErrorCreatingTheCertificate, - ErrorSavingTheCertificateIntoTheCurrentUserPersonalStore, - ErrorExportingTheCertificate, - FailedToTrustTheCertificate, - UserCancelledTrustStep - } -} - -#endif \ No newline at end of file diff --git a/src/Shared/test/Shared.Tests/CertificateManagerTests.cs b/src/Shared/test/Shared.Tests/CertificateManagerTests.cs deleted file mode 100644 index 613f5c966f..0000000000 --- a/src/Shared/test/Shared.Tests/CertificateManagerTests.cs +++ /dev/null @@ -1,304 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -#if NETCOREAPP2_0 || NETCOREAPP2_1 - -using System; -using System.IO; -using System.Linq; -using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.X509Certificates; -using System.Text; -using Xunit; -using Xunit.Abstractions; - -namespace Microsoft.AspNetCore.Certificates.Generation.Tests -{ - public class CertificateManagerTests - { - public CertificateManagerTests(ITestOutputHelper output) - { - Output = output; - } - - public const string TestCertificateSubject = "CN=aspnet.test"; - - public ITestOutputHelper Output { get; } - - [Fact] - public void EnsureCreateHttpsCertificate_CreatesACertificate_WhenThereAreNoHttpsCertificates() - { - try - { - // Arrange - const string CertificateName = nameof(EnsureCreateHttpsCertificate_CreatesACertificate_WhenThereAreNoHttpsCertificates) + ".cer"; - var manager = new CertificateManager(); - - manager.RemoveAllCertificates(CertificatePurpose.HTTPS, StoreName.My, StoreLocation.CurrentUser, TestCertificateSubject); - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - manager.RemoveAllCertificates(CertificatePurpose.HTTPS, StoreName.Root, StoreLocation.CurrentUser, TestCertificateSubject); - } - - // Act - DateTimeOffset now = DateTimeOffset.UtcNow; - now = new DateTimeOffset(now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second, 0, now.Offset); - var result = manager.EnsureAspNetCoreHttpsDevelopmentCertificate(now, now.AddYears(1), CertificateName, trust: false, subject: TestCertificateSubject); - - // Assert - Assert.Equal(EnsureCertificateResult.Succeeded, result); - Assert.True(File.Exists(CertificateName)); - - var exportedCertificate = new X509Certificate2(File.ReadAllBytes(CertificateName)); - Assert.NotNull(exportedCertificate); - Assert.False(exportedCertificate.HasPrivateKey); - - var httpsCertificates = manager.ListCertificates(CertificatePurpose.HTTPS, StoreName.My, StoreLocation.CurrentUser, isValid: false); - var httpsCertificate = Assert.Single(httpsCertificates, c => c.Subject == TestCertificateSubject); - Assert.True(httpsCertificate.HasPrivateKey); - Assert.Equal(TestCertificateSubject, httpsCertificate.Subject); - Assert.Equal(TestCertificateSubject, httpsCertificate.Issuer); - Assert.Equal("sha256RSA", httpsCertificate.SignatureAlgorithm.FriendlyName); - Assert.Equal("1.2.840.113549.1.1.11", httpsCertificate.SignatureAlgorithm.Value); - - Assert.Equal(now.LocalDateTime, httpsCertificate.NotBefore); - Assert.Equal(now.AddYears(1).LocalDateTime, httpsCertificate.NotAfter); - Assert.Contains( - httpsCertificate.Extensions.OfType(), - e => e is X509BasicConstraintsExtension basicConstraints && - basicConstraints.Critical == true && - basicConstraints.CertificateAuthority == false && - basicConstraints.HasPathLengthConstraint == false && - basicConstraints.PathLengthConstraint == 0); - - Assert.Contains( - httpsCertificate.Extensions.OfType(), - e => e is X509KeyUsageExtension keyUsage && - keyUsage.Critical == true && - keyUsage.KeyUsages == X509KeyUsageFlags.KeyEncipherment); - - Assert.Contains( - httpsCertificate.Extensions.OfType(), - e => e is X509EnhancedKeyUsageExtension enhancedKeyUsage && - enhancedKeyUsage.Critical == true && - enhancedKeyUsage.EnhancedKeyUsages.OfType().Single() is Oid keyUsage && - keyUsage.Value == "1.3.6.1.5.5.7.3.1"); - - // Subject alternative name - Assert.Contains( - httpsCertificate.Extensions.OfType(), - e => e.Critical == true && - e.Oid.Value == "2.5.29.17"); - - // ASP.NET HTTPS Development certificate extension - Assert.Contains( - httpsCertificate.Extensions.OfType(), - e => e.Critical == false && - e.Oid.Value == "1.3.6.1.4.1.311.84.1.1" && - Encoding.ASCII.GetString(e.RawData) == "ASP.NET Core HTTPS development certificate"); - - Assert.Equal(httpsCertificate.GetCertHashString(), exportedCertificate.GetCertHashString()); - - } - catch (Exception e) - { - Output.WriteLine(e.Message); - ListCertificates(Output); - throw; - } - } - - private void ListCertificates(ITestOutputHelper output) - { - using (var store = new X509Store(StoreName.My, StoreLocation.CurrentUser)) - { - store.Open(OpenFlags.ReadOnly); - var certificates = store.Certificates; - foreach (var certificate in certificates) - { - Output.WriteLine($"Certificate: '{Convert.ToBase64String(certificate.Export(X509ContentType.Cert))}'."); - certificate.Dispose(); - } - - store.Close(); - } - } - - [Fact] - public void EnsureCreateHttpsCertificate_DoesNotCreateACertificate_WhenThereIsAnExistingHttpsCertificates() - { - // Arrange - const string CertificateName = nameof(EnsureCreateHttpsCertificate_DoesNotCreateACertificate_WhenThereIsAnExistingHttpsCertificates) + ".pfx"; - var certificatePassword = Guid.NewGuid().ToString(); - - var manager = new CertificateManager(); - - manager.RemoveAllCertificates(CertificatePurpose.HTTPS, StoreName.My, StoreLocation.CurrentUser, TestCertificateSubject); - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - manager.RemoveAllCertificates(CertificatePurpose.HTTPS, StoreName.Root, StoreLocation.CurrentUser, TestCertificateSubject); - } - - DateTimeOffset now = DateTimeOffset.UtcNow; - now = new DateTimeOffset(now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second, 0, now.Offset); - manager.EnsureAspNetCoreHttpsDevelopmentCertificate(now, now.AddYears(1), path: null, trust: false, subject: TestCertificateSubject); - - var httpsCertificate = manager.ListCertificates(CertificatePurpose.HTTPS, StoreName.My, StoreLocation.CurrentUser, isValid: false).Single(c => c.Subject == TestCertificateSubject); - - // Act - var result = manager.EnsureAspNetCoreHttpsDevelopmentCertificate(now, now.AddYears(1), CertificateName, trust: false, includePrivateKey: true, password: certificatePassword, subject: TestCertificateSubject); - - // Assert - Assert.Equal(EnsureCertificateResult.ValidCertificatePresent, result); - Assert.True(File.Exists(CertificateName)); - - var exportedCertificate = new X509Certificate2(File.ReadAllBytes(CertificateName), certificatePassword); - Assert.NotNull(exportedCertificate); - Assert.True(exportedCertificate.HasPrivateKey); - - - Assert.Equal(httpsCertificate.GetCertHashString(), exportedCertificate.GetCertHashString()); - } - - [Fact(Skip = "Requires user interaction")] - public void EnsureAspNetCoreHttpsDevelopmentCertificate_ReturnsCorrectResult_WhenUserCancelsTrustStepOnWindows() - { - var manager = new CertificateManager(); - - manager.RemoveAllCertificates(CertificatePurpose.HTTPS, StoreName.My, StoreLocation.CurrentUser, TestCertificateSubject); - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - manager.RemoveAllCertificates(CertificatePurpose.HTTPS, StoreName.Root, StoreLocation.CurrentUser, TestCertificateSubject); - } - - DateTimeOffset now = DateTimeOffset.UtcNow; - now = new DateTimeOffset(now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second, 0, now.Offset); - var trustFailed = manager.EnsureAspNetCoreHttpsDevelopmentCertificate(now, now.AddYears(1), path: null, trust: true, subject: TestCertificateSubject); - - Assert.Equal(EnsureCertificateResult.UserCancelledTrustStep, trustFailed); - } - - [Fact(Skip = "Requires user interaction")] - public void EnsureAspNetCoreHttpsDevelopmentCertificate_CanRemoveCertificates() - { - var manager = new CertificateManager(); - - DateTimeOffset now = DateTimeOffset.UtcNow; - now = new DateTimeOffset(now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second, 0, now.Offset); - manager.EnsureAspNetCoreHttpsDevelopmentCertificate(now, now.AddYears(1), path: null, trust: true, subject: TestCertificateSubject); - - manager.CleanupHttpsCertificates(TestCertificateSubject); - - Assert.Empty(manager.ListCertificates(CertificatePurpose.HTTPS, StoreName.My, StoreLocation.CurrentUser, isValid: false).Where(c => c.Subject == TestCertificateSubject)); - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - Assert.Empty(manager.ListCertificates(CertificatePurpose.HTTPS, StoreName.Root, StoreLocation.CurrentUser, isValid: false).Where(c => c.Subject == TestCertificateSubject)); - } - } - - [Fact] - public void EnsureCreateIdentityTokenSigningCertificate_CreatesACertificate_WhenThereAreNoHttpsCertificates() - { - // Arrange - const string CertificateName = nameof(EnsureCreateIdentityTokenSigningCertificate_CreatesACertificate_WhenThereAreNoHttpsCertificates) + ".cer"; - var manager = new CertificateManager(); - - manager.RemoveAllCertificates(CertificatePurpose.Signing, StoreName.My, StoreLocation.CurrentUser, TestCertificateSubject); - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - manager.RemoveAllCertificates(CertificatePurpose.Signing, StoreName.Root, StoreLocation.CurrentUser, TestCertificateSubject); - } - - // Act - DateTimeOffset now = DateTimeOffset.UtcNow; - now = new DateTimeOffset(now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second, 0, now.Offset); - var result = manager.EnsureAspNetCoreApplicationTokensDevelopmentCertificate(now, now.AddYears(1), CertificateName, trust: false, subject: TestCertificateSubject); - - // Assert - Assert.Equal(EnsureCertificateResult.Succeeded, result); - Assert.True(File.Exists(CertificateName)); - - var exportedCertificate = new X509Certificate2(File.ReadAllBytes(CertificateName)); - Assert.NotNull(exportedCertificate); - Assert.False(exportedCertificate.HasPrivateKey); - - var identityCertificates = manager.ListCertificates(CertificatePurpose.Signing, StoreName.My, StoreLocation.CurrentUser, isValid: false); - var identityCertificate = Assert.Single(identityCertificates, i => i.Subject == TestCertificateSubject); - Assert.True(identityCertificate.HasPrivateKey); - Assert.Equal(TestCertificateSubject, identityCertificate.Subject); - Assert.Equal(TestCertificateSubject, identityCertificate.Issuer); - Assert.Equal("sha256RSA", identityCertificate.SignatureAlgorithm.FriendlyName); - Assert.Equal("1.2.840.113549.1.1.11", identityCertificate.SignatureAlgorithm.Value); - - Assert.Equal(now.LocalDateTime, identityCertificate.NotBefore); - Assert.Equal(now.AddYears(1).LocalDateTime, identityCertificate.NotAfter); - Assert.Contains( - identityCertificate.Extensions.OfType(), - e => e is X509BasicConstraintsExtension basicConstraints && - basicConstraints.Critical == true && - basicConstraints.CertificateAuthority == false && - basicConstraints.HasPathLengthConstraint == false && - basicConstraints.PathLengthConstraint == 0); - - Assert.Contains( - identityCertificate.Extensions.OfType(), - e => e is X509KeyUsageExtension keyUsage && - keyUsage.Critical == true && - keyUsage.KeyUsages == X509KeyUsageFlags.DigitalSignature); - - Assert.Contains( - identityCertificate.Extensions.OfType(), - e => e is X509EnhancedKeyUsageExtension enhancedKeyUsage && - enhancedKeyUsage.Critical == true && - enhancedKeyUsage.EnhancedKeyUsages.OfType().Single() is Oid keyUsage && - keyUsage.Value == "1.3.6.1.5.5.7.3.1"); - - // ASP.NET Core Identity Json Web Token signing development certificate - Assert.Contains( - identityCertificate.Extensions.OfType(), - e => e.Critical == false && - e.Oid.Value == "1.3.6.1.4.1.311.84.1.2" && - Encoding.ASCII.GetString(e.RawData) == "ASP.NET Core Identity Json Web Token signing development certificate"); - - Assert.Equal(identityCertificate.GetCertHashString(), exportedCertificate.GetCertHashString()); - } - - [Fact] - public void EnsureCreateIdentityTokenSigningCertificate_DoesNotCreateACertificate_WhenThereIsAnExistingHttpsCertificates() - { - // Arrange - const string CertificateName = nameof(EnsureCreateIdentityTokenSigningCertificate_DoesNotCreateACertificate_WhenThereIsAnExistingHttpsCertificates) + ".pfx"; - var certificatePassword = Guid.NewGuid().ToString(); - - var manager = new CertificateManager(); - - manager.RemoveAllCertificates(CertificatePurpose.Signing, StoreName.My, StoreLocation.CurrentUser, TestCertificateSubject); - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - manager.RemoveAllCertificates(CertificatePurpose.Signing, StoreName.Root, StoreLocation.CurrentUser, TestCertificateSubject); - } - - DateTimeOffset now = DateTimeOffset.UtcNow; - now = new DateTimeOffset(now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second, 0, now.Offset); - manager.EnsureAspNetCoreApplicationTokensDevelopmentCertificate(now, now.AddYears(1), path: null, trust: false, subject: TestCertificateSubject); - - var identityTokenSigningCertificates = manager.ListCertificates(CertificatePurpose.Signing, StoreName.My, StoreLocation.CurrentUser, isValid: false).Single(c => c.Subject == TestCertificateSubject); - - // Act - var result = manager.EnsureAspNetCoreApplicationTokensDevelopmentCertificate(now, now.AddYears(1), CertificateName, trust: false, includePrivateKey: true, password: certificatePassword, subject: TestCertificateSubject); - - // Assert - Assert.Equal(EnsureCertificateResult.ValidCertificatePresent, result); - Assert.True(File.Exists(CertificateName)); - - var exportedCertificate = new X509Certificate2(File.ReadAllBytes(CertificateName), certificatePassword); - Assert.NotNull(exportedCertificate); - Assert.True(exportedCertificate.HasPrivateKey); - - Assert.Equal(identityTokenSigningCertificates.GetCertHashString(), exportedCertificate.GetCertHashString()); - } - } -} - -#endif \ No newline at end of file