C# Webbrowser:以编程方式定义要使用的证书
C# Webbrowser: Define programatically which certificate to use
我一直致力于连接到服务器的服务,该服务器使用数字签名以经过验证的用户身份访问页面。为了能够连接到网页并使用它,我正在使用 class "WebBrowser" (https://docs.microsoft.com/es-es/dotnet/api/system.windows.forms.webbrowser?view=netcore-3.1)
实际上,当到达该网页时,会弹出此 window 弹出窗口,以选择我在我的 PC 中拥有的可能证书之一来识别我自己:
为了解决这个问题,我一直在网上搜索并找到了不同的潜在解决方案,但其中 none 充分解决了这个问题。
为了能够解决这个问题,我在 class 中包含了下一个代码,这样我就可以捕获来自 WebBrowser 的函数调用:
[Guid("6D5140C1-7436-11CE-8034-00AA006009FA")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[ComImport]
public interface UCOMIServiceProvider
{
[return: MarshalAs(UnmanagedType.I4)]
[PreserveSig]
int QueryService(
[In] ref Guid guidService,
[In] ref Guid riid,
[Out] out IntPtr ppvObject);
}
[ComImport()]
[ComVisible(true)]
[Guid("79eac9d5-bafa-11ce-8c82-00aa004ba90b")]
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
public interface IWindowForBindingUI
{
[return: MarshalAs(UnmanagedType.I4)]
[PreserveSig]
int GetWindow(
[In] ref Guid rguidReason,
[In, Out] ref IntPtr phwnd);
}
[ComImport()]
[ComVisible(true)]
[Guid("79eac9d7-bafa-11ce-8c82-00aa004ba90b")]
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
public interface IHttpSecurity
{
//derived from IWindowForBindingUI
[return: MarshalAs(UnmanagedType.I4)]
[PreserveSig]
int GetWindow(
[In] ref Guid rguidReason,
[In, Out] ref IntPtr phwnd);
[PreserveSig]
int OnSecurityProblem(
[In, MarshalAs(UnmanagedType.U4)] uint dwProblem);
}
以及能够知道何时出现问题并尝试解决问题的功能:
public int OnSecurityProblem(uint dwProblem)
{
int dwCertSelection2 = 2;
IntPtr dwCertSelection = new IntPtr(dwCertSelection2);
//ignore errors
//undocumented return code, does not work on IE6
if (dwProblem == 12044)
{
X509Store store = new X509Store("MY", StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
X509Certificate2Collection collection = (X509Certificate2Collection)store.Certificates;
X509Certificate2Collection fcollection = (X509Certificate2Collection)collection.Find(X509FindType.FindByTimeValid, DateTime.Now, false);
X509Certificate2 x509 = fcollection[2];
IntPtr unmanagedPointer = Marshal.AllocHGlobal(x509.RawData.Length);
Marshal.Copy(x509.RawData, 0, unmanagedPointer, x509.RawData.Length);
bool VF_Resultado = WinHttpSetOption(this.myWebBrowser.Handle, 47, unmanagedPointer, x509.RawData.Length);
Marshal.FreeHGlobal(unmanagedPointer);
store.Close();
return RPC_E_RETRY;
}
return S_OK;
}
为了进一步说明:
if (dwProblem == 12044) --> //12044, ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED。表示我们需要通过认证
bool VF_Resultado = WinHttpSetOption(this.myWebBrowser.Handle, 47, unmanagedPointer, x509.RawData.Length); --> //INTERNET_OPTION_SECURITY_SELECT_CLIENT_CERT 47、理论上有了这个功能我们就可以不用选择就可以指明我们要使用的certify。
此代码基于不同的来源(我尝试了两种解决方案,但没有成功):
https://docs.microsoft.com/en-us/windows/win32/winhttp/ssl-in-winhttp
问题是此代码没有按预期执行,我有各种疑问以确保它能正常工作:
- 我应该使用 WinHTTP、wininet 还是其他库?
- 调用函数 WinHttpSetOption 或 InternetSetOption 时,句柄(函数的第一个参数)应该是什么?
- 我应该如何传递X509证书以确保WebBrowser理解并使用它?我使用的代码来自 C++,我不知道我是否正确移植了数据,实际上我发送的是原始数据。
谢谢
经过多次测试和审查,我得出结论,使用webBrowser控件是不可能做到这一点的,这是因为必须使用此功能才能通过认证:
WinHttpSetOption(this.myWebBrowser.Handle, WINHTTP_OPTION_CLIENT_CERT_CONTEXT, dwCertSelection, size)
这里的主要问题是 WinHttpSetOption 需要调用函数后返回的处理程序会话 "HttpOpenRequest":
https://docs.microsoft.com/en-us/windows/win32/winhttp/hinternet-handles-in-winhttp#handle-hierarchy
无法从 C# WebBrowser 对象获取的数据。
我一直致力于连接到服务器的服务,该服务器使用数字签名以经过验证的用户身份访问页面。为了能够连接到网页并使用它,我正在使用 class "WebBrowser" (https://docs.microsoft.com/es-es/dotnet/api/system.windows.forms.webbrowser?view=netcore-3.1)
实际上,当到达该网页时,会弹出此 window 弹出窗口,以选择我在我的 PC 中拥有的可能证书之一来识别我自己:
为了解决这个问题,我一直在网上搜索并找到了不同的潜在解决方案,但其中 none 充分解决了这个问题。
为了能够解决这个问题,我在 class 中包含了下一个代码,这样我就可以捕获来自 WebBrowser 的函数调用:
[Guid("6D5140C1-7436-11CE-8034-00AA006009FA")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[ComImport]
public interface UCOMIServiceProvider
{
[return: MarshalAs(UnmanagedType.I4)]
[PreserveSig]
int QueryService(
[In] ref Guid guidService,
[In] ref Guid riid,
[Out] out IntPtr ppvObject);
}
[ComImport()]
[ComVisible(true)]
[Guid("79eac9d5-bafa-11ce-8c82-00aa004ba90b")]
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
public interface IWindowForBindingUI
{
[return: MarshalAs(UnmanagedType.I4)]
[PreserveSig]
int GetWindow(
[In] ref Guid rguidReason,
[In, Out] ref IntPtr phwnd);
}
[ComImport()]
[ComVisible(true)]
[Guid("79eac9d7-bafa-11ce-8c82-00aa004ba90b")]
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
public interface IHttpSecurity
{
//derived from IWindowForBindingUI
[return: MarshalAs(UnmanagedType.I4)]
[PreserveSig]
int GetWindow(
[In] ref Guid rguidReason,
[In, Out] ref IntPtr phwnd);
[PreserveSig]
int OnSecurityProblem(
[In, MarshalAs(UnmanagedType.U4)] uint dwProblem);
}
以及能够知道何时出现问题并尝试解决问题的功能:
public int OnSecurityProblem(uint dwProblem)
{
int dwCertSelection2 = 2;
IntPtr dwCertSelection = new IntPtr(dwCertSelection2);
//ignore errors
//undocumented return code, does not work on IE6
if (dwProblem == 12044)
{
X509Store store = new X509Store("MY", StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
X509Certificate2Collection collection = (X509Certificate2Collection)store.Certificates;
X509Certificate2Collection fcollection = (X509Certificate2Collection)collection.Find(X509FindType.FindByTimeValid, DateTime.Now, false);
X509Certificate2 x509 = fcollection[2];
IntPtr unmanagedPointer = Marshal.AllocHGlobal(x509.RawData.Length);
Marshal.Copy(x509.RawData, 0, unmanagedPointer, x509.RawData.Length);
bool VF_Resultado = WinHttpSetOption(this.myWebBrowser.Handle, 47, unmanagedPointer, x509.RawData.Length);
Marshal.FreeHGlobal(unmanagedPointer);
store.Close();
return RPC_E_RETRY;
}
return S_OK;
}
为了进一步说明:
if (dwProblem == 12044) --> //12044, ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED。表示我们需要通过认证
bool VF_Resultado = WinHttpSetOption(this.myWebBrowser.Handle, 47, unmanagedPointer, x509.RawData.Length); --> //INTERNET_OPTION_SECURITY_SELECT_CLIENT_CERT 47、理论上有了这个功能我们就可以不用选择就可以指明我们要使用的certify。
此代码基于不同的来源(我尝试了两种解决方案,但没有成功):
https://docs.microsoft.com/en-us/windows/win32/winhttp/ssl-in-winhttp
问题是此代码没有按预期执行,我有各种疑问以确保它能正常工作:
- 我应该使用 WinHTTP、wininet 还是其他库?
- 调用函数 WinHttpSetOption 或 InternetSetOption 时,句柄(函数的第一个参数)应该是什么?
- 我应该如何传递X509证书以确保WebBrowser理解并使用它?我使用的代码来自 C++,我不知道我是否正确移植了数据,实际上我发送的是原始数据。
谢谢
经过多次测试和审查,我得出结论,使用webBrowser控件是不可能做到这一点的,这是因为必须使用此功能才能通过认证:
WinHttpSetOption(this.myWebBrowser.Handle, WINHTTP_OPTION_CLIENT_CERT_CONTEXT, dwCertSelection, size)
这里的主要问题是 WinHttpSetOption 需要调用函数后返回的处理程序会话 "HttpOpenRequest":
https://docs.microsoft.com/en-us/windows/win32/winhttp/hinternet-handles-in-winhttp#handle-hierarchy
无法从 C# WebBrowser 对象获取的数据。