/// <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);
 }