From bd62f07b708ccf2fda035b914fa5918679001fed Mon Sep 17 00:00:00 2001 From: Radek Zikmund Date: Wed, 2 Mar 2022 13:09:55 +0100 Subject: [PATCH 1/2] Fix performance regression in SSL handshake --- .../src/System/Net/CertificateValidationPal.Windows.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/libraries/System.Net.Security/src/System/Net/CertificateValidationPal.Windows.cs b/src/libraries/System.Net.Security/src/System/Net/CertificateValidationPal.Windows.cs index 99c71a95c46d2f..bfa1fc1064b8bf 100644 --- a/src/libraries/System.Net.Security/src/System/Net/CertificateValidationPal.Windows.cs +++ b/src/libraries/System.Net.Security/src/System/Net/CertificateValidationPal.Windows.cs @@ -48,12 +48,12 @@ internal static SslPolicyErrors VerifyCertificateProperties( SafeFreeCertContext? remoteContext = null; try { - // SECPKG_ATTR_REMOTE_CERT_CHAIN can be used even before the TLS handshake completes, which is necessary - // in order to supply the certificate to the client cert selection callback. However, it is not available on - // windows 7, so use the SECPKG_ATTR_REMOTE_CERT_CONTEXT as a fallback option. - if (!SSPIWrapper.QueryContextAttributes_SECPKG_ATTR_REMOTE_CERT_CHAIN(GlobalSSPI.SSPISecureChannel, securityContext, out remoteContext)) + if (!SSPIWrapper.QueryContextAttributes_SECPKG_ATTR_REMOTE_CERT_CONTEXT(GlobalSSPI.SSPISecureChannel, securityContext, out remoteContext)) { - SSPIWrapper.QueryContextAttributes_SECPKG_ATTR_REMOTE_CERT_CONTEXT(GlobalSSPI.SSPISecureChannel, securityContext, out remoteContext); + // The query can fail if TLS handshake has not completed yet. In that case we fallback to querying + // SECPKG_ATTR_REMOTE_CERT_CHAIN that is more expensive but works even during handshake. + // Note: On Windows versions which don't support querying CERT_CHAIN, we fail and always return null + SSPIWrapper.QueryContextAttributes_SECPKG_ATTR_REMOTE_CERT_CHAIN(GlobalSSPI.SSPISecureChannel, securityContext, out remoteContext); } if (remoteContext != null && !remoteContext.IsInvalid) From 55db46be9baa5b4b512431f3fdc911a7c6430f37 Mon Sep 17 00:00:00 2001 From: Radek Zikmund Date: Thu, 3 Mar 2022 15:01:20 +0100 Subject: [PATCH 2/2] Further optimizations --- .../Net/CertificateValidationPal.Windows.cs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/libraries/System.Net.Security/src/System/Net/CertificateValidationPal.Windows.cs b/src/libraries/System.Net.Security/src/System/Net/CertificateValidationPal.Windows.cs index bfa1fc1064b8bf..1d9f69c33b508d 100644 --- a/src/libraries/System.Net.Security/src/System/Net/CertificateValidationPal.Windows.cs +++ b/src/libraries/System.Net.Security/src/System/Net/CertificateValidationPal.Windows.cs @@ -48,11 +48,18 @@ internal static SslPolicyErrors VerifyCertificateProperties( SafeFreeCertContext? remoteContext = null; try { - if (!SSPIWrapper.QueryContextAttributes_SECPKG_ATTR_REMOTE_CERT_CONTEXT(GlobalSSPI.SSPISecureChannel, securityContext, out remoteContext)) + // SECPKG_ATTR_REMOTE_CERT_CONTEXT will not succeed before TLS handshake completes. Inside the handshake, + // we need to use (more expensive) SECPKG_ATTR_REMOTE_CERT_CHAIN. That one may be unsupported on older + // versions of windows. In that case, we have no option than to return null. + // + // We can use retrieveCollection to distinguish between in-handshake and after-handshake calls, because + // the collection is retrieved for cert validation purposes after the handshake completes. + if (retrieveCollection) // handshake completed + { + SSPIWrapper.QueryContextAttributes_SECPKG_ATTR_REMOTE_CERT_CONTEXT(GlobalSSPI.SSPISecureChannel, securityContext, out remoteContext); + } + else // in handshake { - // The query can fail if TLS handshake has not completed yet. In that case we fallback to querying - // SECPKG_ATTR_REMOTE_CERT_CHAIN that is more expensive but works even during handshake. - // Note: On Windows versions which don't support querying CERT_CHAIN, we fail and always return null SSPIWrapper.QueryContextAttributes_SECPKG_ATTR_REMOTE_CERT_CHAIN(GlobalSSPI.SSPISecureChannel, securityContext, out remoteContext); }