diff --git a/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OpenSsl.cs b/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OpenSsl.cs index bd99e73adb70f9..d89ce63d496fb3 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OpenSsl.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OpenSsl.cs @@ -35,35 +35,54 @@ private sealed class SafeSslContextCache : SafeHandleCache { + private const int ThumbprintSize = 64; // SHA512 size + public readonly bool IsClient; - public readonly byte[]? CertificateThumbprint; + public readonly ReadOnlyMemory CertificateThumbprints; public readonly SslProtocols SslProtocols; - public SslContextCacheKey(bool isClient, SslProtocols sslProtocols, byte[]? certificateThumbprint) + public SslContextCacheKey(bool isClient, SslProtocols sslProtocols, SslStreamCertificateContext? certContext) { IsClient = isClient; SslProtocols = sslProtocols; - CertificateThumbprint = certificateThumbprint; + + CertificateThumbprints = ReadOnlyMemory.Empty; + + if (certContext != null) + { + int certCount = 1 + certContext.IntermediateCertificates.Count; + byte[] certificateThumbprints = new byte[certCount * ThumbprintSize]; + + bool success = certContext.TargetCertificate.TryGetCertHash(HashAlgorithmName.SHA512, certificateThumbprints.AsSpan(0, ThumbprintSize), out _); + Debug.Assert(success); + + certCount = 1; + foreach (X509Certificate2 intermediate in certContext.IntermediateCertificates) + { + success = intermediate.TryGetCertHash(HashAlgorithmName.SHA512, certificateThumbprints.AsSpan(certCount * ThumbprintSize, ThumbprintSize), out _); + Debug.Assert(success); + certCount++; + } + + CertificateThumbprints = certificateThumbprints; + } } public override bool Equals(object? obj) => obj is SslContextCacheKey key && Equals(key); public bool Equals(SslContextCacheKey other) => + IsClient == other.IsClient && - SslProtocols == other.SslProtocols && - (CertificateThumbprint == null && other.CertificateThumbprint == null || - CertificateThumbprint != null && other.CertificateThumbprint != null && CertificateThumbprint.AsSpan().SequenceEqual(other.CertificateThumbprint)); + CertificateThumbprints.Span.SequenceEqual(other.CertificateThumbprints.Span) && + SslProtocols == other.SslProtocols; public override int GetHashCode() { HashCode hash = default; hash.Add(IsClient); + hash.AddBytes(CertificateThumbprints.Span); hash.Add(SslProtocols); - if (CertificateThumbprint != null) - { - hash.AddBytes(CertificateThumbprint); - } return hash.ToHashCode(); } @@ -172,7 +191,7 @@ internal static SafeSslContextHandle GetOrCreateSslContextHandle(SslAuthenticati var key = new SslContextCacheKey( sslAuthenticationOptions.IsClient, sslAuthenticationOptions.IsClient ? protocols : serverProtocolCacheKey, - sslAuthenticationOptions.CertificateContext?.TargetCertificate.GetCertHash(HashAlgorithmName.SHA512)); + sslAuthenticationOptions.CertificateContext); return s_sslContexts.GetOrCreate(key, static (args) => { var (sslAuthOptions, protocols, allowCached) = args; diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/MsQuicConfiguration.Cache.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/MsQuicConfiguration.Cache.cs index 7ae1a571a0f778..6beddff6638336 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/MsQuicConfiguration.Cache.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/MsQuicConfiguration.Cache.cs @@ -46,7 +46,9 @@ private sealed class MsQuicConfigurationCache : SafeHandleCache { - public readonly List CertificateThumbprints; + private const int ThumbprintSize = 64; // SHA512 size + + public readonly ReadOnlyMemory CertificateThumbprints; public readonly QUIC_CREDENTIAL_FLAGS Flags; public readonly QUIC_SETTINGS Settings; public readonly List ApplicationProtocols; @@ -54,16 +56,30 @@ private sealed class MsQuicConfigurationCache : SafeHandleCache? intermediates, List alpnProtocols, QUIC_ALLOWED_CIPHER_SUITE_FLAGS allowedCipherSuites) { - CertificateThumbprints = certificate == null ? new List() : new List { certificate.GetCertHash(HashAlgorithmName.SHA512) }; + int certCount = certificate == null ? 0 : 1; + certCount += intermediates?.Count ?? 0; + byte[] certificateThumbprints = new byte[certCount * ThumbprintSize]; + + certCount = 0; + if (certificate != null) + { + bool success = certificate.TryGetCertHash(HashAlgorithmName.SHA512, certificateThumbprints.AsSpan(0, ThumbprintSize), out _); + Debug.Assert(success); + certCount++; + } if (intermediates != null) { foreach (X509Certificate2 intermediate in intermediates) { - CertificateThumbprints.Add(intermediate.GetCertHash(HashAlgorithmName.SHA512)); + bool success = intermediate.TryGetCertHash(HashAlgorithmName.SHA512, certificateThumbprints.AsSpan(certCount * ThumbprintSize, ThumbprintSize), out _); + Debug.Assert(success); + certCount++; } } + CertificateThumbprints = certificateThumbprints; + Flags = flags; Settings = settings; // 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 public bool Equals(CacheKey other) { - if (CertificateThumbprints.Count != other.CertificateThumbprints.Count) + if (!CertificateThumbprints.Span.SequenceEqual(other.CertificateThumbprints.Span)) { return false; } - for (int i = 0; i < CertificateThumbprints.Count; i++) - { - if (!CertificateThumbprints[i].AsSpan().SequenceEqual(other.CertificateThumbprints[i])) - { - return false; - } - } - if (ApplicationProtocols.Count != other.ApplicationProtocols.Count) { return false; @@ -111,11 +119,7 @@ public override int GetHashCode() { HashCode hash = default; - foreach (var thumbprint in CertificateThumbprints) - { - hash.AddBytes(thumbprint); - } - + hash.AddBytes(CertificateThumbprints.Span); hash.Add(Flags); hash.Add(Settings);