Add task for generating ssl certificates with MSBuild
This commit is contained in:
parent
0e3d091fb7
commit
7ac72dd7d8
|
|
@ -1,6 +1,6 @@
|
|||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.26210.0
|
||||
VisualStudioVersion = 15.0.26510.0
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{66517987-2A5A-4330-B130-207039378FD4}"
|
||||
EndProject
|
||||
|
|
@ -24,6 +24,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.DotNet.Watcher.To
|
|||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Caching.SqlConfig.Tools", "src\Microsoft.Extensions.Caching.SqlConfig.Tools\Microsoft.Extensions.Caching.SqlConfig.Tools.csproj", "{53F3B53D-303A-4DAA-9C38-4F55195FA5B9}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.CertificateGeneration.Task", "src\Microsoft.AspNetCore.CertificateGeneration.Task\Microsoft.AspNetCore.CertificateGeneration.Task.csproj", "{7B293291-26F4-47F0-9C2F-E396F35A4280}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.CertificateGeneration.Task.Tests", "test\Microsoft.AspNetcore.CertificateGeneration.Task.Tests\Microsoft.AspNetCore.CertificateGeneration.Task.Tests.csproj", "{3A7EF01A-073B-4123-850D-DFA4701EBE5B}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
|
@ -54,6 +58,14 @@ Global
|
|||
{53F3B53D-303A-4DAA-9C38-4F55195FA5B9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{53F3B53D-303A-4DAA-9C38-4F55195FA5B9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{53F3B53D-303A-4DAA-9C38-4F55195FA5B9}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{7B293291-26F4-47F0-9C2F-E396F35A4280}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{7B293291-26F4-47F0-9C2F-E396F35A4280}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{7B293291-26F4-47F0-9C2F-E396F35A4280}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{7B293291-26F4-47F0-9C2F-E396F35A4280}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{3A7EF01A-073B-4123-850D-DFA4701EBE5B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{3A7EF01A-073B-4123-850D-DFA4701EBE5B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{3A7EF01A-073B-4123-850D-DFA4701EBE5B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{3A7EF01A-073B-4123-850D-DFA4701EBE5B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
|
@ -65,5 +77,7 @@ Global
|
|||
{7B331122-83B1-4F08-A119-DC846959844C} = {F5B382BC-258F-46E1-AC3D-10E5CCD55134}
|
||||
{8A2E6961-6B12-4A8E-8215-3E7301D52EAC} = {F5B382BC-258F-46E1-AC3D-10E5CCD55134}
|
||||
{53F3B53D-303A-4DAA-9C38-4F55195FA5B9} = {66517987-2A5A-4330-B130-207039378FD4}
|
||||
{7B293291-26F4-47F0-9C2F-E396F35A4280} = {66517987-2A5A-4330-B130-207039378FD4}
|
||||
{3A7EF01A-073B-4123-850D-DFA4701EBE5B} = {F5B382BC-258F-46E1-AC3D-10E5CCD55134}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
|
|
|||
|
|
@ -18,6 +18,13 @@
|
|||
"packageTypes": [
|
||||
"DotnetCliTool"
|
||||
]
|
||||
},
|
||||
"Microsoft.AspNetCore.CertificateGeneration.Task": {
|
||||
"exclusions":{
|
||||
"BUILD_ITEMS_FRAMEWORK": {
|
||||
"*": "This is an MSBuild task intended to run through dotnet msbuild /t:Target independently of whether your project targets full framework or .net core."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
<CoreFxVersion>4.3.0</CoreFxVersion>
|
||||
<InternalAspNetCoreSdkVersion>2.1.0-*</InternalAspNetCoreSdkVersion>
|
||||
<MsBuildPackageVersions>15.1.1012</MsBuildPackageVersions>
|
||||
<NETStandardImplicitPackageVersion>$(BundledNETStandardPackageVersion)</NETStandardImplicitPackageVersion>
|
||||
<TestSdkVersion>15.3.0-*</TestSdkVersion>
|
||||
<XunitVersion>2.3.0-beta2-*</XunitVersion>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,96 @@
|
|||
// 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.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security.Cryptography;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
|
||||
namespace Microsoft.AspNetCore.CertificateGeneration.Task
|
||||
{
|
||||
internal static class CertificateManager
|
||||
{
|
||||
public static X509Certificate2 GenerateSSLCertificate(
|
||||
string subjectName,
|
||||
IEnumerable<string> subjectAlternativeName,
|
||||
string friendlyName,
|
||||
DateTimeOffset notBefore,
|
||||
DateTimeOffset expires,
|
||||
StoreName storeName,
|
||||
StoreLocation storeLocation)
|
||||
{
|
||||
using (var rsa = RSA.Create(2048))
|
||||
{
|
||||
var signingRequest = new CertificateRequest(
|
||||
new X500DistinguishedName(subjectName), rsa, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
|
||||
|
||||
var enhancedKeyUsage = new OidCollection();
|
||||
enhancedKeyUsage.Add(new Oid("1.3.6.1.5.5.7.3.1", "Server Authentication"));
|
||||
signingRequest.CertificateExtensions.Add(new X509EnhancedKeyUsageExtension(enhancedKeyUsage, critical: true));
|
||||
signingRequest.CertificateExtensions.Add(new X509KeyUsageExtension(X509KeyUsageFlags.KeyEncipherment, critical: true));
|
||||
signingRequest.CertificateExtensions.Add(
|
||||
new X509BasicConstraintsExtension(
|
||||
certificateAuthority: false,
|
||||
hasPathLengthConstraint: false,
|
||||
pathLengthConstraint: 0,
|
||||
critical: true));
|
||||
|
||||
var sanBuilder = new SubjectAlternativeNameBuilder();
|
||||
foreach (var alternativeName in subjectAlternativeName)
|
||||
{
|
||||
sanBuilder.AddDnsName(alternativeName);
|
||||
}
|
||||
signingRequest.CertificateExtensions.Add(sanBuilder.Build());
|
||||
|
||||
var certificate = signingRequest.CreateSelfSigned(notBefore, expires);
|
||||
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
certificate.FriendlyName = friendlyName;
|
||||
}
|
||||
|
||||
SaveCertificate(storeName, storeLocation, certificate);
|
||||
|
||||
return certificate;
|
||||
}
|
||||
}
|
||||
|
||||
private static void SaveCertificate(StoreName storeName, StoreLocation storeLocation, X509Certificate2 certificate)
|
||||
{
|
||||
// We need to take this step so that the key gets persisted.
|
||||
var imported = certificate;
|
||||
if (!RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
||||
{
|
||||
var export = certificate.Export(X509ContentType.Pkcs12, "");
|
||||
imported = new X509Certificate2(export, "", X509KeyStorageFlags.PersistKeySet);
|
||||
Array.Clear(export, 0, export.Length);
|
||||
}
|
||||
|
||||
using (var store = new X509Store(storeName, storeLocation))
|
||||
{
|
||||
store.Open(OpenFlags.ReadWrite);
|
||||
store.Add(imported);
|
||||
store.Close();
|
||||
};
|
||||
}
|
||||
|
||||
public static X509Certificate2 FindCertificate(string subjectValue, StoreName storeName, StoreLocation storeLocation)
|
||||
{
|
||||
using (var store = new X509Store(storeName, storeLocation))
|
||||
{
|
||||
store.Open(OpenFlags.ReadOnly);
|
||||
var certificates = store.Certificates.Find(X509FindType.FindBySubjectDistinguishedName, subjectValue, validOnly: false);
|
||||
var current = DateTimeOffset.UtcNow;
|
||||
|
||||
var found = certificates.OfType<X509Certificate2>()
|
||||
.Where(c => c.NotBefore <= current && current <= c.NotAfter && c.HasPrivateKey)
|
||||
.FirstOrDefault();
|
||||
store.Close();
|
||||
|
||||
return found;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
// 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.Security.Cryptography.X509Certificates;
|
||||
using Microsoft.Build.Framework;
|
||||
|
||||
namespace Microsoft.AspNetCore.CertificateGeneration.Task
|
||||
{
|
||||
public class GenerateSSLCertificateTask : Build.Utilities.Task
|
||||
{
|
||||
public bool Force { get; set; }
|
||||
|
||||
protected string Subject { get; set; } = "CN=localhost";
|
||||
|
||||
public override bool Execute()
|
||||
{
|
||||
var subjectValue = Subject;
|
||||
var sansValue = new List<string> { "localhost" };
|
||||
var friendlyNameValue = "ASP.NET Core HTTPS development certificate";
|
||||
var notBeforeValue = DateTime.UtcNow;
|
||||
var expiresValue = DateTime.UtcNow.AddYears(1);
|
||||
var storeNameValue = StoreName.My;
|
||||
var storeLocationValue = StoreLocation.CurrentUser;
|
||||
|
||||
var cert = CertificateManager.FindCertificate(subjectValue, storeNameValue, storeLocationValue);
|
||||
|
||||
if (cert != null && !Force)
|
||||
{
|
||||
LogMessage($"A certificate with subject name '{Subject}' already exists. Skipping certificate generation.");
|
||||
return true;
|
||||
}
|
||||
|
||||
var generated = CertificateManager.GenerateSSLCertificate(subjectValue, sansValue, friendlyNameValue, notBeforeValue, expiresValue, storeNameValue, storeLocationValue);
|
||||
LogMessage($"Generated certificate {generated.SubjectName.Name} - {generated.Thumbprint} - {generated.FriendlyName}");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected virtual void LogMessage(string message) => Log.LogMessage(MessageImportance.High, message);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<Import Project="..\..\build\common.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
<Description>MSBuild target for generating HTTPS certificates for development cross-platform.</Description>
|
||||
<BuildOutputTargetFolder>tools</BuildOutputTargetFolder>
|
||||
<PackageTags>asp.net;ssl;certificates</PackageTags>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="build\**\*.targets" Pack="true" PackagePath="%(Identity)" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Build.Tasks.Core" Version="$(MsBuildPackageVersions)" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
Microsoft.AspNetCore.CertificateGeneration.Task
|
||||
===============================================
|
||||
Microsoft.AspNetCore.CertificateGeneration.Task is an MSBuild task to generate SSL certificates
|
||||
for use in ASP.NET Core for development purposes.
|
||||
|
||||
### How To Install
|
||||
|
||||
Install `Microsoft.AspNetCore.CertificateGeneration.Task` as a `PackageReference` to your project.
|
||||
|
||||
```xml
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.CertificateGeneration.Task" Version="2.0.0" />
|
||||
</ItemGroup>
|
||||
```
|
||||
|
||||
### How To Use
|
||||
|
||||
The command must be executed in the directory that contains the project with the reference to the package.
|
||||
|
||||
Usage: dotnet msbuild /t:GenerateSSLCertificate [/p:ForceGenerateSSLCertificate=true]
|
||||
|
||||
### Testing scenarios
|
||||
|
||||
On a machine without an SSL certificate generated by this task. Create a netcoreapp2.0 mvc application using
|
||||
|
||||
```
|
||||
dotnet new mvc
|
||||
```
|
||||
|
||||
Then try to run the app using:
|
||||
|
||||
```
|
||||
dotnet run
|
||||
```
|
||||
|
||||
When the application fails to run due to a missing SSL certificate. Run:
|
||||
|
||||
```
|
||||
dotnet msbuild /t:GenerateSSLCertificate
|
||||
```
|
||||
|
||||
Run the application again using:
|
||||
|
||||
```
|
||||
dotnet run
|
||||
```
|
||||
|
||||
The application should run successfully. You will still have to trust the certificate as a separate step.
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
|
||||
<!--
|
||||
********************************************************************************************
|
||||
Target: GenerateSSLCertificate
|
||||
|
||||
Generates an SSL certificate to use for development in ASP.NET Core applications
|
||||
********************************************************************************************
|
||||
-->
|
||||
<PropertyGroup>
|
||||
<_SSLCertificateGenerationTaskAssembly>$(MSBuildThisFileDirectory)..\tools\netcoreapp2.0\Microsoft.AspNetCore.CertificateGeneration.Task.dll</_SSLCertificateGenerationTaskAssembly>
|
||||
</PropertyGroup>
|
||||
|
||||
<UsingTask TaskName="Microsoft.AspNetCore.CertificateGeneration.Task.GenerateSSLCertificateTask"
|
||||
AssemblyFile="$(_SSLCertificateGenerationTaskAssembly)" />
|
||||
|
||||
<Target Name="GenerateSSLCertificate">
|
||||
<GenerateSSLCertificateTask Force="$(ForceGenerateSSLCertificate)" />
|
||||
</Target>
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,227 @@
|
|||
// 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.Runtime.InteropServices;
|
||||
using System.Security.Cryptography;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.CertificateGeneration.Task
|
||||
{
|
||||
public class GenerateSSLCertificateTaskTest : IDisposable
|
||||
{
|
||||
private const string TestSubject = "CN=test.ssl.localhost";
|
||||
|
||||
[Fact]
|
||||
public void GenerateSSLCertificateTaskTest_CreatesCertificate_IfNoCertificateIsFound()
|
||||
{
|
||||
// Arrange
|
||||
EnsureCleanUp();
|
||||
var task = new TestGenerateSSLCertificateTask();
|
||||
|
||||
// Act
|
||||
var result = task.Execute();
|
||||
|
||||
// Assert
|
||||
Assert.True(result);
|
||||
var certificates = GetTestCertificates();
|
||||
Assert.Equal(1, certificates.Count);
|
||||
Assert.Equal(1, task.Messages.Count);
|
||||
Assert.StartsWith($"Generated certificate {TestSubject}", task.Messages[0]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GenerateSSLCertificateTaskTest_CreatesCertificate_IfFoundCertificateHasExpired()
|
||||
{
|
||||
// Arrange
|
||||
EnsureCleanUp();
|
||||
CreateCertificate(notBefore: DateTimeOffset.UtcNow.AddYears(-2), expires: DateTimeOffset.UtcNow.AddYears(-1));
|
||||
|
||||
var task = new TestGenerateSSLCertificateTask();
|
||||
|
||||
// Act
|
||||
var result = task.Execute();
|
||||
|
||||
// Assert
|
||||
Assert.True(result);
|
||||
var certificates = GetTestCertificates();
|
||||
Assert.Equal(2, certificates.Count);
|
||||
Assert.Equal(1, task.Messages.Count);
|
||||
Assert.StartsWith($"Generated certificate {TestSubject}", task.Messages[0]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GenerateSSLCertificateTaskTest_CreatesCertificate_IfFoundCertificateIsNotYetValid()
|
||||
{
|
||||
// Arrange
|
||||
EnsureCleanUp();
|
||||
CreateCertificate(notBefore: DateTimeOffset.UtcNow.AddYears(1), expires: DateTimeOffset.UtcNow.AddYears(2));
|
||||
|
||||
var task = new TestGenerateSSLCertificateTask();
|
||||
|
||||
// Act
|
||||
var result = task.Execute();
|
||||
|
||||
// Assert
|
||||
Assert.True(result);
|
||||
var certificates = GetTestCertificates();
|
||||
Assert.Equal(2, certificates.Count);
|
||||
Assert.Equal(1, task.Messages.Count);
|
||||
Assert.StartsWith($"Generated certificate {TestSubject}", task.Messages[0]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GenerateSSLCertificateTaskTest_CreatesCertificate_IfFoundCertificateDoesNotHavePrivateKeys()
|
||||
{
|
||||
// Arrange
|
||||
EnsureCleanUp();
|
||||
CreateCertificate(savePrivateKey: false);
|
||||
var task = new TestGenerateSSLCertificateTask();
|
||||
|
||||
// Act
|
||||
var result = task.Execute();
|
||||
|
||||
// Assert
|
||||
Assert.True(result);
|
||||
var certificates = GetTestCertificates();
|
||||
Assert.Equal(2, certificates.Count);
|
||||
Assert.Equal(1, task.Messages.Count);
|
||||
Assert.StartsWith($"Generated certificate {TestSubject}", task.Messages[0]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GenerateSSLCertificateTaskTest_DoesNothing_IfValidCertificateIsFound()
|
||||
{
|
||||
// Arrange
|
||||
EnsureCleanUp();
|
||||
CreateCertificate();
|
||||
var task = new TestGenerateSSLCertificateTask();
|
||||
|
||||
// Act
|
||||
var result = task.Execute();
|
||||
|
||||
// Assert
|
||||
Assert.True(result);
|
||||
var certificates = GetTestCertificates();
|
||||
Assert.Equal(1, certificates.Count);
|
||||
Assert.Equal(1, task.Messages.Count);
|
||||
Assert.Equal($"A certificate with subject name '{TestSubject}' already exists. Skipping certificate generation.", task.Messages[0]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GenerateSSLCertificateTaskTest_CreatesACertificateWhenThereIsAlreadyAValidCertificate_IfForceIsSpecified()
|
||||
{
|
||||
// Arrange
|
||||
EnsureCleanUp();
|
||||
CreateCertificate();
|
||||
var task = new TestGenerateSSLCertificateTask() { Force = true };
|
||||
|
||||
// Act
|
||||
var result = task.Execute();
|
||||
|
||||
// Assert
|
||||
Assert.True(result);
|
||||
var certificates = GetTestCertificates();
|
||||
Assert.Equal(2, certificates.Count);
|
||||
Assert.Equal(1, task.Messages.Count);
|
||||
Assert.StartsWith($"Generated certificate {TestSubject}", task.Messages[0]);
|
||||
}
|
||||
|
||||
public X509CertificateCollection GetTestCertificates()
|
||||
{
|
||||
using (var store = new X509Store(StoreName.My, StoreLocation.CurrentUser))
|
||||
{
|
||||
store.Open(OpenFlags.ReadWrite);
|
||||
var certificates = store.Certificates.Find(X509FindType.FindBySubjectDistinguishedName, TestSubject, validOnly: false);
|
||||
store.Close();
|
||||
|
||||
return certificates;
|
||||
}
|
||||
}
|
||||
|
||||
private void EnsureCleanUp()
|
||||
{
|
||||
using (var store = new X509Store(StoreName.My, StoreLocation.CurrentUser))
|
||||
{
|
||||
store.Open(OpenFlags.ReadWrite);
|
||||
var certificates = store.Certificates.Find(X509FindType.FindBySubjectDistinguishedName, TestSubject, validOnly: false);
|
||||
store.RemoveRange(certificates);
|
||||
store.Close();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
EnsureCleanUp();
|
||||
}
|
||||
|
||||
private void CreateCertificate(
|
||||
DateTimeOffset notBefore = default(DateTimeOffset),
|
||||
DateTimeOffset expires = default(DateTimeOffset),
|
||||
bool savePrivateKey = true)
|
||||
{
|
||||
using (var rsa = RSA.Create(2048))
|
||||
{
|
||||
var signingRequest = new CertificateRequest(
|
||||
new X500DistinguishedName(TestSubject), rsa, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
|
||||
|
||||
var enhancedKeyUsage = new OidCollection();
|
||||
enhancedKeyUsage.Add(new Oid("1.3.6.1.5.5.7.3.1", "Server Authentication"));
|
||||
signingRequest.CertificateExtensions.Add(new X509EnhancedKeyUsageExtension(enhancedKeyUsage, critical: true));
|
||||
signingRequest.CertificateExtensions.Add(new X509KeyUsageExtension(X509KeyUsageFlags.KeyEncipherment, critical: true));
|
||||
signingRequest.CertificateExtensions.Add(
|
||||
new X509BasicConstraintsExtension(
|
||||
certificateAuthority: false,
|
||||
hasPathLengthConstraint: false,
|
||||
pathLengthConstraint: 0,
|
||||
critical: true));
|
||||
|
||||
var sanBuilder = new SubjectAlternativeNameBuilder();
|
||||
sanBuilder.AddDnsName(TestSubject.Replace("CN=", ""));
|
||||
signingRequest.CertificateExtensions.Add(sanBuilder.Build());
|
||||
|
||||
var certificate = signingRequest.CreateSelfSigned(
|
||||
notBefore == default(DateTimeOffset) ? DateTimeOffset.Now : notBefore,
|
||||
expires == default(DateTimeOffset) ? DateTimeOffset.Now.AddYears(1) : expires);
|
||||
|
||||
|
||||
var imported = certificate;
|
||||
if (!RuntimeInformation.IsOSPlatform(OSPlatform.OSX) && savePrivateKey)
|
||||
{
|
||||
var export = certificate.Export(X509ContentType.Pkcs12, "");
|
||||
|
||||
imported = new X509Certificate2(export, "", X509KeyStorageFlags.PersistKeySet);
|
||||
Array.Clear(export, 0, export.Length);
|
||||
}
|
||||
else if (!savePrivateKey)
|
||||
{
|
||||
var export = certificate.Export(X509ContentType.Cert, "");
|
||||
|
||||
imported = new X509Certificate2(export, "", X509KeyStorageFlags.PersistKeySet);
|
||||
Array.Clear(export, 0, export.Length);
|
||||
}
|
||||
|
||||
using (var store = new X509Store(StoreName.My, StoreLocation.CurrentUser))
|
||||
{
|
||||
store.Open(OpenFlags.ReadWrite);
|
||||
store.Add(imported);
|
||||
store.Close();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private class TestGenerateSSLCertificateTask : GenerateSSLCertificateTask
|
||||
{
|
||||
public TestGenerateSSLCertificateTask()
|
||||
{
|
||||
Subject = TestSubject;
|
||||
}
|
||||
|
||||
public IList<string> Messages { get; set; } = new List<string>();
|
||||
|
||||
protected override void LogMessage(string message) => Messages.Add(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<Import Project="..\..\build\common.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.CertificateGeneration.Task\Microsoft.AspNetCore.CertificateGeneration.Task.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Testing" Version="$(AspNetCoreVersion)" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Testing" Version="$(AspNetCoreVersion)" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="$(TestSdkVersion)" />
|
||||
<PackageReference Include="xunit" Version="$(XunitVersion)" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="$(XunitVersion)" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
Loading…
Reference in New Issue