diff --git a/src/libraries/Common/src/System/Net/Security/CertificateValidation.Windows.cs b/src/libraries/Common/src/System/Net/Security/CertificateValidation.Windows.cs index 90be80c734cc72..c4a326765537d6 100644 --- a/src/libraries/Common/src/System/Net/Security/CertificateValidation.Windows.cs +++ b/src/libraries/Common/src/System/Net/Security/CertificateValidation.Windows.cs @@ -26,7 +26,11 @@ internal static SslPolicyErrors BuildChainAndVerifyProperties(X509Chain chain, X if (!chainBuildResult // Build failed on handle or on policy. && chain.SafeHandle!.DangerousGetHandle() == IntPtr.Zero) // Build failed to generate a valid handle. { +#if NETFRAMEWORK + throw new CryptographicException(Marshal.GetLastWin32Error()); +#else throw new CryptographicException(Marshal.GetLastPInvokeError()); +#endif } if (checkCertName) diff --git a/src/libraries/System.Net.Http.WinHttpHandler/src/System.Net.Http.WinHttpHandler.csproj b/src/libraries/System.Net.Http.WinHttpHandler/src/System.Net.Http.WinHttpHandler.csproj index a6a5771eefdb69..7e6af9f6b954f9 100644 --- a/src/libraries/System.Net.Http.WinHttpHandler/src/System.Net.Http.WinHttpHandler.csproj +++ b/src/libraries/System.Net.Http.WinHttpHandler/src/System.Net.Http.WinHttpHandler.csproj @@ -72,6 +72,8 @@ System.Net.Http.WinHttpHandler Link="Common\System\Net\Security\CertificateHelper.cs" /> + - diff --git a/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpCertificateHelper.cs b/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpCertificateHelper.cs deleted file mode 100644 index 915b35c09d546d..00000000000000 --- a/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpCertificateHelper.cs +++ /dev/null @@ -1,102 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Diagnostics; -using System.Net.Security; -using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.X509Certificates; - -namespace System.Net.Http -{ - internal static class WinHttpCertificateHelper - { - private static readonly Oid s_serverAuthOid = new Oid("1.3.6.1.5.5.7.3.1", "1.3.6.1.5.5.7.3.1"); - - public static void BuildChain( - X509Certificate2 certificate, - X509Certificate2Collection remoteCertificateStore, - string hostName, - bool checkCertificateRevocationList, - out X509Chain chain, - out SslPolicyErrors sslPolicyErrors) - { - sslPolicyErrors = SslPolicyErrors.None; - - // Build the chain. - chain = new X509Chain(); - chain.ChainPolicy.RevocationMode = - checkCertificateRevocationList ? X509RevocationMode.Online : X509RevocationMode.NoCheck; - chain.ChainPolicy.RevocationFlag = X509RevocationFlag.ExcludeRoot; - // Authenticate the remote party: (e.g. when operating in client mode, authenticate the server). - chain.ChainPolicy.ApplicationPolicy.Add(s_serverAuthOid); - - if (remoteCertificateStore.Count > 0) - { - if (NetEventSource.Log.IsEnabled()) - { - foreach (X509Certificate cert in remoteCertificateStore) - { - NetEventSource.Info(remoteCertificateStore, $"Adding cert to ExtraStore: {cert.Subject}"); - } - } - - chain.ChainPolicy.ExtraStore.AddRange(remoteCertificateStore); - } - - if (!chain.Build(certificate)) - { - sslPolicyErrors |= SslPolicyErrors.RemoteCertificateChainErrors; - } - - // Verify the hostName matches the certificate. - unsafe - { - Interop.Crypt32.CERT_CHAIN_POLICY_PARA cppStruct = default; - cppStruct.cbSize = (uint)sizeof(Interop.Crypt32.CERT_CHAIN_POLICY_PARA); - cppStruct.dwFlags = 0; - - Interop.Crypt32.SSL_EXTRA_CERT_CHAIN_POLICY_PARA eppStruct = default; - eppStruct.cbSize = (uint)sizeof(Interop.Crypt32.SSL_EXTRA_CERT_CHAIN_POLICY_PARA); - eppStruct.dwAuthType = Interop.Crypt32.AuthType.AUTHTYPE_SERVER; - - cppStruct.pvExtraPolicyPara = &eppStruct; - - fixed (char* namePtr = hostName) - { - eppStruct.pwszServerName = (ushort*)namePtr; // The native field is WCHAR*, so we can just cast to ushort in this case - cppStruct.dwFlags = - Interop.Crypt32.CertChainPolicyIgnoreFlags.CERT_CHAIN_POLICY_IGNORE_ALL & - ~Interop.Crypt32.CertChainPolicyIgnoreFlags.CERT_CHAIN_POLICY_IGNORE_INVALID_NAME_FLAG; - - Debug.Assert(chain.SafeHandle != null); - - Interop.Crypt32.CERT_CHAIN_POLICY_STATUS status = default; - status.cbSize = (uint)sizeof(Interop.Crypt32.CERT_CHAIN_POLICY_STATUS); - if (Interop.Crypt32.CertVerifyCertificateChainPolicy( - (IntPtr)Interop.Crypt32.CertChainPolicy.CERT_CHAIN_POLICY_SSL, - chain.SafeHandle, - ref cppStruct, - ref status)) - { - if (status.dwError == Interop.Crypt32.CertChainPolicyErrors.CERT_E_CN_NO_MATCH) - { - if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(certificate, nameof(Interop.Crypt32.CertChainPolicyErrors.CERT_E_CN_NO_MATCH)); - sslPolicyErrors |= SslPolicyErrors.RemoteCertificateNameMismatch; - } - } - else - { - // Failure checking the policy. This is a rare error. We will assume the name check failed. - if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(certificate, $"Failure calling {nameof(Interop.Crypt32.CertVerifyCertificateChainPolicy)}"); - sslPolicyErrors |= SslPolicyErrors.RemoteCertificateNameMismatch; - } - } - } - } - - // TODO https://github.com/dotnet/runtime/issues/15462: - // Get the Trusted Issuers List from WinHTTP and use that to help narrow down - // the list of eligible client certificates. - } -} diff --git a/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpRequestCallback.cs b/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpRequestCallback.cs index 6c50ee16817cf7..4ce5a952b45253 100644 --- a/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpRequestCallback.cs +++ b/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpRequestCallback.cs @@ -10,6 +10,7 @@ using System.Net.Sockets; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Threading; using SafeWinHttpHandle = Interop.WinHttp.SafeWinHttpHandle; @@ -21,6 +22,8 @@ namespace System.Net.Http /// internal static class WinHttpRequestCallback { + private static readonly Oid ServerAuthOid = new Oid("1.3.6.1.5.5.7.3.1", "1.3.6.1.5.5.7.3.1"); + public static Interop.WinHttp.WINHTTP_STATUS_CALLBACK StaticCallbackDelegate = new Interop.WinHttp.WINHTTP_STATUS_CALLBACK(WinHttpCallback); @@ -370,13 +373,34 @@ private static void OnRequestSendingRequest(WinHttpRequestState state) try { - WinHttpCertificateHelper.BuildChain( + // Create and configure the X509Chain + chain = new X509Chain(); + chain.ChainPolicy.RevocationMode = state.CheckCertificateRevocationList ? X509RevocationMode.Online : X509RevocationMode.NoCheck; + chain.ChainPolicy.RevocationFlag = X509RevocationFlag.ExcludeRoot; + // Authenticate the remote party: (e.g. when operating in client mode, authenticate the server). + chain.ChainPolicy.ApplicationPolicy.Add(ServerAuthOid); + + if (remoteCertificateStore.Count > 0) + { + if (NetEventSource.Log.IsEnabled()) + { + foreach (X509Certificate cert in remoteCertificateStore) + { + NetEventSource.Info(remoteCertificateStore, $"Adding cert to ExtraStore: {cert.Subject}"); + } + } + + chain.ChainPolicy.ExtraStore.AddRange(remoteCertificateStore); + } + + // Call the shared BuildChainAndVerifyProperties method + // isServer=false because WinHttpHandler is a client validating a server certificate + sslPolicyErrors = System.Net.CertificateValidation.BuildChainAndVerifyProperties( + chain, serverCertificate, - remoteCertificateStore, - state.RequestMessage.RequestUri.Host, - state.CheckCertificateRevocationList, - out chain, - out sslPolicyErrors); + checkCertName: true, + isServer: false, + hostName: state.RequestMessage.RequestUri.Host); result = state.ServerCertificateValidationCallback( state.RequestMessage, diff --git a/src/libraries/System.Net.Http.WinHttpHandler/tests/UnitTests/FakeX509Certificates.cs b/src/libraries/System.Net.Http.WinHttpHandler/tests/UnitTests/FakeX509Certificates.cs index 6af1b365bd2fa8..4b0f3e7978db42 100644 --- a/src/libraries/System.Net.Http.WinHttpHandler/tests/UnitTests/FakeX509Certificates.cs +++ b/src/libraries/System.Net.Http.WinHttpHandler/tests/UnitTests/FakeX509Certificates.cs @@ -6,20 +6,13 @@ using System.Net.Security; using System.Security.Cryptography.X509Certificates; -namespace System.Net.Http +namespace System.Net { - internal static class WinHttpCertificateHelper + internal static partial class CertificateValidation { - public static void BuildChain( - X509Certificate2 certificate, - X509Certificate2Collection remoteCertificateStore, - string hostName, - bool checkCertificateRevocationList, - out X509Chain chain, - out SslPolicyErrors sslPolicyErrors) + internal static SslPolicyErrors BuildChainAndVerifyProperties(X509Chain chain, X509Certificate2 remoteCertificate, bool checkCertName, bool isServer, string? hostName) { - chain = null; - sslPolicyErrors = SslPolicyErrors.None; + return SslPolicyErrors.None; } } }