public static void BuildChain( X509Certificate2 certificate, string hostName, bool checkCertificateRevocationList, out X509Chain chain, out SslPolicyErrors sslPolicyErrors) { chain = null; sslPolicyErrors = SslPolicyErrors.None; // Build the chain. chain = new X509Chain(); chain.ChainPolicy.RevocationMode = checkCertificateRevocationList ? X509RevocationMode.Online : X509RevocationMode.NoCheck; chain.ChainPolicy.RevocationFlag = X509RevocationFlag.ExcludeRoot; chain.Build(certificate); if (chain.ChainStatus != null && chain.ChainStatus.Length != 0) { sslPolicyErrors |= SslPolicyErrors.RemoteCertificateChainErrors; } // Verify the hostName matches the certificate. unsafe { var cppStruct = new Interop.Crypt32.CERT_CHAIN_POLICY_PARA(); cppStruct.cbSize = (uint)Marshal.SizeOf <Interop.Crypt32.CERT_CHAIN_POLICY_PARA>(); cppStruct.dwFlags = 0; var eppStruct = new Interop.Crypt32.SSL_EXTRA_CERT_CHAIN_POLICY_PARA(); eppStruct.cbSize = (uint)Marshal.SizeOf <Interop.Crypt32.SSL_EXTRA_CERT_CHAIN_POLICY_PARA>(); eppStruct.dwAuthType = Interop.Crypt32.AuthType.AUTHTYPE_CLIENT; cppStruct.pvExtraPolicyPara = &eppStruct; fixed(char *namePtr = hostName) { eppStruct.pwszServerName = namePtr; cppStruct.dwFlags = Interop.Crypt32.CertChainPolicyIgnoreFlags.CERT_CHAIN_POLICY_IGNORE_ALL & ~Interop.Crypt32.CertChainPolicyIgnoreFlags.CERT_CHAIN_POLICY_IGNORE_INVALID_NAME_FLAG; var status = new Interop.Crypt32.CERT_CHAIN_POLICY_STATUS(); status.cbSize = (uint)Marshal.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) { sslPolicyErrors |= SslPolicyErrors.RemoteCertificateNameMismatch; } } else { // Failure checking the policy. This is a rare error. We will assume the name check failed. // TODO: Log this error. sslPolicyErrors |= SslPolicyErrors.RemoteCertificateNameMismatch; } } } }
private static void RequestCallback( IntPtr handle, WinHttpRequestState state, uint internetStatus, IntPtr statusInformation, uint statusInformationLength) { try { switch (internetStatus) { case Interop.WinHttp.WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING: OnRequestHandleClosing(state); return; case Interop.WinHttp.WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE: OnRequestSendRequestComplete(state); return; case Interop.WinHttp.WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE: Debug.Assert(statusInformationLength == Marshal.SizeOf <int>()); int bytesAvailable = Marshal.ReadInt32(statusInformation); OnRequestDataAvailable(state, bytesAvailable); return; case Interop.WinHttp.WINHTTP_CALLBACK_STATUS_READ_COMPLETE: OnRequestReadComplete(state, statusInformationLength); return; case Interop.WinHttp.WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE: OnRequestWriteComplete(state); return; case Interop.WinHttp.WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE: OnRequestReceiveResponseHeadersComplete(state); return; case Interop.WinHttp.WINHTTP_CALLBACK_STATUS_REDIRECT: string redirectUriString = Marshal.PtrToStringUni(statusInformation); var redirectUri = new Uri(redirectUriString); OnRequestRedirect(state, redirectUri); return; case Interop.WinHttp.WINHTTP_CALLBACK_STATUS_SENDING_REQUEST: OnRequestSendingRequest(state); return; case Interop.WinHttp.WINHTTP_CALLBACK_STATUS_REQUEST_ERROR: Debug.Assert( statusInformationLength == Marshal.SizeOf <Interop.WinHttp.WINHTTP_ASYNC_RESULT>(), "RequestCallback: statusInformationLength=" + statusInformationLength + " must be sizeof(WINHTTP_ASYNC_RESULT)=" + Marshal.SizeOf <Interop.WinHttp.WINHTTP_ASYNC_RESULT>()); var asyncResult = Marshal.PtrToStructure <Interop.WinHttp.WINHTTP_ASYNC_RESULT>(statusInformation); OnRequestError(state, asyncResult); return; default: return; } } catch (Exception ex) { Interop.WinHttp.WinHttpCloseHandle(handle); state.SavedException = ex; } }