Microsoft Windows HTTP服务(WinHTTP)支持安全套接字层(SSL)事务,包括客户端证书。 本主题说明SSL事务中涉及的概念以及如何使用WinHTTP处理它们。
SSL
是确保安全HTTP
事务的成熟标准。 SSL
提供了一种机制,可对客户端和服务器之间的所有事务执行高达128位的加密。它使客户端能够通过使用服务器证书来验证服务器是否属于可信实体。它还使服务器能够使用客户端证书确认客户端的身份。
这些问题中的每一个(加密,服务器身份和客户端身份)都是在客户端首次从安全超文本传输协议(HTTPS
)服务器请求资源时发生的SSL
握手中协商的。基本上,客户端和服务器各自呈现所需和首选设置的列表。如果可以商定并满足一组公共要求,则建立SSL连接。
WinHTTP
提供了使用SSL
的高级接口。虽然SSL
握手和事务的详细信息在内部处理,WinHTTP
使您能够检索加密级别,指定安全协议,并与服务器和客户端证书交互。以下部分提供有关创建基于WinHTTP
的应用程序的详细信息,这些应用程序选择SSL
协议版本,检查服务器证书并选择要发送到HTTPS
服务器的客户端证书。
服务器证书从服务器发送到客户端,以便客户端可以获得服务器的公钥,并确保服务器已经由证书颁发机构验证。证书可以包含不同类型的数据。例如,X.509
证书包括证书的格式,证书的序列号,用于签署证书的算法,颁发证书的证书颁发机构(CA
)的名称,证书的名称和公钥请求证书的实体,以及CA
的签名。
当使用WinHTTP
应用程序编程接口(API
)时,您可以通过调用WinHttpQueryOption
并指定WINHTTP_OPTION_SECURITY_CERTIFICATE_STRUCT
标志来检索服务器证书。服务器证书以WINHTTP_CERTIFICATE_INFO
结构返回。如果您希望检索证书上下文,请改为指定WINHTTP_OPTION_SERVER_CERT_CONTEXT
标志。
如果服务器证书包含错误,则可以在状态回调函数中获取有关错误的详细信息。 WINHTTP_CALLBACK_STATUS_SECURE_FAILURE
通知指示服务器证书错误。 lpvStatusInformation
参数包含一个或多个详细错误标志。有关详细信息,请参阅WINHTTP_STATUS_CALLBACK
。
在SSL
握手期间,服务器可能需要身份验证。通过向服务器提供有效的客户端证书来认证客户端。 WinHTTP
使您能够从本地证书库中选择和发送证书。以下部分描述了在使用WinHTTP API
或WinHttpRequest
对象时提供客户端证书的过程。
WinHttpSendRequest
和WinHttpReceiveResponse
都可能无法指示请求不成功,因为HTTPS
服务器需要身份验证。在这些情况下,调用GetLastError
以返回ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED
。收到此错误时,请使用适当的CryptoAPI
函数来查找适当的证书。指示此证书应与下一个请求一起发送,通过调用带有WINHTTP_OPTION_CLIENT_CERT_CONTEXT
标志的WinHttpSetOption
。
以下代码示例说明如何在返回ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED
错误后打开证书存储库并根据主题名称找到证书。
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.
}
}
}
在重新发送包含客户端证书的请求之前,您可以确定支持的加密级别是否可以为应用程序接受。 调用WinHttpQueryOption
并指定WINHTTP_OPTION_SECURITY_FLAGS
标志以确定使用的加密级别。
当WinHttp
客户端应用程序向需要SSL
客户端认证的安全HTTP
服务器发送请求时,如果应用程序没有提供客户端证书,WinHttp
将返回ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED
。对于在Windows Server 2008
和Windows Vista
上运行的计算机,WinHttp
使应用程序能够在身份验证质询中检索服务器提供的证书颁发者列表。发放者列表指定由服务器授权以颁发客户端证书的证书授权机构(CA
)的列表。应用程序过滤发放方列表以获取所需的证书。
WinHttp
客户端应用程序在WinHttpSendRequest
或WinHttpReceiveResponse
返回ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED
时检索发行者列表。当返回此错误时,应用程序使用WINHTTP_OPTION_CLIENT_CERT_ISSUER_LIST
选项调用WinHttpQueryOption
。 lpBuffer
参数必须足够大,以包含指向SecPkgContext_IssuerListInfoEx
结构的指针。以下代码示例显示如何检索颁发者列表。
#include
#include
#include
//...
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
结构中的信息cIssuers
和aIssuers
可用于搜索证书,如下面的代码示例所示。有关详细信息,请参阅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;
}
从Windows Server 2008
和Windows Vista
开始,WinHttp API
支持可选的客户端证书。 当服务器请求客户端证书时,WinHttpSendRequest
或WinHttpRecieveResponse
返回ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED
错误。 如果服务器请求证书,但不需要证书,则应用程序可以指定此选项以指示它没有证书。 服务器可以选择另一种认证方案或允许匿名访问服务器。 应用程序在WinHttpSetOption
的lpBuffer
参数中指定WINHTTP_NO_CLIENT_CERT_CONTEXT
宏,如以下代码示例所示。
BOOL fRet = WinHttpSetOption ( hRequest, WINHTTP_OPTION_CLIENT_CERT_CONTEXT, WINHTTP_NO_CLIENT_CERT_CONTEXT,
0);
如果设置了WINHTTP_NO_CLIENT_CERT_CONTEXT
,并且服务器仍需要客户端证书,则它可能会发送403 HTTP
状态代码。有关详细信息,请参阅WINHTTP_OPTION_CLIENT_CERT_ISSUER_LIST
选项。
使用WinHttpRequest
对象的SetClientCertificate
方法选择要通过请求发送到服务器的客户端证书。通过使用SetClientCertificate
方法指定证书选择字符串来选择证书。证书选择字符串由证书位置,证书存储和由反斜杠分隔的主题名称组成。下表列出了此选择字符串的组件。
组件 | 说明 | 可能的值 |
---|---|---|
Location | 确定存储证书的注册表项。 | 可能的值为“LOCAL_MACHINE”,表示证书存储区在下HKEY_LOCAL_MACHINE 和“CURRENT_USER ”,以指示证书存储区处于非模拟状态HKEY_CURRENT_USER。此组件区分大小写。 |
Certificate store | 指示包含相关证书的证书存储的名称。 | 典型的证书存储区是“MY”,“Root”和“TrustedPeople”。 此组件区分大小写。 |
Subject name | 题名标识指定证书存储区中的证书。 选择包含为此组件指定的字符串的第一个证书. | 主题名称可以是任何字符串。 空白字符串表示应使用证书存储库中的第一个证书。 此组件不区分大小写。 |
说明:
证书存储库名称和位置是可选组件。 但是,如果指定证书存储,则还必须指定该证书存储的位置。 默认位置为CURRENT_USER
,默认证书存储区为“MY
”。
以下代码示例显示如何指定应在HKEY_LOCAL_MACHINE
下的注册表中的“个人”证书存储区中选择主题为“我的中间层证书”的证书。
HttpReq.SetClientCertificate("LOCAL_MACHINE\Personal\My Middle-Tier Certificate")
注意在某些语言中,反斜杠是转义字符。 请记住修改证书选择字符串以解决此问题。 例如,在Microsoft JScript中,使用两个相邻的反斜杠而不是一个。
如果未指定证书,并且HTTPS
服务器需要客户端证书,则WinHTTP
会选择默认证书存储库中的第一个证书。如果不存在证书,则会出现错误。如果未接受证书,则服务器返回403
状态代码以指示无法满足请求。然后,您可以使用SetClientCertificate
选择更适当的证书并重新发送请求。