WinHTTP 中的 SSLSSL in WinHTTP

Microsoft Windows HTTP Services (WinHTTP) 支持安全套接字层 (SSL) 包括客户端证书的证书。Microsoft Windows HTTP Services (WinHTTP) supports Secure Sockets Layer (SSL) transactions including client certificates. 本主题介绍 SSL 事务中涉及的概念,以及如何使用 WinHTTP 处理它们。This topic explains concepts involved in an SSL transaction and how they are handled using WinHTTP.

安全套接字层 (Secure Sockets Layer)Secure Sockets Layer

SSL 是确保安全 HTTP 事务的已建立标准。SSL is an established standard for ensuring secure HTTP transactions. SSL 提供一种机制,用于在客户端和服务器之间的所有事务上执行最多128位加密。SSL provides a mechanism to perform up to 128-bit encryption on all transactions between the client and server. 它使客户端能够通过使用服务器证书来验证服务器是否属于受信任的实体。It enables the client to verify that the server belongs to a trusted entity through the use of server certificates. 它还使服务器能够通过客户端证书来确认客户端的标识。It also enables the server to confirm the identity of the client with client certificates.

在客户端首次从安全超文本传输协议请求资源 (HTTPS) 服务器时,会在 SSL 握手中协商每个问题的加密、服务器标识和客户端标识。Each of these issues encryption, server identity, and client identity are negotiated in the SSL handshake that occurs when a client first requests a resource from a Secure Hypertext Transfer Protocol (HTTPS) server. 实质上,每个客户端和服务器都提供必需和首选设置的列表。Essentially, the client and server each present a list of required and preferred settings. 如果可以同意并满足一组通用要求,则会建立 SSL 连接。If a common set of requirements can be agreed upon and met, an SSL connection is established.

WinHTTP 为使用 SSL 提供了一个高级界面。WinHTTP provides a high level interface for using SSL. 虽然 SSL 握手和事务的详细信息是在内部处理的,但是 WinHTTP 允许您检索加密级别、指定安全协议以及与服务器和客户端证书交互。While the details of the SSL handshake and transaction are handled internally, WinHTTP enables you to retrieve encryption levels, specify the security protocol, and interact with server and client certificates. 以下各节提供了有关创建基于 WinHTTP 的应用程序的详细信息,这些应用程序可选择 SSL 协议版本、检查服务器证书并选择要发送到 HTTPS 服务器的客户端证书。The following sections provide details on creating WinHTTP based applications that elect an SSL protocol version, examine server certificates, and select client certificates to send to HTTPS servers.

服务器证书Server Certificates

服务器证书将从服务器发送到客户端,以便客户端可以获取服务器的公钥,并确保服务器已由证书颁发机构验证。Server certificates are sent from the server to the client so that the client can obtain a public key for the server and ensure that the server has been verified by a certification authority. 证书可以包含不同类型的数据。Certificates can contain different types of data. 例如,x.509 证书包括证书的格式、证书的序列号、用于对证书进行签名的算法、颁发证书 (CA) 的证书颁发机构的名称、请求证书的实体的名称和公钥,以及 CA 的签名等。For example, an X.509 certificate includes the format of the certificate, the serial number of the certificate, the algorithm used to sign the certificate, the name of the certification authority (CA) that issued the certificate, the name and public key of the entity that requests the certificate, and the CA's signature.

(API) 使用 WinHTTP 应用程序编程接口时,可以通过调用 WinHttpQueryOption 并指定 WinHTTP _ 选项 " _ 安全 _ 证书 _ 结构 " 标志来检索服务器证书。When using the WinHTTP application programming interface (API), you can retrieve a server certificate by calling WinHttpQueryOption and specifying the WINHTTP_OPTION_SECURITY_CERTIFICATE_STRUCT flag. 服务器证书在 WINHTTP _ 证书 _ 信息 结构中返回。The server certificate is returned in a WINHTTP_CERTIFICATE_INFO structure. 如果希望检索证书上下文,请改为指定 WINHTTP _ 选项 _ 服务器 _ 证书 _ 上下文 标志。If you prefer to retrieve the certificate context, specify the WINHTTP_OPTION_SERVER_CERT_CONTEXT flag instead.

如果服务器证书包含错误,则可以在状态回调函数中获取有关错误的详细信息。If a server certificate contains errors, details about the error can be obtained in the status callback function. WINHTTP _ 回拨 _ 状态 _ 安全 _ 失败通知指示服务器证书错误。The WINHTTP_CALLBACK_STATUS_SECURE_FAILURE notification indicates an error with a server certificate. LpvStatusInformation 参数包含一个或多个详细的错误标志。The lpvStatusInformation parameter contains one or more detailed error flags. 有关详细信息,请参阅 WINHTTP _ 状态 _ 回拨See WINHTTP_STATUS_CALLBACK for more information.

