Original URL: | https://blogs.msdn.microsoft.com/alejacma/2009/06/23/how-to-call-interneterrordlg-to-deal-with-certificate-issues-on-ssl-connections-c/ |
Post name: | How to call InternetErrorDlg to deal with certificate issues on SSL connections (C#) |
Original author: | Alejandro Campos Magencio |
Posting date: | 2009-06-23T05:28:00+00:00 |
Hi all,
The following C# sample shows how to call WinInet APIs to make an SSL request and deal with possible certificate issues with InternetErrorDlg (which will show the same standard dialogs that Internet Explorer shows when something is wrong with server or client certs):using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Net;
namespace WindowsApplication1
{
class Tester
{
public static string TestSSLRequest(IntPtr hWnd, string lpszServerName, short nServerPort, string lpszUrl)
{
// Variables
IntPtr hInternet = IntPtr.Zero;
string lpszAgent;
int dwAccessType = 0;
string lpszProxyName;
string lpszProxyBypass;
int dwFlags = 0;
string lpszUserName;
string lpszPassword;
int dwService = 0;
IntPtr dwContext = IntPtr.Zero;
IntPtr hConnect = IntPtr.Zero;
string lpszVerb;
string lpszObjectName;
string lpszVersion;
string lpszReferer;
IntPtr lplpszAcceptTypes = IntPtr.Zero;
IntPtr hRequest = IntPtr.Zero;
IntPtr lpOptional = IntPtr.Zero;
int dwError = 0;
string lpszHeaders;
int dwHeadersLength = 0;
int dwOptionalLength = 0;
IntPtr lppvData = IntPtr.Zero;
int dwNumberOfBytesAvailable = 0;
IntPtr lpBuffer = IntPtr.Zero;
int dwOption = 0;
ulong ulOptionMask = 0;
int dwBufferLength = 0;
int dwNumberOfBytesToRead = 0;
int dwNumberOfBytesRead = 0;bool bResult = false;
int iResult = 0;// Let's begin!!!
try
{
// Initializes the app's use of the WinINet functions
lpszAgent = "AlejaCMa";
dwAccessType = Win32API.INTERNET_OPEN_TYPE_PRECONFIG;
lpszProxyName = null;
lpszProxyBypass = null;
dwFlags = 0;
hInternet = Win32API.InternetOpen(lpszAgent, dwAccessType, lpszProxyName, lpszProxyBypass, dwFlags);
if (hInternet.Equals(IntPtr.Zero))
{
throw new Exception("InternetOpen: Error #" + Marshal.GetLastWin32Error().ToString());
}// Opens HTTP session for the site
lpszUserName = null;
lpszPassword = null;
dwService = Win32API.INTERNET_SERVICE_HTTP;
dwFlags = 0;
dwContext = IntPtr.Zero;
hConnect = Win32API.InternetConnect(hInternet, lpszServerName, nServerPort, lpszUserName, lpszPassword, dwService, dwFlags, dwContext);
if (hConnect.Equals(IntPtr.Zero))
{
throw new Exception("InternetConnect: Error #" + Marshal.GetLastWin32Error().ToString());
}// Create HTTP request handle
lpszVerb = "GET";
lpszObjectName = lpszUrl;
lpszVersion = null;
lpszReferer = null;
lplpszAcceptTypes = IntPtr.Zero;
dwFlags = Win32API.INTERNET_FLAG_SECURE;
dwContext = IntPtr.Zero;
hRequest = Win32API.HttpOpenRequest(hConnect, lpszVerb, lpszObjectName, lpszVersion, lpszReferer, lplpszAcceptTypes, dwFlags, dwContext);
if (hRequest.Equals(IntPtr.Zero))
{
throw new Exception("HttpOpenRequest: Error #" + Marshal.GetLastWin32Error().ToString());
}// Configure request to get combined cert errors
dwOption = Win32API.INTERNET_OPTION_ERROR_MASK;
ulOptionMask = Win32API.INTERNET_ERROR_MASK_COMBINED_SEC_CERT;
dwBufferLength = Marshal.SizeOf(lpBuffer);
bResult = Win32API.InternetSetOption(hRequest, dwOption, ref ulOptionMask, dwBufferLength);
if (!bResult)
{
throw new Exception("InternetSetOption: Error #" + Marshal.GetLastWin32Error().ToString());
}do
{
// Send request to the server
lpszHeaders = null;
dwHeadersLength = 0;
lpOptional = IntPtr.Zero;
dwOptionalLength = 0;
bResult = Win32API.HttpSendRequest(hRequest, lpszHeaders, dwHeadersLength, lpOptional, dwOptionalLength);if (!bResult)
{
// Deal with possible errors
switch (Marshal.GetLastWin32Error())
{
case Win32API.ERROR_INTERNET_SEC_CERT_ERRORS:
dwError = Win32API.ERROR_INTERNET_SEC_CERT_ERRORS;
break;
case Win32API.ERROR_INTERNET_INVALID_CA:
dwError = Win32API.ERROR_INTERNET_INVALID_CA;
break;
case Win32API.ERROR_INTERNET_SEC_CERT_CN_INVALID:
dwError = Win32API.ERROR_INTERNET_SEC_CERT_CN_INVALID;
break;
case Win32API.ERROR_INTERNET_SEC_CERT_DATE_INVALID:
dwError = Win32API.ERROR_INTERNET_SEC_CERT_DATE_INVALID;
break;
case Win32API.ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED:
dwError = Win32API.ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED;
break;
default:
// Unknown error
throw new Exception("HttpSendRequest: Error #" + Marshal.GetLastWin32Error().ToString());
}// Display cert error dialog box
dwFlags = Win32API.FLAGS_ERROR_UI_FLAGS_GENERATE_DATA + Win32API.FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS;
lppvData = IntPtr.Zero;
iResult = Win32API.InternetErrorDlg(hWnd, hRequest, dwError, dwFlags, lppvData);
switch (iResult)
{
case Win32API.ERROR_SUCCESS:
break;
case Win32API.ERROR_CANCELLED:
throw new Exception("InternetErrorDlg: The function was canceled by the user");
case Win32API.ERROR_INTERNET_FORCE_RETRY:
throw new Exception("InternetErrorDlg: Function needs to redo its request. In the case of authentication this indicates that the user clicked the OK button.");
case Win32API.ERROR_INVALID_HANDLE:
throw new Exception("InternetErrorDlg: The handle to the parent window is invalid");
default:
throw new Exception("InternetErrorDlg: Error #" + iResult.ToString());
}
}
} while (!bResult);// Determine the amount of data available
dwNumberOfBytesAvailable = 0;
dwFlags = 0;
dwContext = IntPtr.Zero;
bResult = Win32API.InternetQueryDataAvailable(hRequest, ref dwNumberOfBytesAvailable, dwFlags, dwContext);
if (!bResult)
{
throw new Exception("InternetQueryDataAvailable: Error #" + Marshal.GetLastWin32Error().ToString());
}// Read data
lpBuffer = Marshal.AllocHGlobal(dwNumberOfBytesAvailable);
dwNumberOfBytesToRead = dwNumberOfBytesAvailable;
dwNumberOfBytesRead = 0;
bResult = Win32API.InternetReadFile(hRequest, lpBuffer, dwNumberOfBytesToRead, ref dwNumberOfBytesRead);
if (!bResult)
{
throw new Exception("InternetReadFile: Error #" + Marshal.GetLastWin32Error().ToString());
}// Everything went well. Return data
return Marshal.PtrToStringAnsi(lpBuffer, dwNumberOfBytesRead);
}
catch (Exception ex)
{
// Show error
MessageBox.Show("Exception: " + ex.Message);
return "";
}
finally
{
// Clean up
if (!lpBuffer.Equals(IntPtr.Zero))
{
Marshal.FreeHGlobal(lpBuffer);
}
if (!hInternet.Equals(IntPtr.Zero))
{
Win32API.InternetCloseHandle(hInternet);
}
if (!hConnect.Equals(IntPtr.Zero))
{
Win32API.InternetCloseHandle(hConnect);
}
if (!hRequest.Equals(IntPtr.Zero))
{
Win32API.InternetCloseHandle(hRequest);
}
}
}
}
}
These are the P/Invoke declarations of the APIs that I've used before:
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
namespace WindowsApplication1
{
class Win32API
{
// #define INTERNET_OPEN_TYPE_DIRECT 1
public const int INTERNET_OPEN_TYPE_DIRECT = 1;//#define INTERNET_OPEN_TYPE_PRECONFIG 0
public const int INTERNET_OPEN_TYPE_PRECONFIG = 0;// #define INTERNET_SERVICE_HTTP 3
public const int INTERNET_SERVICE_HTTP = 3;// #define INTERNET_FLAG_SECURE 0x00800000
public const int INTERNET_FLAG_SECURE = 0x00800000;// #define INTERNET_OPTION_SECURITY_CERTIFICATE 35
public const int INTERNET_OPTION_SECURITY_CERTIFICATE = 35;// #define ERROR_SUCCESS 0L
public const int ERROR_SUCCESS = 0;// #define ERROR_INVALID_HANDLE 6L
public const int ERROR_INVALID_HANDLE = 6;// #define ERROR_INSUFFICIENT_BUFFER 122L
public const int ERROR_INSUFFICIENT_BUFFER = 122;// #define ERROR_CANCELLED 1223L
public const int ERROR_CANCELLED = 1223;// #define INTERNET_OPTION_ERROR_MASK 62
public const int INTERNET_OPTION_ERROR_MASK = 62;// #define INTERNET_ERROR_MASK_COMBINED_SEC_CERT 0x2
public const ulong INTERNET_ERROR_MASK_COMBINED_SEC_CERT = 0x2;// #define INTERNET_ERROR_BASE 12000
public const int INTERNET_ERROR_BASE = 12000;// #define ERROR_INTERNET_FORCE_RETRY (INTERNET_ERROR_BASE + 32)
public const int ERROR_INTERNET_FORCE_RETRY = INTERNET_ERROR_BASE + 32;// #define ERROR_INTERNET_SEC_CERT_DATE_INVALID (INTERNET_ERROR_BASE + 37)
public const int ERROR_INTERNET_SEC_CERT_DATE_INVALID = INTERNET_ERROR_BASE + 37;// #define ERROR_INTERNET_SEC_CERT_CN_INVALID (INTERNET_ERROR_BASE + 38)
public const int ERROR_INTERNET_SEC_CERT_CN_INVALID = INTERNET_ERROR_BASE + 38;// #define ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED (INTERNET_ERROR_BASE + 44)
public const int ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED = INTERNET_ERROR_BASE + 44;// #define ERROR_INTERNET_SEC_CERT_ERRORS (INTERNET_ERROR_BASE + 55)
public const int ERROR_INTERNET_SEC_CERT_ERRORS = INTERNET_ERROR_BASE + 55;//#define ERROR_INTERNET_INVALID_CA (INTERNET_ERROR_BASE + 45)
public const int ERROR_INTERNET_INVALID_CA = INTERNET_ERROR_BASE + 45;// #define FLAGS_ERROR_UI_FILTER_FOR_ERRORS 0x01
public const int FLAGS_ERROR_UI_FILTER_FOR_ERRORS = 0x01;// #define FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS 0x02
public const int FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS = 0x02;// #define FLAGS_ERROR_UI_FLAGS_GENERATE_DATA 0x04
public const int FLAGS_ERROR_UI_FLAGS_GENERATE_DATA = 0x04;// DWORD InternetErrorDlg(
// __in HWND hWnd,
// __inout HINTERNET hRequest,
// __in DWORD dwError,
// __in DWORD dwFlags,
// __inout LPVOID *lppvData
// );
[DllImport("wininet.dll", SetLastError = true)]
public extern static int InternetErrorDlg
(
IntPtr hWnd,
IntPtr hRequest,
int dwError,
int dwFlags,
IntPtr lppvData
);// BOOL HttpSendRequest(
// __in HINTERNET hRequest,
// __in LPCTSTR lpszHeaders,
// __in DWORD dwHeadersLength,
// __in LPVOID lpOptional,
// __in DWORD dwOptionalLength
// );
[DllImport("wininet.dll", CharSet = CharSet.Auto, SetLastError = true)]
public extern static bool HttpSendRequest
(
IntPtr hRequest,
string lpszHeaders,
int dwHeadersLength,
IntPtr lpOptional,
int dwOptionalLength
);// HINTERNET HttpOpenRequest(
// __in HINTERNET hConnect,
// __in LPCTSTR lpszVerb,
// __in LPCTSTR lpszObjectName,
// __in LPCTSTR lpszVersion,
// __in LPCTSTR lpszReferer,
// __in LPCTSTR *lplpszAcceptTypes,
// __in DWORD dwFlags,
// __in DWORD_PTR dwContext
// );
[DllImport("wininet.dll", CharSet=CharSet.Auto, SetLastError = true)]
public extern static IntPtr HttpOpenRequest
(
IntPtr hConnect,
string lpszVerb,
string lpszObjectName,
string lpszVersion,
string lpszReferer,
IntPtr lplpszAcceptTypes,
int dwFlags,
IntPtr dwContext
);// HINTERNET InternetConnect(
// __in HINTERNET hInternet,
// __in LPCTSTR lpszServerName,
// __in INTERNET_PORT nServerPort,
// __in LPCTSTR lpszUsername,
// __in LPCTSTR lpszPassword,
// __in DWORD dwService,
// __in DWORD dwFlags,
// __in DWORD_PTR dwContext
// );
[DllImport("wininet.dll", CharSet = CharSet.Auto, SetLastError = true)]
public extern static IntPtr InternetConnect
(
IntPtr hInternet,
string lpszServerName,
short nServerPort,
string lpszUsername,
string lpszPassword,
int dwService,
int dwFlags,
IntPtr dwContext
);// BOOL WINAPI InternetCloseHandle(
// HINTERNET hInternet
// );
[DllImport("wininet.dll", SetLastError = true)]
public extern static bool InternetCloseHandle
(
IntPtr hInternet
);// HINTERNET InternetOpen(
// __in LPCTSTR lpszAgent,
// __in DWORD dwAccessType,
// __in LPCTSTR lpszProxyName,
// __in LPCTSTR lpszProxyBypass,
// __in DWORD dwFlags
//);
[DllImport("wininet.dll", CharSet = CharSet.Auto, SetLastError = true)]
public extern static IntPtr InternetOpen
(
string lpszAgent,
int dwAccessType,
string lpszProxyName,
string lpszProxyBypass,
int dwFlags
);// BOOL InternetReadFile(
// __in HINTERNET hFile,
// __out LPVOID lpBuffer,
// __in DWORD dwNumberOfBytesToRead,
// __out LPDWORD lpdwNumberOfBytesRead
// );
[DllImport("wininet.dll", SetLastError = true)]
public extern static bool InternetReadFile
(
IntPtr hFile,
IntPtr lpBuffer,
int dwNumberOfBytesToRead,
ref int dwNumberOfBytesRead
);// BOOL InternetQueryDataAvailable(
// __in HINTERNET hFile,
// __out LPDWORD lpdwNumberOfBytesAvailable,
// __in DWORD dwFlags,
// __in DWORD_PTR dwContext
// );
[DllImport("wininet.dll", SetLastError = true)]
public extern static bool InternetQueryDataAvailable
(
IntPtr hFile,
ref int dwNumberOfBytesAvailable,
int dwFlags,
IntPtr dwContext
);// BOOL InternetSetOption(
// __in HINTERNET hInternet,
// __in DWORD dwOption,
// __in LPVOID lpBuffer,
// __in DWORD dwBufferLength
// );
[DllImport("wininet.dll", SetLastError = true)]
public extern static bool InternetSetOption
(
IntPtr hInternet,
int dwOption,
ref ulong lpBuffer,
int dwBufferLength
);
}
}
And this is a sample Form that shows how to use my sample code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
namespace WindowsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}private void button1_Click(object sender, EventArgs e)
{
this.textBox1.Text = Tester.TestSSLRequest(this.Handle, "127.0.0.1", 443, "TEST.ASPX");
}
}
}
I hope this helps.
Regards,
Alex (Alejandro Campos Magencio)
Comments: