1
- using System . Net ;
1
+ using System ;
2
+ using System . Linq ;
3
+ using System . Net ;
2
4
using System . Net . NetworkInformation ;
3
5
using System . Security . Cryptography ;
4
6
using System . Security . Cryptography . X509Certificates ;
@@ -19,10 +21,23 @@ public static class CertificateHelper
19
21
}
20
22
}
21
23
22
- public static X509Certificate2 CreateSelfSigned ( string subjectName )
24
+ public static X509Certificate2 CreateRoot ( string subjectName )
23
25
{
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 ) ;
26
41
certificateRequest . CertificateExtensions . Add ( new X509BasicConstraintsExtension ( false , false , 0 , false ) ) ;
27
42
certificateRequest . CertificateExtensions . Add ( new X509KeyUsageExtension ( X509KeyUsageFlags . DigitalSignature | X509KeyUsageFlags . KeyEncipherment , true ) ) ;
28
43
certificateRequest . CertificateExtensions . Add (
@@ -33,16 +48,32 @@ public static X509Certificate2 CreateSelfSigned(string subjectName)
33
48
new Oid ( "1.3.6.1.5.5.7.3.2" ) //Client authentication
34
49
} ,
35
50
false ) ) ;
51
+ //certificateRequest.CertificateExtensions.Add(new X509SubjectKeyIdentifierExtension(certificateRequest.PublicKey, false));
36
52
37
53
var alternativeNameBuilder = new SubjectAlternativeNameBuilder ( ) ;
38
- foreach ( var name in GetNames ( ) )
54
+ foreach ( var name in GetDnsNames ( ) )
39
55
{
40
56
alternativeNameBuilder . AddDnsName ( name ) ;
41
57
}
58
+ foreach ( var address in GetIPAddresses ( ) )
59
+ {
60
+ alternativeNameBuilder . AddIpAddress ( address ) ;
61
+ }
42
62
certificateRequest . CertificateExtensions . Add ( alternativeNameBuilder . Build ( ) ) ;
43
63
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
+ }
46
77
}
47
78
48
79
public static bool Validate ( X509Certificate2 certificate2 )
@@ -54,10 +85,17 @@ public static bool Validate(X509Certificate2 certificate2)
54
85
. Where ( p => p . Oid ? . Value == "2.5.29.17" ) //Subject Alternative Name
55
86
. Select ( p => p . Format ( false ) )
56
87
. FirstOrDefault ( ) ?? "" ;
57
- foreach ( var name in GetNames ( ) )
88
+
89
+ foreach ( var name in GetDnsNames ( ) )
58
90
{
59
91
if ( ! alternateNames . Contains ( name , StringComparison . InvariantCultureIgnoreCase ) ) return false ;
60
92
}
93
+
94
+ foreach ( var address in GetIPAddresses ( ) )
95
+ {
96
+ if ( ! alternateNames . Contains ( address . ToString ( ) , StringComparison . InvariantCultureIgnoreCase ) ) return false ;
97
+ }
98
+
61
99
return true ;
62
100
}
63
101
@@ -77,12 +115,28 @@ public static bool TryInstallCertificate(X509Certificate2 certificate)
77
115
}
78
116
}
79
117
80
- public static bool TryStore ( X509Certificate certificate , string filePath )
118
+ public static bool TryExport ( X509Certificate2 certificate , string filePath )
81
119
{
82
120
try
83
121
{
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
+ }
86
140
return true ;
87
141
}
88
142
catch
@@ -91,27 +145,7 @@ public static bool TryStore(X509Certificate certificate, string filePath)
91
145
}
92
146
}
93
147
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 )
115
149
{
116
150
try
117
151
{
@@ -124,17 +158,23 @@ public static bool TryExportPemAndKey(X509Certificate2 certificate, string fileP
124
158
}
125
159
}
126
160
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 ( )
128
170
{
129
171
var results = new List < string > ( ) ;
130
172
var domain = TryGet ( ( ) => IPGlobalProperties . GetIPGlobalProperties ( ) . DomainName ) ;
131
173
var computerName = TryGet ( ( ) => Dns . GetHostName ( ) ) ;
132
- var ips = NetworkInterface . GetAllNetworkInterfaces ( ) . SelectMany ( p => p . GetIPProperties ( ) . UnicastAddresses . Select ( q => q . Address . ToString ( ) ) ) ;
133
174
134
175
results . Add ( "localhost" ) ;
135
176
if ( ! string . IsNullOrEmpty ( computerName ) ) results . Add ( computerName ) ;
136
177
if ( ! string . IsNullOrEmpty ( computerName ) && ! string . IsNullOrEmpty ( domain ) ) results . Add ( computerName + "." + domain ) ;
137
- results . AddRange ( ips ) ;
138
178
return results ;
139
179
}
140
180
}
0 commit comments