winhttp を使用して、Web サイトというクライアントが 1 つしかない単純な Web サーバーを作成しました。SSLを有効にするまでは完全に機能しました。エラーは発生せず、すべてが機能しているように見えますが、Web サイトが接続しようとするとエラー コード 104 が表示され、サーバーにアクティビティが表示されません。ウェブサイトは専門家によって作成されたものであるため、バグは私のコードにある可能性が非常に高い. 私は Web プログラミングについて何も知らないので、問題をデバッグするために C++ でテスト クライアントを作成しました。SSLなしでサーバーに接続できます。しかし、クライアント証明書を設定しようとすると失敗します。だから今、私は2つの問題を抱えています。
サーバーコードの関連部分は次のとおりです。
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 を返します。デバッグ出力にアクセス違反エラーが表示されることがあります。報告されたアドレスは request の値に近いです。クライアントコード:
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に適した証明書を作成することになっています。
このテーマに関する優れたチュートリアルがあれば、それは素晴らしいことです。これまでに見つけたものは、上記のコードを書くのに十分でした.