Skip to content

Commit 600ee7f

Browse files
committed
Revise certificates
1 parent fae5df1 commit 600ee7f

File tree

5 files changed

+94
-42
lines changed

5 files changed

+94
-42
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,7 @@ ClientBin/
239239
*.pfx
240240
*.key
241241
*.pem
242+
*.cer
242243
*.publishsettings
243244
orleans.codegen.cs
244245

warp-gateway/Extensions/CertificateHelper.cs

Lines changed: 75 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
using System.Net;
1+
using System;
2+
using System.Linq;
3+
using System.Net;
24
using System.Net.NetworkInformation;
35
using System.Security.Cryptography;
46
using System.Security.Cryptography.X509Certificates;
@@ -19,10 +21,23 @@ public static class CertificateHelper
1921
}
2022
}
2123

22-
public static X509Certificate2 CreateSelfSigned(string subjectName)
24+
public static X509Certificate2 CreateRoot(string subjectName)
2325
{
24-
using var encryptionAlghorithm = RSA.Create(2048);
25-
var certificateRequest = new CertificateRequest($"CN={subjectName}", encryptionAlghorithm, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
26+
using var privateKey = RSA.Create(2048);
27+
var certificateRequest = new CertificateRequest($"CN={subjectName}", privateKey, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
28+
certificateRequest.CertificateExtensions.Add(new X509BasicConstraintsExtension(true, false, 0, true));
29+
certificateRequest.CertificateExtensions.Add(new X509KeyUsageExtension(X509KeyUsageFlags.KeyCertSign | X509KeyUsageFlags.CrlSign, true));
30+
certificateRequest.CertificateExtensions.Add(new X509SubjectKeyIdentifierExtension(certificateRequest.PublicKey, false));
31+
32+
var validFrom = DateTimeOffset.UtcNow;
33+
var validUntil = validFrom.AddYears(1);
34+
return certificateRequest.CreateSelfSigned(validFrom, validUntil);
35+
}
36+
37+
public static X509Certificate2 Create(string subjectName, X509Certificate2? parentCertificate = null)
38+
{
39+
using var privateKey = RSA.Create(2048);
40+
var certificateRequest = new CertificateRequest($"CN={subjectName}", privateKey, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
2641
certificateRequest.CertificateExtensions.Add(new X509BasicConstraintsExtension(false, false, 0, false));
2742
certificateRequest.CertificateExtensions.Add(new X509KeyUsageExtension(X509KeyUsageFlags.DigitalSignature | X509KeyUsageFlags.KeyEncipherment, true));
2843
certificateRequest.CertificateExtensions.Add(
@@ -33,16 +48,32 @@ public static X509Certificate2 CreateSelfSigned(string subjectName)
3348
new Oid("1.3.6.1.5.5.7.3.2") //Client authentication
3449
},
3550
false));
51+
//certificateRequest.CertificateExtensions.Add(new X509SubjectKeyIdentifierExtension(certificateRequest.PublicKey, false));
3652

3753
var alternativeNameBuilder = new SubjectAlternativeNameBuilder();
38-
foreach (var name in GetNames())
54+
foreach (var name in GetDnsNames())
3955
{
4056
alternativeNameBuilder.AddDnsName(name);
4157
}
58+
foreach (var address in GetIPAddresses())
59+
{
60+
alternativeNameBuilder.AddIpAddress(address);
61+
}
4262
certificateRequest.CertificateExtensions.Add(alternativeNameBuilder.Build());
4363

44-
var now = DateTimeOffset.UtcNow;
45-
return certificateRequest.CreateSelfSigned(now, now.AddYears(1));
64+
var validFrom = DateTimeOffset.UtcNow;
65+
var validUntil = validFrom.AddYears(1);
66+
if (parentCertificate == null)
67+
{
68+
return certificateRequest.CreateSelfSigned(validFrom, validUntil);
69+
}
70+
else
71+
{
72+
if (parentCertificate.NotBefore > validFrom) validFrom = parentCertificate.NotBefore;
73+
if (parentCertificate.NotAfter < validUntil) validUntil = parentCertificate.NotAfter;
74+
var childCertificate = certificateRequest.Create(parentCertificate, validFrom, validUntil, Guid.NewGuid().ToByteArray());
75+
return childCertificate.CopyWithPrivateKey(privateKey);
76+
}
4677
}
4778

4879
public static bool Validate(X509Certificate2 certificate2)
@@ -54,10 +85,17 @@ public static bool Validate(X509Certificate2 certificate2)
5485
.Where(p => p.Oid?.Value == "2.5.29.17") //Subject Alternative Name
5586
.Select(p => p.Format(false))
5687
.FirstOrDefault() ?? "";
57-
foreach (var name in GetNames())
88+
89+
foreach (var name in GetDnsNames())
5890
{
5991
if (!alternateNames.Contains(name, StringComparison.InvariantCultureIgnoreCase)) return false;
6092
}
93+
94+
foreach (var address in GetIPAddresses())
95+
{
96+
if (!alternateNames.Contains(address.ToString(), StringComparison.InvariantCultureIgnoreCase)) return false;
97+
}
98+
6199
return true;
62100
}
63101

@@ -77,12 +115,28 @@ public static bool TryInstallCertificate(X509Certificate2 certificate)
77115
}
78116
}
79117

80-
public static bool TryStore(X509Certificate certificate, string filePath)
118+
public static bool TryExport(X509Certificate2 certificate, string filePath)
81119
{
82120
try
83121
{
84-
var pfxBytes = certificate.Export(X509ContentType.Pfx);
85-
File.WriteAllBytes(filePath, pfxBytes);
122+
var extension = Path.GetExtension(filePath).ToLower();
123+
switch (extension)
124+
{
125+
case ".pfx":
126+
File.WriteAllBytes(filePath, certificate.Export(X509ContentType.Pfx));
127+
break;
128+
case ".cer":
129+
File.WriteAllBytes(filePath, certificate.Export(X509ContentType.Cert));
130+
break;
131+
case ".pem":
132+
File.WriteAllText(filePath, certificate.ExportCertificatePem());
133+
break;
134+
case ".key":
135+
File.WriteAllText(filePath, certificate.GetRSAPrivateKey()?.ExportPkcs8PrivateKeyPem());
136+
break;
137+
default:
138+
throw new ArgumentException($"Unsupported extension '{extension}'.", nameof(filePath));
139+
}
86140
return true;
87141
}
88142
catch
@@ -91,27 +145,7 @@ public static bool TryStore(X509Certificate certificate, string filePath)
91145
}
92146
}
93147

94-
public static bool TryExportPemAndKey(X509Certificate2 certificate, string filePath)
95-
{
96-
try
97-
{
98-
var pem = certificate.ExportCertificatePem();
99-
if (pem == null) return false;
100-
File.WriteAllText(filePath + ".pem", pem);
101-
102-
var key = certificate.GetRSAPrivateKey()?.ExportPkcs8PrivateKeyPem();
103-
if (key == null) return false;
104-
File.WriteAllText(filePath + ".key", key);
105-
106-
return true;
107-
}
108-
catch
109-
{
110-
return false;
111-
}
112-
}
113-
114-
public static X509Certificate2? TryLoad(string filePath)
148+
public static X509Certificate2? TryImport(string filePath)
115149
{
116150
try
117151
{
@@ -124,17 +158,23 @@ public static bool TryExportPemAndKey(X509Certificate2 certificate, string fileP
124158
}
125159
}
126160

127-
public static List<string> GetNames()
161+
public static List<IPAddress> GetIPAddresses()
162+
{
163+
return NetworkInterface
164+
.GetAllNetworkInterfaces()
165+
.SelectMany(p => p.GetIPProperties().UnicastAddresses.Select(q => q.Address))
166+
.ToList();
167+
}
168+
169+
public static List<string> GetDnsNames()
128170
{
129171
var results = new List<string>();
130172
var domain = TryGet(() => IPGlobalProperties.GetIPGlobalProperties().DomainName);
131173
var computerName = TryGet(() => Dns.GetHostName());
132-
var ips = NetworkInterface.GetAllNetworkInterfaces().SelectMany(p => p.GetIPProperties().UnicastAddresses.Select(q => q.Address.ToString()));
133174

134175
results.Add("localhost");
135176
if (!string.IsNullOrEmpty(computerName)) results.Add(computerName);
136177
if (!string.IsNullOrEmpty(computerName) && !string.IsNullOrEmpty(domain)) results.Add(computerName + "." + domain);
137-
results.AddRange(ips);
138178
return results;
139179
}
140180
}

wormhole/Program.cs

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,23 @@
11
using Microsoft.AspNetCore.HttpOverrides;
22
using Warpr.Gateway.Extensions;
3-
using Warpr.Gateway.Session;
43

5-
//Ensure certificate
6-
var certificate = CertificateHelper.TryLoad("wormhole.pfx");
4+
//Ensure certificates
5+
var rootCertificate = CertificateHelper.TryImport("wormhole-ca.pfx");
6+
if (rootCertificate == null)
7+
{
8+
rootCertificate = CertificateHelper.CreateRoot("wormhole-ca");
9+
CertificateHelper.TryExport(rootCertificate, "wormhole-ca.pfx");
10+
CertificateHelper.TryExport(rootCertificate, "wormhole-ca.cer");
11+
}
12+
13+
var certificate = CertificateHelper.TryImport("wormhole.pfx");
714
if (certificate == null || !CertificateHelper.Validate(certificate))
815
{
9-
certificate = CertificateHelper.CreateSelfSigned("wormhole");
10-
CertificateHelper.TryStore(certificate, "wormhole.pfx");
11-
CertificateHelper.TryExportPemAndKey(certificate, "wormhole");
16+
certificate = CertificateHelper.Create("wormhole", rootCertificate);
17+
CertificateHelper.TryExport(certificate, "wormhole.cer");
18+
CertificateHelper.TryExport(certificate, "wormhole.pfx");
19+
CertificateHelper.TryExport(certificate, "wormhole.pem");
20+
CertificateHelper.TryExport(certificate, "wormhole.key");
1221
}
1322

1423
if (!certificate.Verify())

wormhole/Properties/launchSettings.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
"commandName": "Project",
1414
"dotnetRunMessages": true,
1515
"launchBrowser": true,
16+
"externalUrlConfiguration": true,
1617
"applicationUrl": "http://localhost:5164",
1718
"environmentVariables": {
1819
"ASPNETCORE_ENVIRONMENT": "Development",
@@ -23,6 +24,7 @@
2324
"commandName": "Project",
2425
"dotnetRunMessages": true,
2526
"launchBrowser": true,
27+
"externalUrlConfiguration": true,
2628
"applicationUrl": "https://localhost:7074;http://localhost:5164",
2729
"environmentVariables": {
2830
"ASPNETCORE_ENVIRONMENT": "Development",

wormhole/wormhole.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
</PropertyGroup>
1717

1818
<ItemGroup>
19-
<PackageReference Include="Microsoft.AspNetCore.SpaProxy" Version="8.0.10" />
19+
<PackageReference Include="Microsoft.AspNetCore.SpaProxy" Version="8.0.14" />
2020
</ItemGroup>
2121

2222
<ItemGroup>

0 commit comments

Comments
 (0)