Add versioning to dotnet-dev-certs (#10908)
This commit is contained in:
parent
5b56de966e
commit
fdba8a91f9
|
|
@ -42,6 +42,10 @@ namespace Microsoft.AspNetCore.Certificates.Generation
|
||||||
private static readonly string MacOSTrustCertificateCommandLineArguments = "security add-trusted-cert -d -r trustRoot -k " + MacOSSystemKeyChain + " ";
|
private static readonly string MacOSTrustCertificateCommandLineArguments = "security add-trusted-cert -d -r trustRoot -k " + MacOSSystemKeyChain + " ";
|
||||||
private const int UserCancelledErrorCode = 1223;
|
private const int UserCancelledErrorCode = 1223;
|
||||||
|
|
||||||
|
// Setting to 0 means we don't append the version byte,
|
||||||
|
// which is what all machines currently have.
|
||||||
|
public int AspNetHttpsCertificateVersion { get; set; } = 1;
|
||||||
|
|
||||||
public IList<X509Certificate2> ListCertificates(
|
public IList<X509Certificate2> ListCertificates(
|
||||||
CertificatePurpose purpose,
|
CertificatePurpose purpose,
|
||||||
StoreName storeName,
|
StoreName storeName,
|
||||||
|
|
@ -83,7 +87,8 @@ namespace Microsoft.AspNetCore.Certificates.Generation
|
||||||
var validCertificates = matchingCertificates
|
var validCertificates = matchingCertificates
|
||||||
.Where(c => c.NotBefore <= now &&
|
.Where(c => c.NotBefore <= now &&
|
||||||
now <= c.NotAfter &&
|
now <= c.NotAfter &&
|
||||||
(!requireExportable || !RuntimeInformation.IsOSPlatform(OSPlatform.Windows) || IsExportable(c)))
|
(!requireExportable || !RuntimeInformation.IsOSPlatform(OSPlatform.Windows) || IsExportable(c))
|
||||||
|
&& MatchesVersion(c))
|
||||||
.ToArray();
|
.ToArray();
|
||||||
|
|
||||||
var invalidCertificates = matchingCertificates.Except(validCertificates);
|
var invalidCertificates = matchingCertificates.Except(validCertificates);
|
||||||
|
|
@ -117,6 +122,25 @@ namespace Microsoft.AspNetCore.Certificates.Generation
|
||||||
bool HasOid(X509Certificate2 certificate, string oid) =>
|
bool HasOid(X509Certificate2 certificate, string oid) =>
|
||||||
certificate.Extensions.OfType<X509Extension>()
|
certificate.Extensions.OfType<X509Extension>()
|
||||||
.Any(e => string.Equals(oid, e.Oid.Value, StringComparison.Ordinal));
|
.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
|
#if !XPLAT
|
||||||
bool IsExportable(X509Certificate2 c) =>
|
bool IsExportable(X509Certificate2 c) =>
|
||||||
((c.GetRSAPrivateKey() is RSACryptoServiceProvider rsaPrivateKey &&
|
((c.GetRSAPrivateKey() is RSACryptoServiceProvider rsaPrivateKey &&
|
||||||
|
|
@ -171,10 +195,22 @@ namespace Microsoft.AspNetCore.Certificates.Generation
|
||||||
pathLengthConstraint: 0,
|
pathLengthConstraint: 0,
|
||||||
critical: true);
|
critical: true);
|
||||||
|
|
||||||
|
byte[] bytePayload;
|
||||||
|
|
||||||
|
if (AspNetHttpsCertificateVersion != 0)
|
||||||
|
{
|
||||||
|
bytePayload = new byte[1];
|
||||||
|
bytePayload[0] = (byte)AspNetHttpsCertificateVersion;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
bytePayload = Encoding.ASCII.GetBytes(AspNetHttpsOidFriendlyName);
|
||||||
|
}
|
||||||
|
|
||||||
var aspNetHttpsExtension = new X509Extension(
|
var aspNetHttpsExtension = new X509Extension(
|
||||||
new AsnEncodedData(
|
new AsnEncodedData(
|
||||||
new Oid(AspNetHttpsOid, AspNetHttpsOidFriendlyName),
|
new Oid(AspNetHttpsOid, AspNetHttpsOidFriendlyName),
|
||||||
Encoding.ASCII.GetBytes(AspNetHttpsOidFriendlyName)),
|
bytePayload),
|
||||||
critical: false);
|
critical: false);
|
||||||
|
|
||||||
extensions.Add(basicConstraints);
|
extensions.Add(basicConstraints);
|
||||||
|
|
@ -633,7 +669,7 @@ namespace Microsoft.AspNetCore.Certificates.Generation
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public EnsureCertificateResult EnsureAspNetCoreHttpsDevelopmentCertificate(
|
public DetailedEnsureCertificateResult EnsureAspNetCoreHttpsDevelopmentCertificate(
|
||||||
DateTimeOffset notBefore,
|
DateTimeOffset notBefore,
|
||||||
DateTimeOffset notAfter,
|
DateTimeOffset notAfter,
|
||||||
string path = null,
|
string path = null,
|
||||||
|
|
@ -645,109 +681,7 @@ namespace Microsoft.AspNetCore.Certificates.Generation
|
||||||
return EnsureValidCertificateExists(notBefore, notAfter, CertificatePurpose.HTTPS, path, trust, includePrivateKey, password, subject);
|
return EnsureValidCertificateExists(notBefore, notAfter, CertificatePurpose.HTTPS, path, trust, includePrivateKey, password, subject);
|
||||||
}
|
}
|
||||||
|
|
||||||
public EnsureCertificateResult EnsureValidCertificateExists(
|
public DetailedEnsureCertificateResult 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;
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is just to avoid breaking changes across repos.
|
|
||||||
// Will be renamed back to EnsureAspNetCoreHttpsDevelopmentCertificate once updates are made elsewhere.
|
|
||||||
public DetailedEnsureCertificateResult EnsureAspNetCoreHttpsDevelopmentCertificate2(
|
|
||||||
DateTimeOffset notBefore,
|
|
||||||
DateTimeOffset notAfter,
|
|
||||||
string path = null,
|
|
||||||
bool trust = false,
|
|
||||||
bool includePrivateKey = false,
|
|
||||||
string password = null,
|
|
||||||
string subject = LocalhostHttpsDistinguishedName)
|
|
||||||
{
|
|
||||||
return EnsureValidCertificateExists2(notBefore, notAfter, CertificatePurpose.HTTPS, path, trust, includePrivateKey, password, subject);
|
|
||||||
}
|
|
||||||
|
|
||||||
public DetailedEnsureCertificateResult EnsureValidCertificateExists2(
|
|
||||||
DateTimeOffset notBefore,
|
DateTimeOffset notBefore,
|
||||||
DateTimeOffset notAfter,
|
DateTimeOffset notAfter,
|
||||||
CertificatePurpose purpose,
|
CertificatePurpose purpose,
|
||||||
|
|
|
||||||
|
|
@ -7,17 +7,20 @@ using System.Linq;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using System.Security.Cryptography.X509Certificates;
|
using System.Security.Cryptography.X509Certificates;
|
||||||
using System.Text;
|
|
||||||
using Microsoft.AspNetCore.Testing.xunit;
|
using Microsoft.AspNetCore.Testing.xunit;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
using Xunit.Abstractions;
|
using Xunit.Abstractions;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Certificates.Generation.Tests
|
namespace Microsoft.AspNetCore.Certificates.Generation.Tests
|
||||||
{
|
{
|
||||||
public class CertificateManagerTests
|
public class CertificateManagerTests : IClassFixture<CertFixture>
|
||||||
{
|
{
|
||||||
public CertificateManagerTests(ITestOutputHelper output)
|
private CertFixture _fixture;
|
||||||
|
private CertificateManager _manager => _fixture.Manager;
|
||||||
|
|
||||||
|
public CertificateManagerTests(ITestOutputHelper output, CertFixture fixture)
|
||||||
{
|
{
|
||||||
|
_fixture = fixture;
|
||||||
Output = output;
|
Output = output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -25,122 +28,31 @@ namespace Microsoft.AspNetCore.Certificates.Generation.Tests
|
||||||
|
|
||||||
public ITestOutputHelper Output { get; }
|
public ITestOutputHelper Output { get; }
|
||||||
|
|
||||||
[Fact(Skip = "True")]
|
[ConditionalFact]
|
||||||
|
[SkipOnHelix("https://github.com/aspnet/AspNetCore/issues/6721")]
|
||||||
public void EnsureCreateHttpsCertificate_CreatesACertificate_WhenThereAreNoHttpsCertificates()
|
public void EnsureCreateHttpsCertificate_CreatesACertificate_WhenThereAreNoHttpsCertificates()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
|
_fixture.CleanupCertificates();
|
||||||
|
|
||||||
const string CertificateName = nameof(EnsureCreateHttpsCertificate_CreatesACertificate_WhenThereAreNoHttpsCertificates) + ".cer";
|
const string CertificateName = nameof(EnsureCreateHttpsCertificate_CreatesACertificate_WhenThereAreNoHttpsCertificates) + ".cer";
|
||||||
var manager = new CertificateManager();
|
|
||||||
|
|
||||||
manager.RemoveAllCertificates(CertificatePurpose.HTTPS, StoreName.My, StoreLocation.CurrentUser, TestCertificateSubject);
|
// Act
|
||||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
|
||||||
{
|
|
||||||
manager.RemoveAllCertificates(CertificatePurpose.HTTPS, StoreName.Root, StoreLocation.CurrentUser, TestCertificateSubject);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Act
|
|
||||||
DateTimeOffset now = DateTimeOffset.UtcNow;
|
DateTimeOffset now = DateTimeOffset.UtcNow;
|
||||||
now = new DateTimeOffset(now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second, 0, now.Offset);
|
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);
|
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<X509Extension>(),
|
|
||||||
e => e is X509BasicConstraintsExtension basicConstraints &&
|
|
||||||
basicConstraints.Critical == true &&
|
|
||||||
basicConstraints.CertificateAuthority == false &&
|
|
||||||
basicConstraints.HasPathLengthConstraint == false &&
|
|
||||||
basicConstraints.PathLengthConstraint == 0);
|
|
||||||
|
|
||||||
Assert.Contains(
|
|
||||||
httpsCertificate.Extensions.OfType<X509Extension>(),
|
|
||||||
e => e is X509KeyUsageExtension keyUsage &&
|
|
||||||
keyUsage.Critical == true &&
|
|
||||||
keyUsage.KeyUsages == (X509KeyUsageFlags.KeyEncipherment | X509KeyUsageFlags.DigitalSignature));
|
|
||||||
|
|
||||||
Assert.Contains(
|
|
||||||
httpsCertificate.Extensions.OfType<X509Extension>(),
|
|
||||||
e => e is X509EnhancedKeyUsageExtension enhancedKeyUsage &&
|
|
||||||
enhancedKeyUsage.Critical == true &&
|
|
||||||
enhancedKeyUsage.EnhancedKeyUsages.OfType<Oid>().Single() is Oid keyUsage &&
|
|
||||||
keyUsage.Value == "1.3.6.1.5.5.7.3.1");
|
|
||||||
|
|
||||||
// Subject alternative name
|
|
||||||
Assert.Contains(
|
|
||||||
httpsCertificate.Extensions.OfType<X509Extension>(),
|
|
||||||
e => e.Critical == true &&
|
|
||||||
e.Oid.Value == "2.5.29.17");
|
|
||||||
|
|
||||||
// ASP.NET HTTPS Development certificate extension
|
|
||||||
Assert.Contains(
|
|
||||||
httpsCertificate.Extensions.OfType<X509Extension>(),
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[ConditionalFact]
|
|
||||||
[SkipOnHelix("https://github.com/aspnet/AspNetCore/issues/6721")]
|
|
||||||
public void EnsureCreateHttpsCertificate2_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.EnsureAspNetCoreHttpsDevelopmentCertificate2(now, now.AddYears(1), CertificateName, trust: false, subject: TestCertificateSubject);
|
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Equal(EnsureCertificateResult.Succeeded, result.ResultCode);
|
Assert.Equal(EnsureCertificateResult.Succeeded, result.ResultCode);
|
||||||
Assert.NotNull(result.Diagnostics);
|
|
||||||
Assert.NotEmpty(result.Diagnostics.Messages);
|
|
||||||
Assert.Empty(result.Diagnostics.Exceptions);
|
|
||||||
|
|
||||||
Assert.True(File.Exists(CertificateName));
|
Assert.True(File.Exists(CertificateName));
|
||||||
|
|
||||||
var exportedCertificate = new X509Certificate2(File.ReadAllBytes(CertificateName));
|
var exportedCertificate = new X509Certificate2(File.ReadAllBytes(CertificateName));
|
||||||
Assert.NotNull(exportedCertificate);
|
Assert.NotNull(exportedCertificate);
|
||||||
Assert.False(exportedCertificate.HasPrivateKey);
|
Assert.False(exportedCertificate.HasPrivateKey);
|
||||||
|
|
||||||
var httpsCertificates = manager.ListCertificates(CertificatePurpose.HTTPS, StoreName.My, StoreLocation.CurrentUser, isValid: false);
|
var httpsCertificates = _manager.ListCertificates(CertificatePurpose.HTTPS, StoreName.My, StoreLocation.CurrentUser, isValid: false);
|
||||||
var httpsCertificate = Assert.Single(httpsCertificates, c => c.Subject == TestCertificateSubject);
|
var httpsCertificate = Assert.Single(httpsCertificates, c => c.Subject == TestCertificateSubject);
|
||||||
Assert.True(httpsCertificate.HasPrivateKey);
|
Assert.True(httpsCertificate.HasPrivateKey);
|
||||||
Assert.Equal(TestCertificateSubject, httpsCertificate.Subject);
|
Assert.Equal(TestCertificateSubject, httpsCertificate.Subject);
|
||||||
|
|
@ -182,7 +94,7 @@ namespace Microsoft.AspNetCore.Certificates.Generation.Tests
|
||||||
httpsCertificate.Extensions.OfType<X509Extension>(),
|
httpsCertificate.Extensions.OfType<X509Extension>(),
|
||||||
e => e.Critical == false &&
|
e => e.Critical == false &&
|
||||||
e.Oid.Value == "1.3.6.1.4.1.311.84.1.1" &&
|
e.Oid.Value == "1.3.6.1.4.1.311.84.1.1" &&
|
||||||
Encoding.ASCII.GetString(e.RawData) == "ASP.NET Core HTTPS development certificate");
|
e.RawData[0] == _manager.AspNetHttpsCertificateVersion);
|
||||||
|
|
||||||
Assert.Equal(httpsCertificate.GetCertHashString(), exportedCertificate.GetCertHashString());
|
Assert.Equal(httpsCertificate.GetCertHashString(), exportedCertificate.GetCertHashString());
|
||||||
|
|
||||||
|
|
@ -211,32 +123,27 @@ namespace Microsoft.AspNetCore.Certificates.Generation.Tests
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact(Skip = "true")]
|
[ConditionalFact]
|
||||||
|
[SkipOnHelix("https://github.com/aspnet/AspNetCore/issues/6721")]
|
||||||
public void EnsureCreateHttpsCertificate_DoesNotCreateACertificate_WhenThereIsAnExistingHttpsCertificates()
|
public void EnsureCreateHttpsCertificate_DoesNotCreateACertificate_WhenThereIsAnExistingHttpsCertificates()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
const string CertificateName = nameof(EnsureCreateHttpsCertificate_DoesNotCreateACertificate_WhenThereIsAnExistingHttpsCertificates) + ".pfx";
|
const string CertificateName = nameof(EnsureCreateHttpsCertificate_DoesNotCreateACertificate_WhenThereIsAnExistingHttpsCertificates) + ".pfx";
|
||||||
var certificatePassword = Guid.NewGuid().ToString();
|
var certificatePassword = Guid.NewGuid().ToString();
|
||||||
|
|
||||||
var manager = new CertificateManager();
|
_fixture.CleanupCertificates();
|
||||||
|
|
||||||
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;
|
DateTimeOffset now = DateTimeOffset.UtcNow;
|
||||||
now = new DateTimeOffset(now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second, 0, now.Offset);
|
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);
|
_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);
|
var httpsCertificate = _manager.ListCertificates(CertificatePurpose.HTTPS, StoreName.My, StoreLocation.CurrentUser, isValid: false).Single(c => c.Subject == TestCertificateSubject);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = manager.EnsureAspNetCoreHttpsDevelopmentCertificate(now, now.AddYears(1), CertificateName, trust: false, includePrivateKey: true, password: certificatePassword, subject: TestCertificateSubject);
|
var result = _manager.EnsureAspNetCoreHttpsDevelopmentCertificate(now, now.AddYears(1), CertificateName, trust: false, includePrivateKey: true, password: certificatePassword, subject: TestCertificateSubject);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Equal(EnsureCertificateResult.ValidCertificatePresent, result);
|
Assert.Equal(EnsureCertificateResult.ValidCertificatePresent, result.ResultCode);
|
||||||
Assert.True(File.Exists(CertificateName));
|
Assert.True(File.Exists(CertificateName));
|
||||||
|
|
||||||
var exportedCertificate = new X509Certificate2(File.ReadAllBytes(CertificateName), certificatePassword);
|
var exportedCertificate = new X509Certificate2(File.ReadAllBytes(CertificateName), certificatePassword);
|
||||||
|
|
@ -247,39 +154,125 @@ namespace Microsoft.AspNetCore.Certificates.Generation.Tests
|
||||||
Assert.Equal(httpsCertificate.GetCertHashString(), exportedCertificate.GetCertHashString());
|
Assert.Equal(httpsCertificate.GetCertHashString(), exportedCertificate.GetCertHashString());
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact(Skip = "Requires user interaction")]
|
[ConditionalFact]
|
||||||
public void EnsureAspNetCoreHttpsDevelopmentCertificate_ReturnsCorrectResult_WhenUserCancelsTrustStepOnWindows()
|
[SkipOnHelix("https://github.com/aspnet/AspNetCore/issues/6721")]
|
||||||
|
public void EnsureCreateHttpsCertificate_ReturnsExpiredCertificateIfVersionIsIncorrect()
|
||||||
{
|
{
|
||||||
var manager = new CertificateManager();
|
_fixture.CleanupCertificates();
|
||||||
|
|
||||||
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;
|
DateTimeOffset now = DateTimeOffset.UtcNow;
|
||||||
now = new DateTimeOffset(now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second, 0, now.Offset);
|
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);
|
_manager.EnsureAspNetCoreHttpsDevelopmentCertificate(now, now.AddYears(1), path: null, trust: false, subject: TestCertificateSubject);
|
||||||
|
|
||||||
Assert.Equal(EnsureCertificateResult.UserCancelledTrustStep, trustFailed);
|
_manager.AspNetHttpsCertificateVersion = 2;
|
||||||
|
|
||||||
|
var httpsCertificateList = _manager.ListCertificates(CertificatePurpose.HTTPS, StoreName.My, StoreLocation.CurrentUser, isValid: true);
|
||||||
|
Assert.Empty(httpsCertificateList);
|
||||||
|
}
|
||||||
|
|
||||||
|
[ConditionalFact]
|
||||||
|
[SkipOnHelix("https://github.com/aspnet/AspNetCore/issues/6721")]
|
||||||
|
public void EnsureCreateHttpsCertificate_ReturnsExpiredCertificateForEmptyVersionField()
|
||||||
|
{
|
||||||
|
_fixture.CleanupCertificates();
|
||||||
|
|
||||||
|
DateTimeOffset now = DateTimeOffset.UtcNow;
|
||||||
|
now = new DateTimeOffset(now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second, 0, now.Offset);
|
||||||
|
_manager.AspNetHttpsCertificateVersion = 0;
|
||||||
|
_manager.EnsureAspNetCoreHttpsDevelopmentCertificate(now, now.AddYears(1), path: null, trust: false, subject: TestCertificateSubject);
|
||||||
|
|
||||||
|
_manager.AspNetHttpsCertificateVersion = 1;
|
||||||
|
|
||||||
|
var httpsCertificateList = _manager.ListCertificates(CertificatePurpose.HTTPS, StoreName.My, StoreLocation.CurrentUser, isValid: true);
|
||||||
|
Assert.Empty(httpsCertificateList);
|
||||||
|
}
|
||||||
|
|
||||||
|
[ConditionalFact]
|
||||||
|
[SkipOnHelix("https://github.com/aspnet/AspNetCore/issues/6721")]
|
||||||
|
public void EnsureCreateHttpsCertificate_ReturnsValidIfVersionIsZero()
|
||||||
|
{
|
||||||
|
_fixture.CleanupCertificates();
|
||||||
|
|
||||||
|
DateTimeOffset now = DateTimeOffset.UtcNow;
|
||||||
|
now = new DateTimeOffset(now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second, 0, now.Offset);
|
||||||
|
_manager.AspNetHttpsCertificateVersion = 0;
|
||||||
|
_manager.EnsureAspNetCoreHttpsDevelopmentCertificate(now, now.AddYears(1), path: null, trust: false, subject: TestCertificateSubject);
|
||||||
|
|
||||||
|
var httpsCertificateList = _manager.ListCertificates(CertificatePurpose.HTTPS, StoreName.My, StoreLocation.CurrentUser, isValid: true);
|
||||||
|
Assert.NotEmpty(httpsCertificateList);
|
||||||
|
}
|
||||||
|
|
||||||
|
[ConditionalFact]
|
||||||
|
[SkipOnHelix("https://github.com/aspnet/AspNetCore/issues/6721")]
|
||||||
|
public void EnsureCreateHttpsCertificate_ReturnValidIfCertIsNewer()
|
||||||
|
{
|
||||||
|
_fixture.CleanupCertificates();
|
||||||
|
|
||||||
|
DateTimeOffset now = DateTimeOffset.UtcNow;
|
||||||
|
now = new DateTimeOffset(now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second, 0, now.Offset);
|
||||||
|
_manager.AspNetHttpsCertificateVersion = 2;
|
||||||
|
_manager.EnsureAspNetCoreHttpsDevelopmentCertificate(now, now.AddYears(1), path: null, trust: false, subject: TestCertificateSubject);
|
||||||
|
|
||||||
|
_manager.AspNetHttpsCertificateVersion = 1;
|
||||||
|
var httpsCertificateList = _manager.ListCertificates(CertificatePurpose.HTTPS, StoreName.My, StoreLocation.CurrentUser, isValid: true);
|
||||||
|
Assert.NotEmpty(httpsCertificateList);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact(Skip = "Requires user interaction")]
|
||||||
|
public void EnsureAspNetCoreHttpsDevelopmentCertificate_ReturnsCorrectResult_WhenUserCancelsTrustStepOnWindows()
|
||||||
|
{
|
||||||
|
_fixture.CleanupCertificates();
|
||||||
|
|
||||||
|
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.ResultCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact(Skip = "Requires user interaction")]
|
[Fact(Skip = "Requires user interaction")]
|
||||||
public void EnsureAspNetCoreHttpsDevelopmentCertificate_CanRemoveCertificates()
|
public void EnsureAspNetCoreHttpsDevelopmentCertificate_CanRemoveCertificates()
|
||||||
{
|
{
|
||||||
var manager = new CertificateManager();
|
_fixture.CleanupCertificates();
|
||||||
|
|
||||||
DateTimeOffset now = DateTimeOffset.UtcNow;
|
DateTimeOffset now = DateTimeOffset.UtcNow;
|
||||||
now = new DateTimeOffset(now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second, 0, now.Offset);
|
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.EnsureAspNetCoreHttpsDevelopmentCertificate(now, now.AddYears(1), path: null, trust: true, subject: TestCertificateSubject);
|
||||||
|
|
||||||
manager.CleanupHttpsCertificates(TestCertificateSubject);
|
_manager.CleanupHttpsCertificates(TestCertificateSubject);
|
||||||
|
|
||||||
Assert.Empty(manager.ListCertificates(CertificatePurpose.HTTPS, StoreName.My, StoreLocation.CurrentUser, isValid: false).Where(c => c.Subject == TestCertificateSubject));
|
Assert.Empty(_manager.ListCertificates(CertificatePurpose.HTTPS, StoreName.My, StoreLocation.CurrentUser, isValid: false).Where(c => c.Subject == TestCertificateSubject));
|
||||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||||
{
|
{
|
||||||
Assert.Empty(manager.ListCertificates(CertificatePurpose.HTTPS, StoreName.Root, StoreLocation.CurrentUser, isValid: false).Where(c => c.Subject == TestCertificateSubject));
|
Assert.Empty(_manager.ListCertificates(CertificatePurpose.HTTPS, StoreName.Root, StoreLocation.CurrentUser, isValid: false).Where(c => c.Subject == TestCertificateSubject));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CertFixture : IDisposable
|
||||||
|
{
|
||||||
|
public const string TestCertificateSubject = "CN=aspnet.test";
|
||||||
|
|
||||||
|
public CertFixture()
|
||||||
|
{
|
||||||
|
Manager = new CertificateManager();
|
||||||
|
|
||||||
|
CleanupCertificates();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal CertificateManager Manager { get; set; }
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
CleanupCertificates();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void CleanupCertificates()
|
||||||
|
{
|
||||||
|
Manager.RemoveAllCertificates(CertificatePurpose.HTTPS, StoreName.My, StoreLocation.CurrentUser, TestCertificateSubject);
|
||||||
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||||
|
{
|
||||||
|
Manager.RemoveAllCertificates(CertificatePurpose.HTTPS, StoreName.Root, StoreLocation.CurrentUser, TestCertificateSubject);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "dotnet-watch", "dotnet-watc
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "dotnet-watch.Tests", "dotnet-watch\test\dotnet-watch.Tests.csproj", "{63F7E822-D1E2-4C41-8ABF-60B9E3A9C54C}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "dotnet-watch.Tests", "dotnet-watch\test\dotnet-watch.Tests.csproj", "{63F7E822-D1E2-4C41-8ABF-60B9E3A9C54C}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "dotnet-dev-certs", "dotnet-dev-certs\src\dotnet-dev-certs.csproj", "{0D6D5693-7E0C-4FE8-B4AA-21207B2650AA}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.DeveloperCertificates.XPlat", "FirstRunCertGenerator\src\Microsoft.AspNetCore.DeveloperCertificates.XPlat.csproj", "{7BBDBDA2-299F-4C36-8338-23C525901DE0}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.DeveloperCertificates.XPlat.Tests", "FirstRunCertGenerator\test\Microsoft.AspNetCore.DeveloperCertificates.XPlat.Tests.csproj", "{1EC6FA27-40A5-433F-8CA1-636E7ED8863E}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
|
@ -21,6 +27,18 @@ Global
|
||||||
{63F7E822-D1E2-4C41-8ABF-60B9E3A9C54C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{63F7E822-D1E2-4C41-8ABF-60B9E3A9C54C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{63F7E822-D1E2-4C41-8ABF-60B9E3A9C54C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{63F7E822-D1E2-4C41-8ABF-60B9E3A9C54C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{63F7E822-D1E2-4C41-8ABF-60B9E3A9C54C}.Release|Any CPU.Build.0 = Release|Any CPU
|
{63F7E822-D1E2-4C41-8ABF-60B9E3A9C54C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{0D6D5693-7E0C-4FE8-B4AA-21207B2650AA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{0D6D5693-7E0C-4FE8-B4AA-21207B2650AA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{0D6D5693-7E0C-4FE8-B4AA-21207B2650AA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{0D6D5693-7E0C-4FE8-B4AA-21207B2650AA}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{7BBDBDA2-299F-4C36-8338-23C525901DE0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{7BBDBDA2-299F-4C36-8338-23C525901DE0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{7BBDBDA2-299F-4C36-8338-23C525901DE0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{7BBDBDA2-299F-4C36-8338-23C525901DE0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{1EC6FA27-40A5-433F-8CA1-636E7ED8863E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{1EC6FA27-40A5-433F-8CA1-636E7ED8863E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{1EC6FA27-40A5-433F-8CA1-636E7ED8863E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{1EC6FA27-40A5-433F-8CA1-636E7ED8863E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|
|
||||||
|
|
@ -81,6 +81,7 @@ namespace Microsoft.AspNetCore.DeveloperCertificates.Tools
|
||||||
(check.HasValue() && (exportPath.HasValue() || password.HasValue() || clean.HasValue())))
|
(check.HasValue() && (exportPath.HasValue() || password.HasValue() || clean.HasValue())))
|
||||||
{
|
{
|
||||||
reporter.Error(@"Incompatible set of flags. Sample usages
|
reporter.Error(@"Incompatible set of flags. Sample usages
|
||||||
|
'dotnet dev-certs https'
|
||||||
'dotnet dev-certs https --clean'
|
'dotnet dev-certs https --clean'
|
||||||
'dotnet dev-certs https --check --trust'
|
'dotnet dev-certs https --check --trust'
|
||||||
'dotnet dev-certs https -ep ./certificate.pfx -p password --trust'");
|
'dotnet dev-certs https -ep ./certificate.pfx -p password --trust'");
|
||||||
|
|
@ -133,7 +134,7 @@ namespace Microsoft.AspNetCore.DeveloperCertificates.Tools
|
||||||
}
|
}
|
||||||
|
|
||||||
manager.CleanupHttpsCertificates();
|
manager.CleanupHttpsCertificates();
|
||||||
reporter.Verbose("HTTPS development certificates successfully removed from the machine.");
|
reporter.Output("HTTPS development certificates successfully removed from the machine.");
|
||||||
return Success;
|
return Success;
|
||||||
}
|
}
|
||||||
catch(Exception e)
|
catch(Exception e)
|
||||||
|
|
@ -152,12 +153,12 @@ namespace Microsoft.AspNetCore.DeveloperCertificates.Tools
|
||||||
var certificates = certificateManager.ListCertificates(CertificatePurpose.HTTPS, StoreName.My, StoreLocation.CurrentUser, isValid: true);
|
var certificates = certificateManager.ListCertificates(CertificatePurpose.HTTPS, StoreName.My, StoreLocation.CurrentUser, isValid: true);
|
||||||
if (certificates.Count == 0)
|
if (certificates.Count == 0)
|
||||||
{
|
{
|
||||||
reporter.Verbose("No valid certificate found.");
|
reporter.Output("No valid certificate found.");
|
||||||
return ErrorNoValidCertificateFound;
|
return ErrorNoValidCertificateFound;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
reporter.Verbose("A valid certificate was found.");
|
reporter.Output("A valid certificate was found.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (trust != null && trust.HasValue())
|
if (trust != null && trust.HasValue())
|
||||||
|
|
@ -166,13 +167,13 @@ namespace Microsoft.AspNetCore.DeveloperCertificates.Tools
|
||||||
var trustedCertificates = certificateManager.ListCertificates(CertificatePurpose.HTTPS, store, StoreLocation.CurrentUser, isValid: true);
|
var trustedCertificates = certificateManager.ListCertificates(CertificatePurpose.HTTPS, store, StoreLocation.CurrentUser, isValid: true);
|
||||||
if (!certificates.Any(c => certificateManager.IsTrusted(c)))
|
if (!certificates.Any(c => certificateManager.IsTrusted(c)))
|
||||||
{
|
{
|
||||||
reporter.Verbose($@"The following certificates were found, but none of them is trusted:
|
reporter.Output($@"The following certificates were found, but none of them is trusted:
|
||||||
{string.Join(Environment.NewLine, certificates.Select(c => $"{c.Subject} - {c.Thumbprint}"))}");
|
{string.Join(Environment.NewLine, certificates.Select(c => $"{c.Subject} - {c.Thumbprint}"))}");
|
||||||
return ErrorCertificateNotTrusted;
|
return ErrorCertificateNotTrusted;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
reporter.Verbose("A trusted certificate was found.");
|
reporter.Output("A trusted certificate was found.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -207,7 +208,9 @@ namespace Microsoft.AspNetCore.DeveloperCertificates.Tools
|
||||||
password.HasValue(),
|
password.HasValue(),
|
||||||
password.Value());
|
password.Value());
|
||||||
|
|
||||||
switch (result)
|
reporter.Verbose(string.Join(Environment.NewLine, result.Diagnostics.Messages));
|
||||||
|
|
||||||
|
switch (result.ResultCode)
|
||||||
{
|
{
|
||||||
case EnsureCertificateResult.Succeeded:
|
case EnsureCertificateResult.Succeeded:
|
||||||
reporter.Output("The HTTPS developer certificate was generated successfully.");
|
reporter.Output("The HTTPS developer certificate was generated successfully.");
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue