How to call InternetErrorDlg to deal with certificate issues on SSL connections (C#)
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
- Anonymous
October 20, 2009
Hi...do you have any idea about how to use httpqueryinfo method? can you provide c# implementation of the same if possible. i have tried using it as mentioned below[DllImport("wininet.dll", SetLastError = true)] public extern static bool HttpQueryInfo ( IntPtr hInternet, int dwInfoLevel, ref string lpvBuffer, ref int lpdwBufferLength, int lpdwIndex );But as soon as i use it gives the following exception{"Attempted to read or write protected memory. This is often an indication that other memory is corrupt."} - Anonymous
August 04, 2012
I created 2 files using code I found in some forums to circumvent windows.h and windows com port libraries:"nowindows.h"/* file nowindows.h v1.0 use at your own risk /#ifndef DWORD#define WINAPItypedef unsigned long DWORD;typedef short WCHAR;typedef void * HANDLE;#define MAX_PATH PATH_MAXtypedef unsigned char BYTE;typedef unsigned short WORD;typedef unsigned int BOOL;#include <sys/types.h>#include <sys/stat.h>#include "unistd.h"#include <fcntl.h>#define GENERIC_READ O_RDONLY //read only mode#define GENERIC_WRITE O_WRONLY //write only mode#define CREATE_ALWAYS O_CREAT //create new file#define OPEN_EXISTING 0 //fake parameter's value#define FILE_ATTRIBUTE_NORMAL 0644 // file attributes#endifand"nowindowscomport.h"/ file nowindowscomport.h v1.0 use at your own risk */typedef struct _DCB { DWORD DCBlength; DWORD BaudRate; DWORD fBinary :1; DWORD fParity :1; DWORD fOutxCtsFlow :1; DWORD fOutxDsrFlow :1; DWORD fDtrControl :2; DWORD fDsrSensitivity :1; DWORD fTXContinueOnXoff :1; DWORD fOutX :1; DWORD fInX :1; DWORD fErrorChar :1; DWORD fNull :1; DWORD fRtsControl :2; DWORD fAbortOnError :1; DWORD fDummy2 :17; WORD wReserved; WORD XonLim; WORD XoffLim; BYTE ByteSize; BYTE Parity; BYTE StopBits; char XonChar; char XoffChar; char ErrorChar; char EofChar; char EvtChar; WORD wReserved1;} DCB, *LPDCB;typedef struct _COMSTAT { DWORD fCtsHold :1; DWORD fDsrHold :1; DWORD fRlsdHold :1; DWORD fXoffHold :1; DWORD fXoffSent :1; DWORD fEof :1; DWORD fTxim :1; DWORD fReserved :25; DWORD cbInQue; DWORD cbOutQue;} COMSTAT, *LPCOMSTAT;typedef struct _COMMTIMEOUTS { DWORD ReadIntervalTimeout; DWORD ReadTotalTimeoutMultiplier; DWORD ReadTotalTimeoutConstant; DWORD WriteTotalTimeoutMultiplier; DWORD WriteTotalTimeoutConstant;} COMMTIMEOUTS, LPCOMMTIMEOUTS;#define ERROR_INVALID_HANDLE 6L/ Purge functions for Comm Port /#define PURGE_TXABORT 0x0001 / Kill the pending/current writes to the @@ -377,11 +382,4 @@ */// #define DTR_CONTROL_ENABLE 1// #define DTR_CONTROL_HANDSHAKE 2#define PURGE_RXCLEAR 0x0008#define PURGE_TXCLEAR 0x0004#define PURGE_RXABORT 0x0002// DTR Control Flow Values.#define DTR_CONTROL_DISABLE 0x00#define DTR_CONTROL_ENABLE 0x01#define DTR_CONTROL_HANDSHAKE 0x02#define RTS_CONTROL_DISABLE 0x00#define NOPARITY 0#define ONESTOPBIT 0 - Anonymous
May 18, 2015
Can you tell me how to find the value of dWflag? Such as ERROR_INVALID_HANDLE,INTERNET_FLAG_NO_COOKIES ? Thanks.