客户端证书Client Certificates

在 SSL 握手期间,服务器可能需要进行身份验证。During the SSL handshake, the server might require authentication. 客户端通过向服务器提供有效的客户端证书进行身份验证。The client is authenticated by supplying a valid client certificate to the server. WinHTTP 使你可以从本地 证书存储中选择和发送证书。WinHTTP enables you to select and send a certificate from a local certificate store. 以下部分介绍了在使用 WinHTTP API 或 WinHttpRequest 对象时提供客户端证书的过程。The following sections describe the process that provides client certificates when using either the WinHTTP API or the WinHttpRequest object.

WinHTTP APIWinHTTP API

由于 HTTPS 服务器要求身份验证, WinHttpSendRequestWinHttpReceiveResponse 都可能无法表示请求未成功。Both WinHttpSendRequest and WinHttpReceiveResponse can fail to indicate that a request was unsuccessful because the HTTPS server requires authentication. 在这些情况下,请调用 GetLastError 以 _ 返回 _ 所需的错误 WINHTTP 客户端 _ 身份验证 _ 证书 _ 。In these cases, call GetLastError to returns ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED. 收到此错误后,使用适当的 CryptoAPI 函数查找适当的证书。Upon receiving this error, use the appropriate CryptoAPI functions to find an appropriate certificate. 通过使用 WINHTTP _ 选项 _ 客户端 _ 证书 _ 上下文标志调用 WinHttpSetOption ,指明此证书应与下一个请求一起发送。Indicate that this certificate should be sent with the next request by calling WinHttpSetOption with the WINHTTP_OPTION_CLIENT_CERT_CONTEXT flag.

下面的代码示例演示了如何在返回错误 _ WINHTTP _ 客户端 _ 身份验证 _ 证书 _ 需要错误后,基于使用者名称打开证书存储和查找证书。The following code example shows how to open a certificate store and locate a certificate based on subject name after the ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED error has been returned.

  if( !WinHttpReceiveResponse( hRequest, NULL ) )
  {
    if( GetLastError( ) == ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED )
    {
      //MY is the store the certificate is in.
      hMyStore = CertOpenSystemStore( 0, TEXT("MY") );
      if( hMyStore )
      {
        pCertContext = CertFindCertificateInStore( hMyStore,
             X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
             0,
             CERT_FIND_SUBJECT_STR,
             (LPVOID) szCertName, //Subject string in the certificate.
             NULL );
        if( pCertContext )
        {
          WinHttpSetOption( hRequest, 
                            WINHTTP_OPTION_CLIENT_CERT_CONTEXT,
                            (LPVOID) pCertContext, 
                            sizeof(CERT_CONTEXT) );
          CertFreeCertificateContext( pCertContext );
        }
        CertCloseStore( hMyStore, 0 );

        // NOTE: Application should now resend the request.
      }
    }
  }

重新发送包含客户端证书的请求之前,可以确定应用程序是否可接受支持的加密级别。Before resending a request that contains a client certificate, you can determine if the supported level of encryption is acceptable for your application. 调用 WinHttpQueryOption 并指定 WINHTTP _ 选项 _ 安全 _ 标志 标志来确定所使用的加密级别。Call WinHttpQueryOption and specify the WINHTTP_OPTION_SECURITY_FLAGS flag to determine the level of encryption that is used.

SSL 客户端身份验证的颁发者列表检索Issuer List Retrieval for SSL Client Authentication

当 WinHttp 客户端应用程序向要求 SSL 客户端身份验证的安全 HTTP 服务器发送请求时,如果应用程序未提供客户端证书,WinHttp 将返回 _ _ _ _ _ 所需的错误 WinHttp 客户端身份验证证书When the WinHttp client application sends a request to a secure HTTP server that requires SSL client authentication, WinHttp returns an ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED if the application has not supplied a client certificate. 对于在 Windows Server 2008 和 Windows Vista 上运行的计算机,WinHttp 允许应用程序在身份验证质询中检索由服务器提供的证书颁发者列表。For computers running on Windows Server 2008 and Windows Vista, WinHttp enables the application to retrieve the certificate issuer list supplied by the server in the authentication challenge. 颁发者列表指定服务器授权的证书颁发机构 (颁发客户端证书的证书颁发机构) 的列表。The Issuer List specifies a list of Certificate Authorities (CAs) that are authorized by the server to issue client certificates. 应用程序将筛选颁发者列表以获取所需的证书。The application filters the issuer list to obtain the required certificate.

WinHttpSendRequestWinHttpReceiveResponse 返回 _ 所需的 "winhttp _ 客户端 _ 身份验证 _ 证书 _" 时,winhttp 客户端应用程序将检索颁发者列表。The WinHttp client application retrieves the issuer list when WinHttpSendRequest, or WinHttpReceiveResponse returns ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED. 返回此错误时,应用程序将调用 WinHttpQueryOption ,并提供 WINHTTP _ 选项 " _ 客户端 _ 证书 _ 颁发者 _ 列表 " 选项。When this error is returned, the application calls WinHttpQueryOption with the WINHTTP_OPTION_CLIENT_CERT_ISSUER_LIST option. LpBuffer 参数必须足够大,以便包含指向 SecPkgContext _ IssuerListInfoEx结构的指针。The lpBuffer parameter must be large enough to contain a pointer to the SecPkgContext_IssuerListInfoEx structure. 下面的代码示例演示如何检索颁发者列表。The following code example shows how to retrieve the issuer list.

#include <windows.h>
#include <winhttp.h>
#include <schannel.h>

//...

void GetIssuerList(HINTERNET hRequest)
{
  SecPkgContext_IssuerListInfoEx* pIssuerList = NULL;
  DWORD dwBufferSize = sizeof(SecPkgContext_IssuerListInfoEx*);

  if (WinHttpQueryOption(hRequest,
           WINHTTP_OPTION_CLIENT_CERT_ISSUER_LIST,
           &pIssuerList,
           &dwBufferSize) == TRUE)
  {
    // Use the pIssuerList for cert store filtering.
    GlobalFree(pIssuerList); // Free the issuer list when done.
  }
}

SecPkgContext _ IssuerListInfoEx结构 cIssuersaIssuers 中的信息可用于搜索证书,如下面的代码示例中所示。The information in the SecPkgContext_IssuerListInfoEx structure, cIssuers and aIssuers, can be used to search for the certificate as shown in the code example below. 有关详细信息,请参阅 CertFindChainInStoreFor more information, see CertFindChainInStore.

PCERT_CONTEXT pClientCert = NULL;
PCCERT_CHAIN_CONTEXT pClientCertChain = NULL;

CERT_CHAIN_FIND_BY_ISSUER_PARA SrchCriteria;
::ZeroMemory(&SrchCriteria, sizeof(CERT_CHAIN_FIND_BY_ISSUER_PARA));
SrchCriteria.cbSize = sizeof(CERT_CHAIN_FIND_BY_ISSUER_PARA);

SrchCriteria.cIssuer = pIssuerList->cIssuers;
SrchCriteria.rgIssuer = pIssuerList->aIssuers;

pClientCertChain = CertFindChainInStore(
            hClientCertStore,
            X509_ASN_ENCODING,
            CERT_CHAIN_FIND_BY_ISSUER_CACHE_ONLY_URL_FLAG |
            // Do not perform wire download when building chains.
            CERT_CHAIN_FIND_BY_ISSUER_CACHE_ONLY_FLAG,
            // Do not search pCacheEntry->_ClientCertStore 
            // for issuer certs.
            CERT_CHAIN_FIND_BY_ISSUER,
            &SrchCriteria,
            NULL);

if (pClientCertChain)
{
    pClientCert = (PCERT_CONTEXT) pClientCertChain->rgpChain[0]->rgpElement[0]->pCertContext;

    CertDuplicateCertificateContext(pClientCert);

    CertFreeCertificateChain(pClientCertChain);

    pClientCertChain = NULL;
}

可选客户端 SSL 证书Optional Client SSL Certificates

从 Windows Server 2008 和 Windows Vista 开始,WinHttp API 支持可选的客户端证书。Starting in Windows Server 2008 and Windows Vista, the WinHttp API supports optional client certificates. 当服务器请求客户端证书时, WinHttpSendRequestWinHttpRecieveResponse 将返回 错误 " _ WINHTTP _ 客户端 _ 身份验证 _ 证书 _ 需要 错误"。When the server requests a client certificate, WinHttpSendRequest, or WinHttpRecieveResponse returns an ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED error. 如果服务器请求证书,但不需要,则应用程序可以指定此选项以指示该证书没有证书。If the server requests the certificate, but does not require it, the application can specify this option to indicate that it does not have a certificate. 服务器可以选择另一种身份验证方案,也可以允许匿名访问服务器。The server can choose another authentication scheme or allow anonymous access to the server. 应用程序在 WinHttpSetOptionlpBuffer 参数中指定 WINHTTP _ 无 _ 客户端 _ 证书 _ 上下文 宏,如下面的代码示例中所示。The application specifies the WINHTTP_NO_CLIENT_CERT_CONTEXT macro in the lpBuffer parameter of WinHttpSetOption as shown in the following code example.

