/// <summary> /// Проставить опции проверки сертификатов сервера /// </summary> /// <param name="hRequest"> /// Указатель на HTTPS-запрос /// </param> private static void SetCertificateCheckOptions(IntPtr hRequest) { uint flagsLen = sizeof(uint); var flags = new WinInet.InternetOptionSecurityFlags(); var pFlags = Marshal.AllocHGlobal(Marshal.SizeOf(flags)); Marshal.StructureToPtr(flags, pFlags, true); var queryOption = WinInet.InternetQueryOptionW(hRequest, WinInet.INTERNET_OPTION_SECURITY_FLAGS, pFlags, ref flagsLen); flags = (WinInet.InternetOptionSecurityFlags) Marshal.PtrToStructure(pFlags, typeof(WinInet.InternetOptionSecurityFlags)); Marshal.FreeHGlobal(pFlags); if (!queryOption) { ThrowWinInetLastException("InternetQueryOption + INTERNET_OPTION_SECURITY_FLAGS"); } flags.value |= WinInet.SECURITY_FLAG_IGNORE_REVOCATION; flags.value |= WinInet.SECURITY_FLAG_IGNORE_UNKNOWN_CA; pFlags = Marshal.AllocHGlobal(Marshal.SizeOf(flags)); Marshal.StructureToPtr(flags, pFlags, true); var certRevSet = WinInet.InternetSetOptionW(hRequest, WinInet.INTERNET_OPTION_SECURITY_FLAGS, pFlags, flagsLen); Marshal.FreeHGlobal(pFlags); if (!certRevSet) { ThrowWinInetLastException("InternetSetOption + INTERNET_OPTION_SECURITY_FLAGS"); } }
/// <summary> /// Получить содержимое /// </summary> /// <param name="hRequest"> /// Указатель на HTTPS-запрос /// </param> /// <param name="result"> /// Результат HTTPS-запроса /// </param> private static void GetContent(IntPtr hRequest, HttpResult result) { using (var stream = new MemoryStream()) { const uint chunkSize = 1024U; using (var buffer = InteropUtil.Alloc(chunkSize)) { while (true) { var bytesRead = 0U; if (!WinInet.InternetReadFile(hRequest, buffer.Address, chunkSize, ref bytesRead)) { ThrowWinInetLastException("InternetReadFile"); } for (var i = 0; i < bytesRead; i++) { stream.WriteByte(Marshal.ReadByte(buffer.Address + i)); } if (bytesRead <= 0) { break; } } } result.Content = stream.ToArray(); } }
/// <summary> /// Создать HTTPS-запрос /// </summary> /// <returns> /// Указатель на HTTPS-запрос /// </returns> private IntPtr CreateRequest() { var hRequest = WinInet.HttpOpenRequest( hConnect, "POST", uri.AbsolutePath, null, null, IntPtr.Zero, WinInet.INTERNET_FLAG_SECURE | WinInet.INTERNET_FLAG_IGNORE_CERT_CN_INVALID | WinInet.INTERNET_FLAG_IGNORE_CERT_DATE_INVALID | WinInet.INTERNET_FLAG_NO_AUTO_REDIRECT | WinInet.INTERNET_FLAG_PRAGMA_NOCACHE | WinInet.INTERNET_FLAG_NO_CACHE_WRITE | WinInet.INTERNET_FLAG_KEEP_CONNECTION, (IntPtr)1); if (hRequest == IntPtr.Zero) { ThrowWinInetLastException("HttpOpenRequest"); } return(hRequest); }
/// <summary> /// Отправить HTTPS-запрос /// </summary> /// <param name="hRequest"> /// Указатель на HTTPS-запрос /// </param> /// <param name="head"> /// Заголовки запроса /// </param> /// <param name="body"> /// Тело запроса /// </param> private static void ExecuteRequest(IntPtr hRequest, string head, byte[] body) { var bSend = WinInet.HttpSendRequestW(hRequest, head, head.Length, body, body.Length); if (!bSend) { ThrowWinInetLastException("HttpSendRequest"); } }
/// <summary> /// Проставить клиентский сертификат /// </summary> /// <param name="hRequest"> /// Указатель на HTTPS-запрос /// </param> private void SetClientCertificate(IntPtr hRequest) { var certSet = WinInet.InternetSetOptionW( hRequest, WinInet.INTERNET_OPTION_CLIENT_CERT_CONTEXT, pCertContext, (uint)Marshal.SizeOf(typeof(Crypt32.CERT_CONTEXT))); if (!certSet) { ThrowWinInetLastException("InternetSetOption + INTERNET_OPTION_CLIENT_CERT_CONTEXT"); } }
/// <summary> /// Получить тип содержимого /// </summary> /// <param name="hRequest"> /// Указатель на HTTPS-запрос /// </param> /// <param name="result"> /// Результат HTTPS-запроса /// </param> private static void GetContentType(IntPtr hRequest, HttpResult result) { var statusSize = 256U; using (var buffer = InteropUtil.Alloc(statusSize)) { if (!WinInet.HttpQueryInfoW( hRequest, WinInet.HTTP_QUERY_CONTENT_TYPE, buffer.Address, ref statusSize, IntPtr.Zero)) { ThrowWinInetLastException("HttpQueryInfoW(HTTP_QUERY_CONTENT_TYPE)"); } result.ContentType = Marshal.PtrToStringUni(buffer.Address); } }
/// <summary> /// Получить код статуса HTTP /// </summary> /// <param name="hRequest"> /// Указатель на HTTPS-запрос /// </param> /// <param name="result"> /// Результат HTTPS-запроса /// </param> private static void GetStatusCode(IntPtr hRequest, HttpResult result) { uint statusSize = sizeof(uint); using (var buffer = InteropUtil.Alloc(statusSize)) { if (!WinInet.HttpQueryInfoW( hRequest, WinInet.HTTP_QUERY_FLAG_NUMBER | WinInet.HTTP_QUERY_STATUS_CODE, buffer.Address, ref statusSize, IntPtr.Zero)) { ThrowWinInetLastException("HttpQueryInfoW(HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE)"); } result.Code = (HttpStatusCode)Marshal.ReadInt32(buffer.Address); } }
/// <summary> /// Выполнить HTTPS запрос /// </summary> /// <param name="head"> /// Заголовки запроса /// </param> /// <param name="body"> /// Тело запроса /// </param> /// <returns> /// Результат HTTPS-запроса /// </returns> public HttpResult ExecuteRequest(string head, byte[] body) { var result = new HttpResult(); var hRequest = CreateRequest(); if (hasClientCertificate) { SetClientCertificate(hRequest); SetCertificateCheckOptions(hRequest); } ExecuteRequest(hRequest, head, body); GetStatusCode(hRequest, result); GetContentType(hRequest, result); GetContent(hRequest, result); WinInet.InternetCloseHandle(hRequest); return(result); }
/// <summary> /// Конструктор /// </summary> /// <param name="uri"> /// Адрес конечной точки /// </param> /// <param name="certificate"> /// Клиентский сертифика /// </param> public WinInetHttpsClient(Uri uri, X509Certificate2 certificate) { this.uri = uri; #region Инициализация Crypt32 if (certificate != null) { // Поиск сертификата var hStore = Crypt32.CertOpenSystemStoreW(0, "My"); var hash = certificate.GetCertHash(); Crypt32.CRYPTOAPI_BLOB cryptBlob; cryptBlob.cbData = hash.Length; using (var hashHandle = InteropUtil.Pin(hash)) { cryptBlob.pbData = hashHandle.Address; using (var blobHandle = InteropUtil.Pin(cryptBlob)) { pCertContext = Crypt32.CertFindCertificateInStore( hStore, Crypt32.X509_ASN_ENCODING | Crypt32.PKCS_7_ASN_ENCODING, 0, Crypt32.CERT_FIND_SHA1_HASH, blobHandle.Address, IntPtr.Zero); if (pCertContext == IntPtr.Zero) { ThrowWinInetLastException("CertFindCertificateInStore"); } } } Crypt32.CertCloseStore(hStore, 0); hasClientCertificate = true; } else { hasClientCertificate = false; } #endregion #region Инициализация WinInet // Инициализация WinInet hInternet = WinInet.InternetOpenW( "WSL_Proxy", WinInet.INTERNET_OPEN_TYPE_DIRECT, null, null, 0); if (hInternet == IntPtr.Zero) { ThrowWinInetLastException("InternetOpen"); } hConnect = WinInet.InternetConnectW(hInternet, uri.Host, (short)uri.Port, null, null, WinInet.INTERNET_SERVICE_HTTP, 0, IntPtr.Zero); if (hConnect == IntPtr.Zero) { ThrowWinInetLastException("InternetConnect"); } #endregion }
/// <summary> /// Выполняет определяемые приложением задачи, связанные с удалением, высвобождением или сбросом неуправляемых ресурсов. /// </summary> public void Dispose() { WinInet.InternetCloseHandle(hConnect); WinInet.InternetCloseHandle(hInternet); }