Skip to content

Commit d343214

Browse files
authored
Use certificate thumbprints from entire chain in SSL_CTX cache (#112858)
* Use certificate thumbprints from entire chain in SSL_CTX cache. * Feedback
1 parent db681fb commit d343214

File tree

2 files changed

+51
-28
lines changed

2 files changed

+51
-28
lines changed

src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OpenSsl.cs

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -35,35 +35,54 @@ private sealed class SafeSslContextCache : SafeHandleCache<SslContextCacheKey, S
3535

3636
internal readonly struct SslContextCacheKey : IEquatable<SslContextCacheKey>
3737
{
38+
private const int ThumbprintSize = 64; // SHA512 size
39+
3840
public readonly bool IsClient;
39-
public readonly byte[]? CertificateThumbprint;
41+
public readonly ReadOnlyMemory<byte> CertificateThumbprints;
4042
public readonly SslProtocols SslProtocols;
4143

42-
public SslContextCacheKey(bool isClient, SslProtocols sslProtocols, byte[]? certificateThumbprint)
44+
public SslContextCacheKey(bool isClient, SslProtocols sslProtocols, SslStreamCertificateContext? certContext)
4345
{
4446
IsClient = isClient;
4547
SslProtocols = sslProtocols;
46-
CertificateThumbprint = certificateThumbprint;
48+
49+
CertificateThumbprints = ReadOnlyMemory<byte>.Empty;
50+
51+
if (certContext != null)
52+
{
53+
int certCount = 1 + certContext.IntermediateCertificates.Count;
54+
byte[] certificateThumbprints = new byte[certCount * ThumbprintSize];
55+
56+
bool success = certContext.TargetCertificate.TryGetCertHash(HashAlgorithmName.SHA512, certificateThumbprints.AsSpan(0, ThumbprintSize), out _);
57+
Debug.Assert(success);
58+
59+
certCount = 1;
60+
foreach (X509Certificate2 intermediate in certContext.IntermediateCertificates)
61+
{
62+
success = intermediate.TryGetCertHash(HashAlgorithmName.SHA512, certificateThumbprints.AsSpan(certCount * ThumbprintSize, ThumbprintSize), out _);
63+
Debug.Assert(success);
64+
certCount++;
65+
}
66+
67+
CertificateThumbprints = certificateThumbprints;
68+
}
4769
}
4870

4971
public override bool Equals(object? obj) => obj is SslContextCacheKey key && Equals(key);
5072

5173
public bool Equals(SslContextCacheKey other) =>
74+
5275
IsClient == other.IsClient &&
53-
SslProtocols == other.SslProtocols &&
54-
(CertificateThumbprint == null && other.CertificateThumbprint == null ||
55-
CertificateThumbprint != null && other.CertificateThumbprint != null && CertificateThumbprint.AsSpan().SequenceEqual(other.CertificateThumbprint));
76+
CertificateThumbprints.Span.SequenceEqual(other.CertificateThumbprints.Span) &&
77+
SslProtocols == other.SslProtocols;
5678

5779
public override int GetHashCode()
5880
{
5981
HashCode hash = default;
6082

6183
hash.Add(IsClient);
84+
hash.AddBytes(CertificateThumbprints.Span);
6285
hash.Add(SslProtocols);
63-
if (CertificateThumbprint != null)
64-
{
65-
hash.AddBytes(CertificateThumbprint);
66-
}
6786

6887
return hash.ToHashCode();
6988
}
@@ -172,7 +191,7 @@ internal static SafeSslContextHandle GetOrCreateSslContextHandle(SslAuthenticati
172191
var key = new SslContextCacheKey(
173192
sslAuthenticationOptions.IsClient,
174193
sslAuthenticationOptions.IsClient ? protocols : serverProtocolCacheKey,
175-
sslAuthenticationOptions.CertificateContext?.TargetCertificate.GetCertHash(HashAlgorithmName.SHA512));
194+
sslAuthenticationOptions.CertificateContext);
176195
return s_sslContexts.GetOrCreate(key, static (args) =>
177196
{
178197
var (sslAuthOptions, protocols, allowCached) = args;

src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/MsQuicConfiguration.Cache.cs

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -46,24 +46,40 @@ private sealed class MsQuicConfigurationCache : SafeHandleCache<CacheKey, MsQuic
4646

4747
private readonly struct CacheKey : IEquatable<CacheKey>
4848
{
49-
public readonly List<byte[]> CertificateThumbprints;
49+
private const int ThumbprintSize = 64; // SHA512 size
50+
51+
public readonly ReadOnlyMemory<byte> CertificateThumbprints;
5052
public readonly QUIC_CREDENTIAL_FLAGS Flags;
5153
public readonly QUIC_SETTINGS Settings;
5254
public readonly List<SslApplicationProtocol> ApplicationProtocols;
5355
public readonly QUIC_ALLOWED_CIPHER_SUITE_FLAGS AllowedCipherSuites;
5456

5557
public CacheKey(QUIC_SETTINGS settings, QUIC_CREDENTIAL_FLAGS flags, X509Certificate? certificate, ReadOnlyCollection<X509Certificate2>? intermediates, List<SslApplicationProtocol> alpnProtocols, QUIC_ALLOWED_CIPHER_SUITE_FLAGS allowedCipherSuites)
5658
{
57-
CertificateThumbprints = certificate == null ? new List<byte[]>() : new List<byte[]> { certificate.GetCertHash(HashAlgorithmName.SHA512) };
59+
int certCount = certificate == null ? 0 : 1;
60+
certCount += intermediates?.Count ?? 0;
61+
byte[] certificateThumbprints = new byte[certCount * ThumbprintSize];
62+
63+
certCount = 0;
64+
if (certificate != null)
65+
{
66+
bool success = certificate.TryGetCertHash(HashAlgorithmName.SHA512, certificateThumbprints.AsSpan(0, ThumbprintSize), out _);
67+
Debug.Assert(success);
68+
certCount++;
69+
}
5870

5971
if (intermediates != null)
6072
{
6173
foreach (X509Certificate2 intermediate in intermediates)
6274
{
63-
CertificateThumbprints.Add(intermediate.GetCertHash(HashAlgorithmName.SHA512));
75+
bool success = intermediate.TryGetCertHash(HashAlgorithmName.SHA512, certificateThumbprints.AsSpan(certCount * ThumbprintSize, ThumbprintSize), out _);
76+
Debug.Assert(success);
77+
certCount++;
6478
}
6579
}
6680

81+
CertificateThumbprints = certificateThumbprints;
82+
6783
Flags = flags;
6884
Settings = settings;
6985
// make defensive copy to prevent modification (the list comes from user code)
@@ -75,19 +91,11 @@ public CacheKey(QUIC_SETTINGS settings, QUIC_CREDENTIAL_FLAGS flags, X509Certifi
7591

7692
public bool Equals(CacheKey other)
7793
{
78-
if (CertificateThumbprints.Count != other.CertificateThumbprints.Count)
94+
if (!CertificateThumbprints.Span.SequenceEqual(other.CertificateThumbprints.Span))
7995
{
8096
return false;
8197
}
8298

83-
for (int i = 0; i < CertificateThumbprints.Count; i++)
84-
{
85-
if (!CertificateThumbprints[i].AsSpan().SequenceEqual(other.CertificateThumbprints[i]))
86-
{
87-
return false;
88-
}
89-
}
90-
9199
if (ApplicationProtocols.Count != other.ApplicationProtocols.Count)
92100
{
93101
return false;
@@ -111,11 +119,7 @@ public override int GetHashCode()
111119
{
112120
HashCode hash = default;
113121

114-
foreach (var thumbprint in CertificateThumbprints)
115-
{
116-
hash.AddBytes(thumbprint);
117-
}
118-
122+
hash.AddBytes(CertificateThumbprints.Span);
119123
hash.Add(Flags);
120124
hash.Add(Settings);
121125

0 commit comments

Comments
 (0)