65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 Wininet 通过 SSL 连接到 HTTPS 服务器,发送客户端证书并读取响应

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.71/5 (30投票s)

2003 年 4 月 3 日

2分钟阅读

viewsIcon

510535

downloadIcon

8684

一个示例类,演示如何连接到 HTTPS 服务器,该类发送所需的客户端证书并验证用户。

引言

最近我需要与 Visa 服务器建立安全套接字层 (SSL) 连接,并发送我们签名的客户端证书,以便授权我们的 MPI 应用程序。 我搜索了许多文章,但只找到了一些关于该主题的文章。 我从不同的文章和 MSDN 中收集了部分解决方案,并实现了一个简单的类,该类以编程方式执行此操作。

InternetErrorDlg API 适用于某些目的,包括发送客户端证书,模拟向用户显示的选择对话框。 但在许多情况下,程序员可能需要在没有用户界面的情况下进行身份验证(即,用户界面需要单击确定 J.. 如果我们希望我们的程序自动执行某些操作,这对我们来说可能没有用)。 这可以通过 InternetSetOption()INTERNET_OPTION_CLIENT_CERT_CONTEXT 标志来完成。 不要忘记,此选项仅适用于 Internet Explorer 5.01 或更高版本(如 MSDN 所写)。

INTERNET_OPTION_CLIENT_CERT_CONTEXT 标志未包含在 VC6.0 默认标头中。 如果您已安装平台 SDK,则没有问题,请在 sdk/include 目录中包含 wininet 标头,否则您可以手动定义它;

#define INTERNET_OPTION_CLIENT_CERT_CONTEXT 84

如果您没有旧的 wininet.dll 版本,这应该可以。

对于不熟悉 wininet、SSL 或证书的读者

我不会告诉你 wininet 函数的作用以及它们是如何使用的,也不会告诉你关于证书的信息。 这些都是通用主题,可以从许多资源(例如 MSDN)中收集大量信息。 如果您给我发送电子邮件,我会尝试回答这些问题。

嗯,流程很简单。 首先,我们连接到 HTTPS 服务器并发送 HTTPS 请求。 如果服务器要求提供签名的客户端证书,我们会打开并搜索系统存储以查找我们需要的证书上下文。 然后重新发送我们的请求,但在附加证书上下文之后。 如果服务器满意,我们就通过了身份验证。

  • ConnectToHttpsServer() 总结了连接的流程。 这是初始位置。
  • SendHttpsRequest() 发送请求。 之后,如果服务器需要客户端证书,我们会在系统存储中搜索它。 如果我们找到了它,InternetSetOption() 会将上下文附加到连接。 然后我们再次尝试 SendHttpsRequest()

该类的一个示例用法可以是这样的

CSslConnection inetSec;
string sAgentName("My Firm"); 
string sServerName("207.219.70.31");//Can be any https server address 
string sUserName("");//if required 
string sPass("");//if required 
string sObjectName("/xxx.asp");//there should be an object to send a verb 
string sOrganizationUnitName("3-D Secure Compliance TestFacility"); 
string strVerb = "POST";//I chose POST verb. That’s usually done 

inetSec.SetAgentName(sAgentName); 

inetSec.SetCertStoreType(certStoreMY); 
         //The stores provided by the system 
         // are: MY, ROOT, SPC and CA 

inetSec.SetObjectName(sObjectName);      

inetSec.SetOrganizationName(sOrganizationUnitName); 

inetSec.SetPort(9660);//443 is the default HTTPS port 
inetSec.SetServerName(sServerName); 

inetSec.SetRequestID(0); 

if (!inetSec.ConnectToHttpsServer(strVerb)) { 

    cout << inetSec.GetLastErrorString()  << " Code: " 
       << inetSec.GetLastErrorCode(); << endl; 
    return 0; 
} 

if (!inetSec.SendHttpsRequest()) 
{ 
    cout << inetSec.GetLastErrorString() << " Code: " 
        << inetSec.GetLastErrorCode(); << endl
    return 0; 
} 

string response = inetSec.GetRequestResult(); 
cout << response.c_str() << endl;

带“组织名称”注释的函数和变量完全是示例。 我选择使用证书中“颁发者字段的 O 值”,这是我的搜索条件。 您可能希望通过不同的字段执行存储搜索。 因为证书中有很多字段,并且可以通过这些字段中的任何一个执行上下文搜索。

您可能会添加自己的函数而不是使用 FindCertWithOUNITName() 函数。 如果你这样做,只需更改调用此函数的代码(仅在一个地方),并提供一些适合您的证书搜索条件的变量和访问器。

© . All rights reserved.