99using System . Security . Cryptography ;
1010using System . Security . Cryptography . X509Certificates ;
1111using System . Text ;
12+ using System . Text . Json ;
13+ using System . Text . Json . Nodes ;
14+ using System . Text . Json . Serialization ;
1215
1316#nullable enable
1417
1518namespace Microsoft . AspNetCore . Certificates . Generation ;
1619
1720internal abstract class CertificateManager
1821{
19- internal const int CurrentAspNetCoreCertificateVersion = 2 ;
22+ internal const int CurrentAspNetCoreCertificateVersion = 3 ;
23+ internal const int CurrentMinimumAspNetCoreCertificateVersion = 3 ;
2024
2125 // OID used for HTTPS certs
2226 internal const string AspNetHttpsOid = "1.3.6.1.4.1.311.84.1.1" ;
2327 internal const string AspNetHttpsOidFriendlyName = "ASP.NET Core HTTPS development certificate" ;
2428
2529 private const string ServerAuthenticationEnhancedKeyUsageOid = "1.3.6.1.5.5.7.3.1" ;
2630 private const string ServerAuthenticationEnhancedKeyUsageOidFriendlyName = "Server Authentication" ;
31+
32+ // dns names of the host from a container
33+ private const string LocalHostDockerHttpsDnsName = "host.docker.internal" ;
34+ private const string ContainersDockerHttpsDnsName = "host.containers.internal" ;
2735
36+ // main cert subject
2837 private const string LocalhostHttpsDnsName = "localhost" ;
2938 internal const string LocalhostHttpsDistinguishedName = "CN=" + LocalhostHttpsDnsName ;
3039
@@ -49,6 +58,13 @@ public int AspNetHttpsCertificateVersion
4958 internal set ;
5059 }
5160
61+ public int MinimumAspNetHttpsCertificateVersion
62+ {
63+ get ;
64+ // For testing purposes only
65+ internal set ;
66+ }
67+
5268 public string Subject { get ; }
5369
5470 public CertificateManager ( ) : this ( LocalhostHttpsDistinguishedName , CurrentAspNetCoreCertificateVersion )
@@ -57,9 +73,16 @@ public CertificateManager() : this(LocalhostHttpsDistinguishedName, CurrentAspNe
5773
5874 // For testing purposes only
5975 internal CertificateManager ( string subject , int version )
76+ : this ( subject , version , version )
77+ {
78+ }
79+
80+ // For testing purposes only
81+ internal CertificateManager ( string subject , int generatedVersion , int minimumVersion )
6082 {
6183 Subject = subject ;
62- AspNetHttpsCertificateVersion = version ;
84+ AspNetHttpsCertificateVersion = generatedVersion ;
85+ MinimumAspNetHttpsCertificateVersion = minimumVersion ;
6386 }
6487
6588 /// <remarks>
@@ -147,30 +170,30 @@ bool HasOid(X509Certificate2 certificate, string oid) =>
147170 certificate . Extensions . OfType < X509Extension > ( )
148171 . Any ( e => string . Equals ( oid , e . Oid ? . Value , StringComparison . Ordinal ) ) ;
149172
150- static byte GetCertificateVersion ( X509Certificate2 c )
151- {
152- var byteArray = c . Extensions . OfType < X509Extension > ( )
153- . Where ( e => string . Equals ( AspNetHttpsOid , e . Oid ? . Value , StringComparison . Ordinal ) )
154- . Single ( )
155- . RawData ;
156-
157- if ( ( byteArray . Length == AspNetHttpsOidFriendlyName . Length && byteArray [ 0 ] == ( byte ) 'A' ) || byteArray . Length == 0 )
158- {
159- // No Version set, default to 0
160- return 0b0 ;
161- }
162- else
163- {
164- // Version is in the only byte of the byte array.
165- return byteArray [ 0 ] ;
166- }
167- }
168-
169173 bool IsValidCertificate ( X509Certificate2 certificate , DateTimeOffset currentDate , bool requireExportable ) =>
170174 certificate . NotBefore <= currentDate &&
171175 currentDate <= certificate . NotAfter &&
172176 ( ! requireExportable || IsExportable ( certificate ) ) &&
173- GetCertificateVersion ( certificate ) >= AspNetHttpsCertificateVersion ;
177+ GetCertificateVersion ( certificate ) >= MinimumAspNetHttpsCertificateVersion ;
178+ }
179+
180+ private static byte GetCertificateVersion ( X509Certificate2 c )
181+ {
182+ var byteArray = c . Extensions . OfType < X509Extension > ( )
183+ . Where ( e => string . Equals ( AspNetHttpsOid , e . Oid ? . Value , StringComparison . Ordinal ) )
184+ . Single ( )
185+ . RawData ;
186+
187+ if ( ( byteArray . Length == AspNetHttpsOidFriendlyName . Length && byteArray [ 0 ] == ( byte ) 'A' ) || byteArray . Length == 0 )
188+ {
189+ // No Version set, default to 0
190+ return 0b0 ;
191+ }
192+ else
193+ {
194+ // Version is in the only byte of the byte array.
195+ return byteArray [ 0 ] ;
196+ }
174197 }
175198
176199 protected virtual void PopulateCertificatesFromStore ( X509Store store , List < X509Certificate2 > certificates , bool requireExportable )
@@ -487,7 +510,7 @@ public void CleanupHttpsCertificates()
487510 /// <remarks>Implementations may choose to throw, rather than returning <see cref="TrustLevel.None"/>.</remarks>
488511 protected abstract TrustLevel TrustCertificateCore ( X509Certificate2 certificate ) ;
489512
490- protected abstract bool IsExportable ( X509Certificate2 c ) ;
513+ internal abstract bool IsExportable ( X509Certificate2 c ) ;
491514
492515 protected abstract void RemoveCertificateFromTrustedRoots ( X509Certificate2 certificate ) ;
493516
@@ -649,6 +672,8 @@ internal X509Certificate2 CreateAspNetCoreHttpsDevelopmentCertificate(DateTimeOf
649672 var extensions = new List < X509Extension > ( ) ;
650673 var sanBuilder = new SubjectAlternativeNameBuilder ( ) ;
651674 sanBuilder . AddDnsName ( LocalhostHttpsDnsName ) ;
675+ sanBuilder . AddDnsName ( LocalHostDockerHttpsDnsName ) ;
676+ sanBuilder . AddDnsName ( ContainersDockerHttpsDnsName ) ;
652677
653678 var keyUsage = new X509KeyUsageExtension ( X509KeyUsageFlags . KeyEncipherment | X509KeyUsageFlags . DigitalSignature , critical : true ) ;
654679 var enhancedKeyUsage = new X509EnhancedKeyUsageExtension (
0 commit comments