BOOL fRet = WinHttpSetOption ( hRequest,
                               WINHTTP_OPTION_CLIENT_CERT_CONTEXT,
                               WINHTTP_NO_CLIENT_CERT_CONTEXT,
                               0);

如果 WINHTTP _ 未设置 _ 客户端 _ 证书 _ 上下文 ,并且服务器仍需要客户端证书,则它可能会发送 403 HTTP 状态代码。If the WINHTTP_NO_CLIENT_CERT_CONTEXT is set, and the server still requires a client certificate, it may send a 403 HTTP status code. 有关详细信息,请参阅 WINHTTP _ 选项 _ 客户端 _ 证书 _ 颁发者 _ 列表 选项。For more information, see the WINHTTP_OPTION_CLIENT_CERT_ISSUER_LIST option.

WinHttpRequest 对象WinHttpRequest Object

使用 WinHttpRequest对象的 SetClientCertificate方法选择要使用请求发送到服务器的客户端证书。Use the SetClientCertificate method of the WinHttpRequest object to select client certificates to send to the server with a request. 通过使用 SetClientCertificate 方法指定证书选择字符串来选择证书。Select a certificate by specifying a certificate selection string with the SetClientCertificate method. 证书选择字符串由证书位置、 证书存储和用反斜杠分隔的使用者名称组成。The certificate selection string consists of the certificate location, certificate store, and subject name delimited by backslashes. 下表列出了此选择字符串的组件。The following table lists components for this selection string.

组件Component 说明Description 可能值Possible values
位置Location 确定用于存储证书的注册表项。Determines the registry key under which the certificates are stored. 可能的值为 "本地 _ 计算机",表示 证书存储HKEY _ 本地 _ 计算机The possible values are "LOCAL_MACHINE" to indicate that the certificate store is under HKEY_LOCAL_MACHINE
和 "当前 _ 用户",表示 证书存储区 位于非模拟的 HKEY _ 当前 _ 用户下。and "CURRENT_USER" to indicate that the certificate store is under the non-impersonated HKEY_CURRENT_USER.
此组件区分大小写。This component is case-sensitive.
证书存储Certificate store 指示包含相关证书的 证书存储区 的名称。Indicates the name of the certificate store that contains the relevant certificate. 典型的 证书存储区 为 "MY"、"Root" 和 "TrustedPeople"。Typical certificate stores are "MY", "Root", and "TrustedPeople". 此组件区分大小写。This component is case-sensitive.
使用者名称Subject name 标识指定的 证书存储中的证书。Identifies a certificate within the specified certificate store. 将选择第一个包含为此组件指定的字符串的证书。The first certificate that contains the string specified for this component is selected. 使用者名称可以是任意字符串。The subject name can be any string. 空字符串表示应该使用 证书存储 中的第一个证书。A blank string indicates that the first certificate in the certificate store should be used. 此组件不区分大小写。This component is case-insensitive.

证书存储区的名称和位置是可选组件。The certificate store name and location are optional components. 但是,如果指定了 证书存储,则还必须指定该 证书存储区 的位置。However, if you specify a certificate store, you must also specify the location of that certificate store. 默认位置为 "当前 _ 用户",默认 证书存储区 为 "MY"。The default location is CURRENT_USER and the default certificate store is "MY".

下面的代码示例演示如何使用 HKEY _ 本地 _ 计算机 下的注册表中的 "个人"证书存储区来指定是否应选择主题为 "我的 Middle-Tier 证书" 的证书。The following code example shows how to specify that a certificate with the subject "My Middle-Tier Certificate" should be chosen from the "Personal" certificate store in the registry under HKEY_LOCAL_MACHINE.

HttpReq.SetClientCertificate("LOCAL_MACHINE\Personal\My Middle-Tier Certificate")

备注

在某些语言中,反斜杠是转义符。In some languages the backslash is an escape character. 请记得为此修改证书选择字符串。Remember to modify the certificate selection string to account for this. 例如,在 Microsoft JScript 中,使用两个相邻的反斜杠而不是一个。For example, in Microsoft JScript, use two adjacent backslashes instead of one.

如果未指定证书,并且 HTTPS 服务器需要客户端证书,则 WinHTTP 会选择默认 证书存储中的第一个证书。If you do not specify a certificate and an HTTPS server requires a client certificate, WinHTTP selects the first certificate in the default certificate store. 如果证书不存在,则会引发错误。If no certificates exist, an error is raised. 如果未接受该证书,则服务器将返回403状态代码,以指示无法完成该请求。If the certificate is not accepted, the server returns a 403 status code to indicate that the request cannot be fulfilled. 然后,可以使用 SetClientCertificate 选择更合适的证书,然后重新发送请求。You can then choose a more appropriate certificate with SetClientCertificate and resend the request.