11// Licensed to the .NET Foundation under one or more agreements.
22// The .NET Foundation licenses this file to you under the MIT license.
33
4- using System . Net . Security ;
54using System . Runtime . CompilerServices ;
65using System . Runtime . InteropServices ;
76using System . Security . Cryptography ;
@@ -38,6 +37,29 @@ public static partial class Certificates
3837 private static readonly X509BasicConstraintsExtension s_eeConstraints =
3938 new X509BasicConstraintsExtension ( false , false , 0 , false ) ;
4039
40+ private static X509Certificate2 s_dynamicServerCertificate ;
41+ private static X509Certificate2Collection s_dynamicCaCertificates ;
42+ private static object certLock = new object ( ) ;
43+
44+
45+ // These Get* methods make a copy of the certificates so that consumers own the lifetime of the
46+ // certificates handed back. Consumers are expected to dispose of their certs when done with them.
47+
48+ public static X509Certificate2 GetDynamicServerCerttificate ( X509Certificate2Collection ? chainCertificates )
49+ {
50+ lock ( certLock )
51+ {
52+ if ( s_dynamicServerCertificate == null )
53+ {
54+ CleanupCertificates ( ) ;
55+ ( s_dynamicServerCertificate , s_dynamicCaCertificates ) = GenerateCertificates ( "localhost" , nameof ( Configuration ) + nameof ( Certificates ) ) ;
56+ }
57+
58+ chainCertificates ? . AddRange ( s_dynamicCaCertificates ) ;
59+ return new X509Certificate2 ( s_dynamicServerCertificate ) ;
60+ }
61+ }
62+
4163 public static void CleanupCertificates ( [ CallerMemberName ] string ? testName = null , StoreName storeName = StoreName . CertificateAuthority )
4264 {
4365 string caName = $ "O={ testName } ";
@@ -56,9 +78,7 @@ public static void CleanupCertificates([CallerMemberName] string? testName = nul
5678 }
5779 }
5880 }
59- catch
60- {
61- }
81+ catch { } ;
6282
6383 try
6484 {
@@ -75,9 +95,7 @@ public static void CleanupCertificates([CallerMemberName] string? testName = nul
7595 }
7696 }
7797 }
78- catch
79- {
80- }
98+ catch { } ;
8199 }
82100
83101 internal static X509ExtensionCollection BuildTlsServerCertExtensions ( string serverName )
@@ -101,68 +119,7 @@ private static X509ExtensionCollection BuildTlsCertExtensions(string targetName,
101119 return extensions ;
102120 }
103121
104- internal class PkiHolder : IDisposable
105- {
106- internal CertificateAuthority Root { get ; }
107- internal CertificateAuthority [ ] Intermediates { get ; }
108- public X509Certificate2 EndEntity { get ; }
109- public X509Certificate2Collection IssuerChain { get ; }
110- internal RevocationResponder Responder { get ; }
111-
112- private readonly string ? _testName ;
113-
114- public PkiHolder ( string ? testName , CertificateAuthority root , CertificateAuthority [ ] intermediates , X509Certificate2 endEntity , RevocationResponder responder )
115- {
116- _testName = testName ;
117- Root = root ;
118- Intermediates = intermediates ;
119- EndEntity = endEntity ;
120- Responder = responder ;
121-
122- // Walk the intermediates backwards so we build the chain collection as
123- // Issuer3
124- // Issuer2
125- // Issuer1
126- // Root
127- IssuerChain = new X509Certificate2Collection ( ) ;
128- for ( int i = intermediates . Length - 1 ; i >= 0 ; i -- )
129- {
130- CertificateAuthority authority = intermediates [ i ] ;
131-
132- IssuerChain . Add ( authority . CloneIssuerCert ( ) ) ;
133- }
134-
135- IssuerChain . Add ( root . CloneIssuerCert ( ) ) ;
136- }
137-
138- public SslStreamCertificateContext CreateSslStreamCertificateContext ( )
139- {
140- return SslStreamCertificateContext . Create ( EndEntity , IssuerChain ) ;
141- }
142-
143- public void Dispose ( )
144- {
145- foreach ( CertificateAuthority authority in Intermediates )
146- {
147- authority . Dispose ( ) ;
148- }
149- Root . Dispose ( ) ;
150- EndEntity . Dispose ( ) ;
151- Responder . Dispose ( ) ;
152-
153- foreach ( X509Certificate2 authority in IssuerChain )
154- {
155- authority . Dispose ( ) ;
156- }
157-
158- if ( PlatformDetection . IsWindows && _testName != null )
159- {
160- CleanupCertificates ( _testName ) ;
161- }
162- }
163- }
164-
165- internal static PkiHolder GenerateCertificates ( string targetName , [ CallerMemberName ] string ? testName = null , bool longChain = false , bool serverCertificate = true , bool ephemeralKey = false )
122+ public static ( X509Certificate2 certificate , X509Certificate2Collection ) GenerateCertificates ( string targetName , [ CallerMemberName ] string ? testName = null , bool longChain = false , bool serverCertificate = true , bool ephemeralKey = false )
166123 {
167124 const int keySize = 2048 ;
168125 if ( PlatformDetection . IsWindows && testName != null )
@@ -174,7 +131,7 @@ internal static PkiHolder GenerateCertificates(string targetName, [CallerMemberN
174131 X509ExtensionCollection extensions = BuildTlsCertExtensions ( targetName , serverCertificate ) ;
175132
176133 CertificateAuthority . BuildPrivatePki (
177- PkiOptions . AllRevocation ,
134+ PkiOptions . IssuerRevocationViaCrl ,
178135 out RevocationResponder responder ,
179136 out CertificateAuthority root ,
180137 out CertificateAuthority [ ] intermediates ,
@@ -185,15 +142,34 @@ internal static PkiHolder GenerateCertificates(string targetName, [CallerMemberN
185142 keyFactory : CertificateAuthority . KeyFactory . RSASize ( keySize ) ,
186143 extensions : extensions ) ;
187144
145+ // Walk the intermediates backwards so we build the chain collection as
146+ // Issuer3
147+ // Issuer2
148+ // Issuer1
149+ // Root
150+ for ( int i = intermediates . Length - 1 ; i >= 0 ; i -- )
151+ {
152+ CertificateAuthority authority = intermediates [ i ] ;
153+
154+ chain . Add ( authority . CloneIssuerCert ( ) ) ;
155+ authority . Dispose ( ) ;
156+ }
157+
158+ chain . Add ( root . CloneIssuerCert ( ) ) ;
159+
160+ responder . Dispose ( ) ;
161+ root . Dispose ( ) ;
162+
188163 if ( ! ephemeralKey && PlatformDetection . IsWindows )
189164 {
190165 X509Certificate2 ephemeral = endEntity ;
191166 endEntity = X509CertificateLoader . LoadPkcs12 ( endEntity . Export ( X509ContentType . Pfx ) , ( string ? ) null , X509KeyStorageFlags . Exportable ) ;
192167 ephemeral . Dispose ( ) ;
193168 }
194169
195- return new PkiHolder ( testName , root , intermediates , endEntity , responder ) ;
170+ return ( endEntity , chain ) ;
196171 }
172+
197173 }
198174 }
199175}
0 commit comments