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;
}
}
}