SSL 上の TCP 通信を行うサーバとクライアントの実装方法
この資料では SSP を使って、SSL 上の TCP 通信を行う方法を示します。
SSL のコードを書くことがやや複雑なことに加えて、SSL を利用するにはサーバー証明書などが必要になります。 ネットワークやサーバーの管理をしている人はわかるでしょうが、普通はあまり馴染みがないものだと思います。
とはいえ、「SSL とは何か?」 とか 「サーバー証明書とは何か?」 とか、そういうテーマについてはまた別の機会に書くことにして、 ここではプログラミングの詳細のみにとどめておきます。めちゃくちゃ長くなってしまいますからね。
SSL 固有のトピックとしては、「サーバー証明書が必要」 ということだけ覚えておいてください。 そしてサーバー証明書はコモンネーム (Common Name, CN) というもので識別されます。 この資料のテストを行うためのサーバー証明書は次のリンクからダウンロードして、インストールして使ってください。
コードの多くの部分は NTLM を用いたサーバーとクライアントの実装と同様です。異なるセキュリティパッケージに対して、 同様のコードが使用できることがわかると思います。
ちなみに、この SSPI を用いた SSL の実装については、ちょっと苦い思い出があります。
私がまだこうした実装方法の知識が無かった頃、仕事で急にその知識が必要になりました。
そこであちこち資料を探したのですがなかなか良い資料が見つかりません。やっと
Programming Server-Side Applications for Microsoft Windows 2000
に SSL の実装の章があることがわかりました。この本はなんとか手に入れたのですが、まだ当時は英語の書籍を読むのが今ほどは得意ではなかったので、
急いでいたこともあり分厚い洋書を読むのに恐れをなして翻訳書を探しました。
翻訳書を買って読み進めていたら、なんと、あろうことか和書には大切な SSL の実装方法だけがポカッと抜けているではありませんか...
原書に解説があるのに翻訳書には抜けている...
せっかく翻訳書を買ったのに勝手に説明を抜かされては面白くないので、その出版社に問い合わせてみました。
すると結局、
「何も言わずに割愛したのは悪かったけど、大事な部分かどうか判断する権利はこちらにある」 と一蹴されてしまいました。
こちらが仕事で必要に迫られてそれを読もうとしていたのに、大事な部分ではないとは何事だ! と少々頭にきてしまいました。
きっと翻訳書だけを読んだ人には、そこが割愛されていたことには気付かなかったでしょうけど...
それ以来、私はなるべく翻訳書ではなく原著で読むようにしています。(ぶっちゃけ、たいていの翻訳書は訳語がバラバラで何のことを言っているのかわからないことが多いですし)
では、さっそくコードを書いてみましょう。
ヘルパー関数
クライアントとサーバー両方で使用するヘルパー関数はこちらです。
以下のコードを helpers.h として保存してください。
#pragma once #include <winsock2.h> void PrintHexDump(DWORD length, PBYTE buffer); BOOL ReceiveData(SOCKET hSocket, PBYTE pbBuff, ULONG ulSize); BOOL SendData(SOCKET hSocket, PBYTE pbBuff, ULONG ulSize);
以下のコードを helpers.cpp として保存してください。
#include "helpers.h" #include <stdio.h> /////////////////////////////////////////////////////////////////////////////// void PrintHexDump(DWORD length, PBYTE buffer) { DWORD i,count,index; CHAR rgbDigits[]="0123456789abcdef"; CHAR rgbLine[100]; char cbLine; for(index = 0; length; length -= count, buffer += count, index += count) { count = (length > 16) ? 16:length; sprintf(rgbLine, "%4.4x ",index); cbLine = 6; for(i=0;i<count;i++) { rgbLine[cbLine++] = rgbDigits[buffer[i] >> 4]; rgbLine[cbLine++] = rgbDigits[buffer[i] & 0x0f]; if(i == 7) { rgbLine[cbLine++] = ':'; } else { rgbLine[cbLine++] = ' '; } } for(; i < 16; i++) { rgbLine[cbLine++] = ' '; rgbLine[cbLine++] = ' '; rgbLine[cbLine++] = ' '; } rgbLine[cbLine++] = ' '; for(i = 0; i < count; i++) { if(buffer[i] < 32 || buffer[i] > 126) { rgbLine[cbLine++] = '.'; } else { rgbLine[cbLine++] = buffer[i]; } } rgbLine[cbLine++] = 0; printf("%s\n", rgbLine); } } // end PrintHexDump /////////////////////////////////////////////////////////////////////////////// BOOL ReceiveData(SOCKET hSocket, PBYTE pbBuff, ULONG ulSize) { BYTE bTemp[1024]; PBYTE pBuffPointer = pbBuff; ULONG ByteRead = 0; ULONG ulTotalSize = ulSize; do { ::ZeroMemory(bTemp, 1024); ByteRead = recv(hSocket, (char*)bTemp, ulTotalSize > 1024 ? 1024 : ulTotalSize, 0); if(SOCKET_ERROR == ByteRead) { printf("Error in receiving data.\n"); return FALSE; } memcpy(pBuffPointer, bTemp, ByteRead); pBuffPointer += ByteRead; ulTotalSize -= ByteRead; } while(ulTotalSize > 0); return TRUE; } /////////////////////////////////////////////////////////////////////////////// BOOL SendData(SOCKET hSocket, PBYTE pbBuff, ULONG ulSize) { PBYTE pBuffPointer = pbBuff; ULONG ByteSent = 0; ULONG ulTotalSize = ulSize; do { ByteSent = send(hSocket, (char*)pBuffPointer, ulTotalSize, 0); if(SOCKET_ERROR == ByteSent) { printf("Error in sending data.\n"); return FALSE; } pBuffPointer += ByteSent; ulTotalSize -= ByteSent; } while(ulTotalSize > 0); return TRUE; }
SSL サーバーの実装
次のコードを sslsrv.cpp として保存してください。
次のコードではクライアントからの接続を 127.0.0.1 の TCP ポート 3000 番で待ちます。 次に証明書ストアから CN が "keisukeo-hp" である証明書を探し、見つかればそれをサーバー証明書として用いて処理を続行します。 証明書がインストールされていないとここで処理が終了しますから気をつけてください。 ご自身の既に持っている証明書を使うのでしたら CN を書き換えてください。
次に SChannel 情報 (SCHANNEL_CRED) を作成し、それと証明書情報を関連付け AcquireCredentialsHandle に渡します。
ここまでくれば NTLM の場合とほぼ同様です。
ちなみに、クライアントとの このプログラムの固有の 「プロトコル」 として、先に送信するデータ量を 4 バイトで送信し、 指定されたデータ量を受け取ったら、送受信が切り替わるという風にしています。
つまり、次のようなやり取りをするということです。
クライアント→サーバー: 10
クライアント→サーバー: (10 バイトのデータ)
クライアント←サーバー: 5
クライアント←サーバー: (5 バイトのデータ)
...
#include <winsock2.h> #include <security.h> #include <schannel.h> #include <sspi.h> #include <wincrypt.h> #include <stdio.h> #include "helpers.h" #define szMESSAGE_FROM_SERVER "This message was sent from sslsrv.exe." /////////////////////////////////////////////////////////////////////////////// void main() { // // ネットワーク接続の準備 // WSADATA wsaData; WORD wRet = WSAStartup(WINSOCK_VERSION, &wsaData); if (wRet) { printf ("WSAStartup failed. %d\n", wRet); return; } INT nRet; // リスニングソケットを作成 SOCKET hListenSocket = socket(AF_INET, SOCK_STREAM, 0); if(hListenSocket == INVALID_SOCKET) { printf("socket Failed.\n"); return; } ULONG ulAddress = inet_addr("127.0.0.1"); SOCKADDR_IN name; name.sin_family = AF_INET; name.sin_port = htons(3000); memmove(&name.sin_addr, &ulAddress, sizeof(ULONG)); // 名前をつける... nRet = bind(hListenSocket, (SOCKADDR*)&name, sizeof(SOCKADDR_IN)); if(nRet) { printf("bind Failed.\n"); return; } // リスニング nRet = listen(hListenSocket, 2); if(nRet) { printf("listen Failed.\n"); return; } printf("Listening...\n"); // クライアントの接続を待つ SOCKADDR_IN client_addr; INT addr_len = sizeof(client_addr); SOCKET hClientSocket = accept(hListenSocket, (SOCKADDR*)&client_addr, &addr_len); if(hClientSocket == INVALID_SOCKET) { printf("Last error = %u\n", WSAGetLastError()); return; } printf("Accepting a client.\n"); closesocket(hListenSocket); // リスニング終わり // // 接続は確立された // // 証明書の情報を取得 HCERTSTORE hCertStore = CertOpenStore( CERT_STORE_PROV_SYSTEM_A, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_SYSTEM_STORE_LOCAL_MACHINE, "MY"); if(NULL==hCertStore) { printf("CertOpenSotore Failed.\n"); return; } PSTR pszCN = "keisukeo-hp"; // CN は証明書にあわせ変えてください! CERT_RDN_ATTR certRDNAttr[1]; certRDNAttr[0].pszObjId = szOID_COMMON_NAME; certRDNAttr[0].dwValueType = CERT_RDN_PRINTABLE_STRING; certRDNAttr[0].Value.pbData = (PBYTE) pszCN; certRDNAttr[0].Value.cbData = lstrlen(pszCN); CERT_RDN certRDN = {1, certRDNAttr}; PCCERT_CONTEXT pCertContext = CertFindCertificateInStore( hCertStore, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_FIND_SUBJECT_ATTR, &certRDN, NULL); if(NULL==pCertContext) { printf("CertFindCertificateInStore Failed.\n"); return; } // SChannel 情報 SCHANNEL_CRED sslCred = {0}; sslCred.dwVersion = SCHANNEL_CRED_VERSION; sslCred.cCreds = 1; sslCred.paCred = &pCertContext; sslCred.grbitEnabledProtocols = SP_PROT_SSL3; // SSPI のクレデンシャルハンドル取得 CredHandle hCredential; TimeStamp tsExpiry; SECURITY_STATUS ss; ss = AcquireCredentialsHandle( NULL, UNISP_NAME, SECPKG_CRED_BOTH, NULL, &sslCred, NULL, NULL, &hCredential, &tsExpiry); if(ss != SEC_E_OK) { printf("AcquireCredentialsHandle Failed.\n"); return; } // // クライアントとのハンドシェイク // SecBuffer secBufIn[1], secBufOut[1]; SecBufferDesc secBufDescIn, secBufDescOut; BOOL bFirst = TRUE; CtxtHandle hContext; PBYTE pbBuff = NULL; ULONG ulSize = 0; ss = SEC_I_CONTINUE_NEEDED; while(ss == SEC_I_CONTINUE_NEEDED) { // データ転送量 if (!ReceiveData(hClientSocket, (PBYTE)&ulSize, sizeof(ulSize))) { return; } // ulSize 分のサイズのデータを受け取る pbBuff = (PBYTE) calloc(ulSize, 1); if(NULL == pbBuff) { printf("calloc Failed.\n"); return; } if (!ReceiveData(hClientSocket, pbBuff, ulSize)) { return; } PrintHexDump(ulSize, pbBuff); // ブロブの受け入れ secBufIn[0].BufferType = SECBUFFER_TOKEN; secBufIn[0].cbBuffer = ulSize; secBufIn[0].pvBuffer = pbBuff; secBufDescIn.cBuffers = 1; secBufDescIn.ulVersion = SECBUFFER_VERSION; secBufDescIn.pBuffers = secBufIn; secBufOut[0].BufferType = SECBUFFER_TOKEN; secBufOut[0].cbBuffer = 0; secBufOut[0].pvBuffer = NULL; secBufDescOut.cBuffers = 1; secBufDescOut.ulVersion = SECBUFFER_VERSION; secBufDescOut.pBuffers = secBufOut; ULONG ulAttr = 0; ss = AcceptSecurityContext(&hCredential, bFirst ? NULL: &hContext, &secBufDescIn, ulAttr | ASC_REQ_ALLOCATE_MEMORY , SECURITY_NETWORK_DREP, &hContext, &secBufDescOut, &ulAttr , NULL); printf("AcceptSecurityContext returned %x\n", ss); bFirst = FALSE; if(secBufOut[0].cbBuffer != 0) { ULONG ulOutSize = secBufOut[0].cbBuffer; // 次に送るサイズのデータを後で送る SendData(hClientSocket, (PBYTE) &ulOutSize, sizeof(ulOutSize)); // データ送信 SendData(hClientSocket, (PBYTE) secBufOut[0].pvBuffer, secBufOut[0].cbBuffer); // ASC_REQ_ALLOCATE_MEMORY フラグを立てていたので... FreeContextBuffer(secBufOut[0].pvBuffer); } if(pbBuff) { free(pbBuff); pbBuff = NULL; } } // while SEC_I_CONTINUE_NEEDED // // メッセージを暗号化して送信する // SecPkgContext_StreamSizes PkgSize; ss = QueryContextAttributes( &hContext, SECPKG_ATTR_STREAM_SIZES, &PkgSize); if(SEC_E_OK != ss) { printf("QueryContextAttributes Failed.\n"); return; } ULONG ulMsgSize = strlen(szMESSAGE_FROM_SERVER); PBYTE pIOBuf = (PBYTE) calloc( PkgSize.cbHeader + PkgSize.cbMaximumMessage + PkgSize.cbTrailer, 1); if(!pIOBuf) { printf("calloc Failed.\n"); return; } ::CopyMemory(pIOBuf + PkgSize.cbHeader, szMESSAGE_FROM_SERVER, ulMsgSize); SecBuffer secBufMsg[3]; SecBufferDesc secBufDescMsg; // シグネチャバッファ secBufMsg[0].BufferType = SECBUFFER_STREAM_HEADER; secBufMsg[0].cbBuffer = PkgSize.cbHeader; secBufMsg[0].pvBuffer = pIOBuf; // メッセージバッファ secBufMsg[1].BufferType = SECBUFFER_DATA; secBufMsg[1].cbBuffer = ulMsgSize; secBufMsg[1].pvBuffer = pIOBuf + PkgSize.cbHeader; // パディングバッファ secBufMsg[2].BufferType = SECBUFFER_STREAM_TRAILER; secBufMsg[2].cbBuffer = PkgSize.cbTrailer; secBufMsg[2].pvBuffer = pIOBuf + PkgSize.cbHeader + ulMsgSize; // バッファディスクリプション secBufDescMsg.cBuffers = 3; secBufDescMsg.ulVersion = SECBUFFER_VERSION; secBufDescMsg.pBuffers = secBufMsg; ss = EncryptMessage(&hContext, 0, &secBufDescMsg, 0); if(SEC_E_OK != ss) { printf("EncryptMessage Failed.\n"); return; } // メッセージストリームの送信 ulSize = secBufMsg[0].cbBuffer + secBufMsg[1].cbBuffer + secBufMsg[2].cbBuffer; SendData(hClientSocket, (PBYTE)&ulSize, sizeof(ulSize)); SendData(hClientSocket, pIOBuf, ulSize); printf("Message Stream:\n"); PrintHexDump(ulSize, pIOBuf); // 割り当てメモリの解放 if(pIOBuf) { free(pIOBuf); pIOBuf = NULL; } // // SSP のクリーンアップ // ss = DeleteSecurityContext(&hContext); if(SEC_E_OK != ss) { printf("DeleteSecurityContext Failed.\n"); return; } ss = FreeCredentialsHandle(&hCredential); if(SEC_E_OK != ss) { printf("FreeCredentialsHandle Failed.\n"); return; } // // ソケットを閉じる // shutdown(hClientSocket, SD_SEND); shutdown(hClientSocket ,SD_BOTH); closesocket(hClientSocket); WSACleanup (); }
makefile は次の通りです。
TARGETNAME=sslsrv OUTDIR=.\chk LINK32=link.exe ALL : "$(OUTDIR)\$(TARGETNAME).exe" CPPFLAGS=\ /nologo\ /MT\ /W4\ /Fo"$(OUTDIR)\\"\ /Fd"$(OUTDIR)\\"\ /c\ /Zi\ /DWIN32\ /DSECURITY_WIN32 LINK32_FLAGS=\ Secur32.lib\ Crypt32.lib\ Ws2_32.lib\ /nologo\ /subsystem:console\ /pdb:"$(OUTDIR)\$(TARGETNAME).pdb"\ /machine:I386\ /out:"$(OUTDIR)\$(TARGETNAME).exe"\ /DEBUG LINK32_OBJS= \ "$(OUTDIR)\$(TARGETNAME).obj" "$(OUTDIR)\helpers.obj" "$(OUTDIR)\$(TARGETNAME).exe" : "$(OUTDIR)" $(LINK32_OBJS) $(LINK32) $(LINK32_FLAGS) $(LINK32_OBJS) "$(OUTDIR)" : if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" .c{$(OUTDIR)}.obj: $(CPP) $(CPPFLAGS) $< .cpp{$(OUTDIR)}.obj: $(CPP) $(CPPFLAGS) $<
ヘルパー関数、sslsrv.cpp 及び makefile を同じディレクトリに配置して、 nmake すればサブディレクトリ chk に sslsrv.exe が作成されます。
SSL クライアントの実装
以下のコードを sslclt.cpp として保存します。
#include <winsock2.h> #include <security.h> #include <schannel.h> #include <sspi.h> #include <wincrypt.h> #include <stdio.h> #include "helpers.h" /////////////////////////////////////////////////////////////////////////////// void main() { // // ネットワーク接続の準備 // WSADATA wsaData; WORD wRet = WSAStartup(WINSOCK_VERSION, &wsaData); if (wRet) { printf ("WSAStartup failed. %d\n", wRet); return; } INT nRet; // ソケットの作成 SOCKET hClientSocket = socket(AF_INET, SOCK_STREAM, 0); if(hClientSocket == INVALID_SOCKET) { printf("socket Failed.\n"); return; } printf("Socket has been created.\n"); // サーバーへ接続 ULONG ulAddress = inet_addr("127.0.0.1"); SOCKADDR_IN name; name.sin_family = AF_INET; name.sin_port = htons(3000); memmove(&name.sin_addr, &ulAddress, sizeof(ULONG)); nRet = connect(hClientSocket, (SOCKADDR*) &name, sizeof(SOCKADDR_IN)); if(nRet) { printf("connect Failed. LastError = %u\n", WSAGetLastError()); return; } printf("Successfully connecting to the server.\n"); // // ここまでにネットワーク接続は確立された // // SChannel 情報 SCHANNEL_CRED sslCred = {0}; sslCred.dwVersion = SCHANNEL_CRED_VERSION; sslCred.grbitEnabledProtocols = SP_PROT_SSL3; sslCred.dwFlags = SCH_CRED_NO_DEFAULT_CREDS | SCH_CRED_MANUAL_CRED_VALIDATION; // SSPI クレデンシャルの取得 CredHandle hCredential; TimeStamp tsExpiry; SECURITY_STATUS ss; ss = AcquireCredentialsHandle( NULL, UNISP_NAME, SECPKG_CRED_OUTBOUND, NULL, &sslCred, NULL, NULL, &hCredential, &tsExpiry); if(ss != SEC_E_OK) { printf("AcquireCredentialsHandle Failed.\n"); return; } // サーバーとのハンドシェイク SecBuffer secBufIn[1], secBufOut[1]; SecBufferDesc secBufDescIn, secBufDescOut; BOOL bFirst = TRUE; CtxtHandle hContext; PBYTE pbBuff = NULL; ULONG ulSize = 0; ss = SEC_I_CONTINUE_NEEDED; while(ss == SEC_I_CONTINUE_NEEDED) { ULONG ulAttr = 0; if(bFirst) { secBufDescIn.cBuffers = 0; secBufDescIn.ulVersion = SECBUFFER_VERSION; secBufDescIn.pBuffers = NULL; } else { ULONG ulInSize; if (!ReceiveData(hClientSocket, (PBYTE)&ulInSize, sizeof(ulInSize))) { return; } pbBuff = (PBYTE) calloc(ulInSize, 1); if(NULL == pbBuff) { printf("calloc Failed.\n"); return; } if (!ReceiveData(hClientSocket, pbBuff, ulInSize)) { return; } PrintHexDump(ulInSize, pbBuff); secBufIn[0].BufferType = SECBUFFER_TOKEN; secBufIn[0].cbBuffer = ulInSize; secBufIn[0].pvBuffer = pbBuff; secBufDescIn.cBuffers = 1; secBufDescIn.ulVersion = SECBUFFER_VERSION; secBufDescIn.pBuffers = secBufIn; } secBufOut[0].BufferType = SECBUFFER_TOKEN; secBufOut[0].cbBuffer = 0; secBufOut[0].pvBuffer = NULL; secBufDescOut.cBuffers = 1; secBufDescOut.ulVersion = SECBUFFER_VERSION; secBufDescOut.pBuffers = secBufOut; ss = InitializeSecurityContext(&hCredential, bFirst ? NULL: &hContext, "localhost", ulAttr | ISC_REQ_ALLOCATE_MEMORY, 0, SECURITY_NETWORK_DREP, &secBufDescIn, 0, &hContext, &secBufDescOut, &ulAttr, NULL); if ( FAILED(ss) ) { printf ("InitializeSecurityContext failed. %x\n", ss); return; } printf("InitializeSecurityContext returned %x\n", ss); // 0x90312 = SEC_I_CONTINUE_NEEDED bFirst = FALSE; if(secBufOut[0].cbBuffer != 0) { ULONG ulOutSize = secBufOut[0].cbBuffer; // 次のサイズのデータを送信する SendData(hClientSocket, (PBYTE) &ulOutSize, sizeof(ulOutSize)); // データを送信する SendData(hClientSocket, (PBYTE) secBufOut[0].pvBuffer, secBufOut[0].cbBuffer); // ASC_REQ_ALLOCATE_MEMORY フラグを立てていたので... FreeContextBuffer(secBufOut[0].pvBuffer); } if(pbBuff) { free(pbBuff); pbBuff = NULL; } } // while SEC_I_CONTINUE_NEEDED // // サーバーからメッセージを受け取り復号化する // PBYTE pIOBuf; SecBuffer secBufMsg[4] = {0}; SecBufferDesc secBufDescMsg; // メッセージストリームの受け取り ReceiveData(hClientSocket, (PBYTE)&ulSize, sizeof(ulSize)); pIOBuf = (PBYTE) calloc(ulSize, 1); if(!pIOBuf) { printf("calloc Failed.\n"); return; } ReceiveData(hClientSocket, pIOBuf, ulSize); printf("Raw Message Stream:\n"); PrintHexDump(ulSize, pIOBuf); secBufMsg[0].BufferType = SECBUFFER_DATA; secBufMsg[0].cbBuffer = ulSize; secBufMsg[0].pvBuffer = pIOBuf; secBufMsg[1].BufferType = SECBUFFER_EMPTY; secBufMsg[2].BufferType = SECBUFFER_EMPTY; secBufMsg[3].BufferType = SECBUFFER_EMPTY; // バッファディスクリプション secBufDescMsg.cBuffers = 4; secBufDescMsg.ulVersion = SECBUFFER_VERSION; secBufDescMsg.pBuffers = secBufMsg; ULONG lQual = 0; ss = DecryptMessage(&hContext, &secBufDescMsg, 0, &lQual); if(SEC_E_OK != ss) { if(SEC_E_INCOMPLETE_MESSAGE == ss) { printf("DecryptMessage Failed. SEC_E_INCOMPLETE_MESSAGE.\n"); } else if(SEC_E_INVALID_HANDLE == ss) { printf("DecryptMessage Failed. SEC_E_INVALID_HANDLE.\n"); } else if(SEC_E_INVALID_TOKEN == ss) { printf("DecryptMessage Failed. SEC_E_INVALID_TOKEN.\n"); } else { printf("DecryptMessage Failed. (0x%08x)\n", ss); } return; } // // バッファの出力 // PBYTE pMessageFromServer = NULL; ulSize = secBufMsg[1].cbBuffer; pMessageFromServer = (PBYTE) calloc(ulSize, 1); if(!pMessageFromServer) { printf("calloc Failed.\n"); return; } ::CopyMemory(pMessageFromServer, secBufMsg[1].pvBuffer, ulSize); printf("Message from the server:\n"); PrintHexDump(ulSize, pMessageFromServer); fwrite(pMessageFromServer, ulSize, 1, stdout); printf("\n"); // 割り当てたメモリの解放 if(pIOBuf) { free(pIOBuf); pIOBuf = NULL; } // // SSPI のクリーンアップ // ss = DeleteSecurityContext(&hContext); if(SEC_E_OK != ss) { printf("DeleteSecurityContext Failed.\n"); return; } ss = FreeCredentialsHandle(&hCredential); if(SEC_E_OK != ss) { printf("FreeCredentialsHandle Failed.\n"); return; } // // ソケットを閉じる // shutdown(hClientSocket, SD_SEND); shutdown(hClientSocket, SD_BOTH); closesocket(hClientSocket); WSACleanup (); }
makefile は次の通りです。
TARGETNAME=sslclt OUTDIR=.\chk LINK32=link.exe ALL : "$(OUTDIR)\$(TARGETNAME).exe" CPPFLAGS=\ /nologo\ /MT\ /W4\ /Fo"$(OUTDIR)\\"\ /Fd"$(OUTDIR)\\"\ /c\ /Zi\ /DWIN32\ /DSECURITY_WIN32 LINK32_FLAGS=\ Secur32.lib\ Crypt32.lib\ Ws2_32.lib\ /nologo\ /subsystem:console\ /pdb:"$(OUTDIR)\$(TARGETNAME).pdb"\ /machine:I386\ /out:"$(OUTDIR)\$(TARGETNAME).exe"\ /DEBUG LINK32_OBJS= \ "$(OUTDIR)\$(TARGETNAME).obj" "$(OUTDIR)\helpers.obj" "$(OUTDIR)\$(TARGETNAME).exe" : "$(OUTDIR)" $(LINK32_OBJS) $(LINK32) $(LINK32_FLAGS) $(LINK32_OBJS) "$(OUTDIR)" : if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" .c{$(OUTDIR)}.obj: $(CPP) $(CPPFLAGS) $< .cpp{$(OUTDIR)}.obj: $(CPP) $(CPPFLAGS) $<
実行例
先に sslsrv.exe を実行してください。次のように接続待ち状態になります.
> sslsrv.exe Listening...
この状態でもうひとつコマンドプロンプトを開き、クライアントを実行します。
すると、次のように一気にプログラムが実行されます。
> sslclt.exe Socket has been created. Successfully connecting to the server. InitializeSecurityContext returned 90312 0000 16 03 00 04 bb 02 00 00:46 03 00 49 bc 65 4b 04 ........F..I.eK. 0010 89 ad dc 43 f2 0b 14 a7:5b 88 6c ef 06 97 58 77 ...C....[.l...Xw 0020 0a 49 66 9c 38 92 41 87:30 87 95 20 44 1e 00 00 .If.8.A.0.. D... 0030 0c d6 49 c1 62 ca f4 41:74 34 51 14 43 00 47 0f ..I.b..At4Q.C.G. 0040 df a1 2a ed 6b d5 b7 fb:51 a1 fc 73 00 05 00 0b ..*.k...Q..s.... 0050 00 04 69 00 04 66 00 04:63 30 82 04 5f 30 82 04 ..i..f..c0.._0.. 0060 09 a0 03 02 01 02 02 0a:61 19 01 0a 00 00 00 00 ........a....... 0070 00 02 30 0d 06 09 2a 86:48 86 f7 0d 01 01 05 05 ..0...*.H....... 0080 00 30 81 92 31 22 30 20:06 09 2a 86 48 86 f7 0d .0..1"0 ..*.H... 0090 01 09 01 16 13 64 61 64:6f 73 61 6e 40 6b 65 69 .....dadosan@kei 00a0 63 6f 64 65 2e 63 6f 6d:31 0b 30 09 06 03 55 04 code.com1.0...U. 00b0 06 13 02 55 53 31 13 30:11 06 03 55 04 08 13 0a ...US1.0...U.... 00c0 43 61 6c 69 66 6f 72 6e:69 61 31 14 30 12 06 03 California1.0... 00d0 55 04 07 13 0b 4c 6f 73:20 41 6e 67 65 6c 65 73 U....Los Angeles 00e0 31 0e 30 0c 06 03 55 04:0a 13 05 6f 79 61 6d 61 1.0...U....oyama 00f0 31 10 30 0e 06 03 55 04:0b 13 07 6b 65 69 63 6f 1.0...U....keico 0100 64 65 31 12 30 10 06 03:55 04 03 13 09 6b 65 69 de1.0...U....kei 0110 73 75 6b 65 63 61 30 1e:17 0d 30 39 30 32 31 38 sukeca0...090218 0120 32 30 30 34 33 38 5a 17:0d 31 30 30 32 31 38 32 200438Z..1002182 0130 30 31 34 33 38 5a 30 67:31 0b 30 09 06 03 55 04 01438Z0g1.0...U. 0140 06 13 02 55 53 31 0b 30:09 06 03 55 04 08 13 02 ...US1.0...U.... 0150 43 41 31 14 30 12 06 03:55 04 07 13 0b 4c 6f 73 CA1.0...U....Los 0160 20 41 6e 67 65 6c 65 73:31 0e 30 0c 06 03 55 04 Angeles1.0...U. 0170 0a 13 05 6f 79 61 6d 61:31 0f 30 0d 06 03 55 04 ...oyama1.0...U. 0180 0b 13 06 66 61 6d 69 6c:79 31 14 30 12 06 03 55 ...family1.0...U 0190 04 03 13 0b 6b 65 69 73:75 6b 65 6f 2d 68 70 30 ....keisukeo-hp0 01a0 81 9f 30 0d 06 09 2a 86:48 86 f7 0d 01 01 01 05 ..0...*.H....... 01b0 00 03 81 8d 00 30 81 89:02 81 81 00 a5 1e 8e 07 .....0.......... 01c0 93 ef c2 a6 6e 12 f5 2a:b1 ab 3a 87 2d 65 6e 10 ....n..*..:.-en. 01d0 c1 59 29 2d c9 a3 d9 9f:35 6c 63 47 0d ff c1 4a .Y)-....5lcG...J 01e0 3f bc 1a 04 36 bd 08 cd:79 41 46 65 2e 41 48 37 ?...6...yAFe.AH7 01f0 50 9e 57 55 80 a7 94 69:29 e4 1e 37 71 44 7d c9 P.WU...i)..7qD}. 0200 2d ce 33 8c 46 cd 4f 59:b7 f1 61 d3 d6 2a 1e 72 -.3.F.OY..a..*.r 0210 db 54 86 da a7 e5 ed 5c:64 46 be 0f 77 f6 f2 07 .T.....\dF..w... 0220 3f 6e c9 3f 9a 95 c3 5c:8c 1f 53 49 9d a7 39 a0 ?n.?...\..SI..9. 0230 55 05 05 b0 77 29 4f 91:95 e6 de 7b 02 03 01 00 U...w)O....{.... 0240 01 a3 82 02 25 30 82 02:21 30 0e 06 03 55 1d 0f ....%0..!0...U.. 0250 01 01 ff 04 04 03 02 04:f0 30 13 06 03 55 1d 25 .........0...U.% 0260 04 0c 30 0a 06 08 2b 06:01 05 05 07 03 01 30 1d ..0...+.......0. 0270 06 03 55 1d 0e 04 16 04:14 fd e2 7f 85 c1 5d e9 ..U...........]. 0280 71 9a 84 c1 ea 91 32 9f:34 22 f8 8f 50 30 81 ce q.....2.4"..P0.. 0290 06 03 55 1d 23 04 81 c6:30 81 c3 80 14 25 28 ba ..U.#...0....%(. 02a0 4b 60 1f 4f 45 0a 13 3d:31 f4 fa 4f 33 9a a5 55 K`.OE..=1..O3..U 02b0 6d a1 81 98 a4 81 95 30:81 92 31 22 30 20 06 09 m......0..1"0 .. 02c0 2a 86 48 86 f7 0d 01 09:01 16 13 64 61 64 6f 73 *.H........dados 02d0 61 6e 40 6b 65 69 63 6f:64 65 2e 63 6f 6d 31 0b an@keicode.com1. 02e0 30 09 06 03 55 04 06 13:02 55 53 31 13 30 11 06 0...U....US1.0.. 02f0 03 55 04 08 13 0a 43 61:6c 69 66 6f 72 6e 69 61 .U....California 0300 31 14 30 12 06 03 55 04:07 13 0b 4c 6f 73 20 41 1.0...U....Los A 0310 6e 67 65 6c 65 73 31 0e:30 0c 06 03 55 04 0a 13 ngeles1.0...U... 0320 05 6f 79 61 6d 61 31 10:30 0e 06 03 55 04 0b 13 .oyama1.0...U... 0330 07 6b 65 69 63 6f 64 65:31 12 30 10 06 03 55 04 .keicode1.0...U. 0340 03 13 09 6b 65 69 73 75:6b 65 63 61 82 10 65 07 ...keisukeca..e. 0350 26 be 67 69 be b0 48 6b:3a a4 e6 55 4f 56 30 6f &.gi..Hk:..UOV0o 0360 06 03 55 1d 1f 04 68 30:66 30 30 a0 2e a0 2c 86 ..U...h0f00...,. 0370 2a 68 74 74 70 3a 2f 2f:6b 65 69 73 75 6b 65 77 *http://keisukew 0380 32 6b 2f 43 65 72 74 45:6e 72 6f 6c 6c 2f 6b 65 2k/CertEnroll/ke 0390 69 73 75 6b 65 63 61 2e:63 72 6c 30 32 a0 30 a0 isukeca.crl02.0. 03a0 2e 86 2c 66 69 6c 65 3a:2f 2f 5c 5c 6b 65 69 73 ..,file://\\keis 03b0 75 6b 65 77 32 6b 5c 43:65 72 74 45 6e 72 6f 6c ukew2k\CertEnrol 03c0 6c 5c 6b 65 69 73 75 6b:65 63 61 2e 63 72 6c 30 l\keisukeca.crl0 03d0 81 98 06 08 2b 06 01 05:05 07 01 01 04 81 8b 30 ....+..........0 03e0 81 88 30 41 06 08 2b 06:01 05 05 07 30 02 86 35 ..0A..+.....0..5 03f0 68 74 74 70 3a 2f 2f 6b:65 69 73 75 6b 65 77 32 http://keisukew2 0400 6b 2f 43 65 72 74 45 6e:72 6f 6c 6c 2f 6b 65 69 k/CertEnroll/kei 0410 73 75 6b 65 77 32 6b 5f:6b 65 69 73 75 6b 65 63 sukew2k_keisukec 0420 61 2e 63 72 74 30 43 06:08 2b 06 01 05 05 07 30 a.crt0C..+.....0 0430 02 86 37 66 69 6c 65 3a:2f 2f 5c 5c 6b 65 69 73 ..7file://\\keis 0440 75 6b 65 77 32 6b 5c 43:65 72 74 45 6e 72 6f 6c ukew2k\CertEnrol 0450 6c 5c 6b 65 69 73 75 6b:65 77 32 6b 5f 6b 65 69 l\keisukew2k_kei 0460 73 75 6b 65 63 61 2e 63:72 74 30 0d 06 09 2a 86 sukeca.crt0...*. 0470 48 86 f7 0d 01 01 05 05:00 03 41 00 4f da 32 18 H.........A.O.2. 0480 33 d7 2e b7 c2 2b cb 60:79 36 e3 c2 a6 18 d3 9b 3....+.`y6...... 0490 2f dc b7 91 89 01 cb 5a:77 36 df 59 b3 08 72 9f /......Zw6.Y..r. 04a0 5e 94 c9 4b 3a 8a c6 92:49 b9 0d 6a cc cd b7 ee ^..K:...I..j.... 04b0 48 78 1c 8d 11 03 7f a5:ac 19 f7 ab 0e 00 00 00 Hx.............. InitializeSecurityContext returned 90312 0000 14 03 00 00 01 01 16 03:00 00 3c 49 47 6d da ed ..........<IGm.. 0010 d6 7a ec 41 70 2d 1a 2f:bd 79 77 d5 ee 6e a6 c1 .z.Ap-./.yw..n.. 0020 b1 ab 90 1e 3c 8d 33 c0:68 f2 c8 5c 05 2a 17 d4 ....<.3.h..\.*.. 0030 b7 60 5b c6 7c 96 98 b0:93 30 0f 22 2d dc 7a 2a .`[.|....0."-.z* 0040 b7 d5 29 7f 82 ff 8a ..).... InitializeSecurityContext returned 0 Raw Message Stream: 0000 17 03 00 00 3a cb 00 86:a0 38 51 2e b3 7a 55 0d ....:....8Q..zU. 0010 91 d3 1b 64 56 80 9d b8:f4 be d3 47 b1 34 13 85 ...dV......G.4.. 0020 b1 99 14 c4 66 f0 6d da:30 f9 29 f7 49 fb 70 fd ....f.m.0.).I.p. 0030 89 ab d7 30 46 1e fd fb:29 a0 4f d7 cd 23 28 ...0F...).O..#( Message from the server: 0000 54 68 69 73 20 6d 65 73:73 61 67 65 20 77 61 73 This message was 0010 20 73 65 6e 74 20 66 72:6f 6d 20 73 73 6c 73 72 sent from sslsr 0020 76 2e 65 78 65 2e v.exe. This message was sent from sslsrv.exe. >
サーバー側の出力を見ると確かにクライアントの接続をアクセプトし、 通信したことがわかります。
Accepting a client. 0000 16 03 00 00 33 01 00 00:2f 03 00 49 bc 65 4b 69 ....3.../..I.eKi 0010 28 bb dc 83 6f fb fa 15:f8 e8 25 c1 f1 9e 24 5a (...o.....%...$Z 0020 5b e8 87 cb 92 ab c1 5f:f1 41 7c 00 00 08 00 05 [......_.A|..... 0030 00 0a 00 13 00 04 01 00: ........ AcceptSecurityContext returned 90312 0000 16 03 00 00 84 10 00 00:80 33 18 f2 e6 33 9e 33 .........3...3.3 0010 f8 84 54 a8 2b 98 ea f4:27 1d 4f 95 d2 ea e9 d0 ..T.+...'.O..... 0020 07 f1 d3 0e 23 1d 79 b6:a1 54 d5 c6 7b b7 4c a7 ....#.y..T..{.L. 0030 64 75 f0 ee 5b 50 84 11:aa f9 ff 40 32 b4 11 9a du..[P.....@2... 0040 01 5b 64 c8 55 b9 49 fb:ea d9 4d 7e 0e dc ee 8c .[d.U.I...M~.... 0050 32 26 40 2c bd 8c d7 3b:b7 6f 97 2f 12 f4 23 9b 2&@,...;.o./..#. 0060 c3 3f ea 97 11 a0 8d ed:3f 1b 99 5d 57 48 c7 f6 .?......?..]WH.. 0070 10 03 b4 64 c4 db b3 2c:d4 56 9a 27 ae 18 2d c6 ...d...,.V.'..-. 0080 27 1c 8a bf 39 ce 7b 96:0a 14 03 00 00 01 01 16 '...9.{......... 0090 03 00 00 3c c7 da 89 27:f7 38 0b c7 d9 44 bb a3 ...<...'.8...D.. 00a0 88 55 92 f9 55 4d 6c ef:a5 0e 57 63 08 f4 d6 c2 .U..UMl...Wc.... 00b0 72 8f 5d 4d c5 30 1a c1:09 28 6b e8 1a d3 22 d0 r.]M.0...(k...". 00c0 da 0f 24 14 55 8c fc 60:bd 89 e6 54 eb cf f8 c4 ..$.U..`...T.... AcceptSecurityContext returned 0 Message Stream: 0000 17 03 00 00 3a cb 00 86:a0 38 51 2e b3 7a 55 0d ....:....8Q..zU. 0010 91 d3 1b 64 56 80 9d b8:f4 be d3 47 b1 34 13 85 ...dV......G.4.. 0020 b1 99 14 c4 66 f0 6d da:30 f9 29 f7 49 fb 70 fd ....f.m.0.).I.p. 0030 89 ab d7 30 46 1e fd fb:29 a0 4f d7 cd 23 28 ...0F...).O..#( >
実行結果を見れば、確かにハンドシェイクを行い暗号化されたメッセージを送受信していることがわかると思います。
ちなみに冒頭の写真はシーメンスの古い暗号機です。 Photo by octal. Thank you!