SSL 与 WinHTTP
我用 winhttp 创建了一个简单的 Web 服务器,它只有一个客户端,一个网站。在我启用 SSL 之前它工作得很好。我没有收到任何错误,并且一切似乎都正常,但网站在尝试连接时收到错误代码 104,并且我在服务器中没有看到任何活动。该网站是由专业人士创建的,因此该错误很可能出现在我的代码中。由于我对网络编程一无所知,所以我用 C++ 创建了一个测试客户端来调试问题。 没有 SSL,它可以连接到服务器。但是当我尝试设置客户端证书时,它失败了。所以现在我有两个问题。
这是服务器代码的相关部分:
ULONG ret = NO_ERROR;
HRESULT hr = S_OK;
HTTPAPI_VERSION ver = HTTPAPI_VERSION_1;
ret = HttpInitialize(ver,HTTP_INITIALIZE_SERVER|HTTP_INITIALIZE_CONFIG,NULL);
if(ret!=NO_ERROR)
return;
SOCKADDR_IN sa;
HTTP_SERVICE_CONFIG_SSL_SET scssl;
memset(&sa,0,sizeof(sa));
sa.sin_addr.S_un.S_addr = 0;
sa.sin_family = AF_INET;
sa.sin_port = 443;
scssl.KeyDesc.pIpPort = (SOCKADDR*)&sa;
scssl.ParamDesc.AppId = AppID;
scssl.ParamDesc.DefaultCertCheckMode = 0;
scssl.ParamDesc.DefaultFlags = HTTP_SERVICE_CONFIG_SSL_FLAG_NEGOTIATE_CLIENT_CERT;
scssl.ParamDesc.DefaultRevocationFreshnessTime = 0;
scssl.ParamDesc.DefaultRevocationUrlRetrievalTimeout = 10000;
scssl.ParamDesc.pSslCertStoreName = L"MY";
scssl.ParamDesc.pDefaultSslCtlIdentifier = NULL;
scssl.ParamDesc.pDefaultSslCtlStoreName = NULL;
scssl.ParamDesc.pSslHash = (void*)ServerCertHash;
scssl.ParamDesc.SslHashLength = ARRAYSIZE(ServerCertHash);
ret = HttpSetServiceConfiguration(NULL,HttpServiceConfigSSLCertInfo,&scssl,sizeof(scssl),NULL);
if(ret!=NO_ERROR)
return;
ret = HttpCreateHttpHandle(&m_RequestQueue,0);
if(ret!=NO_ERROR)
return;
wcsncpy(m_Url,L"https://+:443/WebShop/",256);
ret = HttpAddUrl(m_RequestQueue,m_Url,NULL);
if(ret!=NO_ERROR)
return;
文档没有说明当 pDefaultSslCtlIdentifier 为 NULL 时会发生什么,但我想这意味着服务器接受任何受信任的证书。我尝试设置我需要的,但 HttpSetServiceConfiguration 调用失败。
在客户端,WinHttpSetOption 失败,并且 GetLastError() 返回 6。有时我会在调试输出中看到访问冲突错误。报告的地址接近请求的值。 客户端代码:
char* types[] = {"application/soap+xml",NULL};
hint = InternetOpen("WebTestClient",INTERNET_OPEN_TYPE_DIRECT,NULL,NULL,0);
if(hint!=NULL)
{
InternetSetStatusCallback(hint,&HTTPStatusCallbackFunc);
hsession = InternetConnect(hint,"127.0.0.1",443,NULL,NULL,3,0,NULL);
if(hsession!=NULL)
{
hreqest = HttpOpenRequest(hsession,"GET","/WebShop/","HTTP/1.1",NULL,(LPCSTR*)types,INTERNET_FLAG_SECURE|INTERNET_FLAG_IGNORE_CERT_CN_INVALID,NULL);
if(hreqest!=NULL)
{
if(SetupSSL(hreqest))
{
if(HttpSendRequest(hreqest,NULL,0,inbuf,inlen))
{
...
}
}
InternetCloseHandle(hreqest);
}
InternetCloseHandle(hsession);
}
InternetCloseHandle(hint);
}
bool SetupSSL(HINTERNET request)
{
HCERTSTORE store = CertOpenSystemStore(NULL,"MY");
DWORD ret = 0;
bool ok = false;
if(store==NULL)
return false;
PCCERT_CONTEXT context = CertFindCertificateInStore(store,X509_ASN_ENCODING,0,CERT_FIND_SUBJECT_STR,L"WebClient",NULL);
if(context!=NULL)
{
// this fails
ok = WinHttpSetOption(request,WINHTTP_OPTION_CLIENT_CERT_CONTEXT,(LPVOID)context,sizeof(CERT_CONTEXT))==TRUE;
if(!ok)
ret = GetLastError(); // returns 6
CertFreeCertificateContext(context);
}
CertCloseStore(store,0);
return ok;
};
我用这个脚本创建了证书:
makecert -r -pe -n "CN=BeeLSoft" -ss CA -sr LocalMachine -a sha1 -sky signature -cy authority -sv Root.pvk Root.cer
makecert -pe -n "CN=beelsoft.dyndns.org" -a sha1 -sky exchange -eku 1.3.6.1.5.5.7.3.1 -ic Root.cer -iv Root.pvk -sp "Microsoft RSA SChannel Cryptographic Provider" -sy 12 -sv Server.pvk Server.cer
pvk2pfx -pvk Server.pvk -spc Server.cer -pfx Server.pfx
makecert -pe -n "CN=WebClient" -a sha1 -sky exchange -eku 1.3.6.1.5.5.7.3.1 -ic Root.cer -iv Root.pvk -sp "Microsoft RSA SChannel Cryptographic Provider" -sy 12 -sv Client.pvk Client.cer
pvk2pfx -pvk Client.pvk -spc Client.cer -pfx Client.pfx
我不明白这些东西,但它应该创建适合 SSL 的证书。
如果有关于这个主题的好的教程,那就太好了。到目前为止我发现的内容足以编写上面的代码。
I have created a simple web server with winhttp that has only one client, a website. It worked perfectly until I enabled SSL. I don't get any errors, and everything seems to work, but the website gets error code 104 when it tries to connect, and I don't see any activity in the server. The website was created by professionals, so the bug is very likely to be in my code. Since I know nothing about web programming, I created a test client in c++, to debug the problem.
Without SSL it can connect to the server. But when I try to set the clients certificate, it fails. So now I have two problems.
Here is the relevant part of the server code :
ULONG ret = NO_ERROR;
HRESULT hr = S_OK;
HTTPAPI_VERSION ver = HTTPAPI_VERSION_1;
ret = HttpInitialize(ver,HTTP_INITIALIZE_SERVER|HTTP_INITIALIZE_CONFIG,NULL);
if(ret!=NO_ERROR)
return;
SOCKADDR_IN sa;
HTTP_SERVICE_CONFIG_SSL_SET scssl;
memset(&sa,0,sizeof(sa));
sa.sin_addr.S_un.S_addr = 0;
sa.sin_family = AF_INET;
sa.sin_port = 443;
scssl.KeyDesc.pIpPort = (SOCKADDR*)&sa;
scssl.ParamDesc.AppId = AppID;
scssl.ParamDesc.DefaultCertCheckMode = 0;
scssl.ParamDesc.DefaultFlags = HTTP_SERVICE_CONFIG_SSL_FLAG_NEGOTIATE_CLIENT_CERT;
scssl.ParamDesc.DefaultRevocationFreshnessTime = 0;
scssl.ParamDesc.DefaultRevocationUrlRetrievalTimeout = 10000;
scssl.ParamDesc.pSslCertStoreName = L"MY";
scssl.ParamDesc.pDefaultSslCtlIdentifier = NULL;
scssl.ParamDesc.pDefaultSslCtlStoreName = NULL;
scssl.ParamDesc.pSslHash = (void*)ServerCertHash;
scssl.ParamDesc.SslHashLength = ARRAYSIZE(ServerCertHash);
ret = HttpSetServiceConfiguration(NULL,HttpServiceConfigSSLCertInfo,&scssl,sizeof(scssl),NULL);
if(ret!=NO_ERROR)
return;
ret = HttpCreateHttpHandle(&m_RequestQueue,0);
if(ret!=NO_ERROR)
return;
wcsncpy(m_Url,L"https://+:443/WebShop/",256);
ret = HttpAddUrl(m_RequestQueue,m_Url,NULL);
if(ret!=NO_ERROR)
return;
The documentation says nothing about what happens, when pDefaultSslCtlIdentifier is NULL, but I guess it means the server accepts any trusted certificate. I tried to set the one I need, but than the HttpSetServiceConfiguration call failed.
On the client side WinHttpSetOption fails, and GetLastError() returns 6. Sometimes I see access violation errors in the debug output. The reported addresses are close to the value of request.
The client code :
char* types[] = {"application/soap+xml",NULL};
hint = InternetOpen("WebTestClient",INTERNET_OPEN_TYPE_DIRECT,NULL,NULL,0);
if(hint!=NULL)
{
InternetSetStatusCallback(hint,&HTTPStatusCallbackFunc);
hsession = InternetConnect(hint,"127.0.0.1",443,NULL,NULL,3,0,NULL);
if(hsession!=NULL)
{
hreqest = HttpOpenRequest(hsession,"GET","/WebShop/","HTTP/1.1",NULL,(LPCSTR*)types,INTERNET_FLAG_SECURE|INTERNET_FLAG_IGNORE_CERT_CN_INVALID,NULL);
if(hreqest!=NULL)
{
if(SetupSSL(hreqest))
{
if(HttpSendRequest(hreqest,NULL,0,inbuf,inlen))
{
...
}
}
InternetCloseHandle(hreqest);
}
InternetCloseHandle(hsession);
}
InternetCloseHandle(hint);
}
bool SetupSSL(HINTERNET request)
{
HCERTSTORE store = CertOpenSystemStore(NULL,"MY");
DWORD ret = 0;
bool ok = false;
if(store==NULL)
return false;
PCCERT_CONTEXT context = CertFindCertificateInStore(store,X509_ASN_ENCODING,0,CERT_FIND_SUBJECT_STR,L"WebClient",NULL);
if(context!=NULL)
{
// this fails
ok = WinHttpSetOption(request,WINHTTP_OPTION_CLIENT_CERT_CONTEXT,(LPVOID)context,sizeof(CERT_CONTEXT))==TRUE;
if(!ok)
ret = GetLastError(); // returns 6
CertFreeCertificateContext(context);
}
CertCloseStore(store,0);
return ok;
};
I created the certificates with this script:
makecert -r -pe -n "CN=BeeLSoft" -ss CA -sr LocalMachine -a sha1 -sky signature -cy authority -sv Root.pvk Root.cer
makecert -pe -n "CN=beelsoft.dyndns.org" -a sha1 -sky exchange -eku 1.3.6.1.5.5.7.3.1 -ic Root.cer -iv Root.pvk -sp "Microsoft RSA SChannel Cryptographic Provider" -sy 12 -sv Server.pvk Server.cer
pvk2pfx -pvk Server.pvk -spc Server.cer -pfx Server.pfx
makecert -pe -n "CN=WebClient" -a sha1 -sky exchange -eku 1.3.6.1.5.5.7.3.1 -ic Root.cer -iv Root.pvk -sp "Microsoft RSA SChannel Cryptographic Provider" -sy 12 -sv Client.pvk Client.cer
pvk2pfx -pvk Client.pvk -spc Client.cer -pfx Client.pfx
I don't understand any of that stuff, but its supposed to create certificates suitable for SSL.
If there is a good tutorial on this subject, that would be nice. What I found so far was just enough to write the code above.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论