Freigeben über


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.