EternalWindows
SSPI / 署名と検証
対応するサーバーはこちら

署名は、これから送信するデータが第三者によって改ざんされておらず、 間違いなく送信者によって作成されたデータであることを証明する技術です。 署名は暗号化を行うEncryptMessageでも行われますが、 暗号化を行わず署名だけを行いたい場合はMakeSignatureを呼び出します。

SECURITY_STATUS SEC_Entry MakeSignature(
  PCtxtHandle phContext,
  ULONG fQOP,
  PSecBufferDesc pMessage,
  ULONG MessageSeqNo
);

phContextは、コンテキストハンドルを指定します。 fQOPは、保護の品質を表す定数を指定します。 pMessageは、署名したいデータを格納したSecBufferDesc構造体を指定します。 MessageSeqNoは、0を指定します。

今回のクライアントプログラムは、データに署名をしてサーバーに送信します。 事前に対応するサーバープログラムを起動しておいてください。

#define  SECURITY_WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#include <security.h>

#pragma comment (lib, "secur32.lib")
#pragma comment (lib, "ws2_32.lib")

SOCKET g_soc = INVALID_SOCKET;

SOCKET InitializeWinsock(LPSTR lpszServerName, LPSTR lpszPort);
BOOL ClientHandshake(PCredHandle phCredential, PCtxtHandle phContext);
void SendSignData(PCtxtHandle phContext, LPVOID lpData, ULONG uSize);
void SendData(LPVOID lpData, ULONG uSize);
LPVOID ReceiveData(ULONG *lpuSize);

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
{
	TCHAR      szData[] = TEXT("sample");
	CredHandle hCredential;
	CtxtHandle hContext;
	TimeStamp  ts;

	if (AcquireCredentialsHandle(NULL, TEXT("NTLM"), SECPKG_CRED_OUTBOUND, NULL, NULL, NULL, NULL, &hCredential, &ts) != SEC_E_OK) {
		MessageBox(NULL, TEXT("クレデンシャルハンドルの取得に失敗しました。"), NULL, MB_ICONWARNING);
		return 0;
	}
	
	g_soc = InitializeWinsock("localhost", "5000");
	if (g_soc == INVALID_SOCKET) {
		FreeCredentialsHandle(&hCredential);
		WSACleanup();
		return 0;
	}
	
	if (!ClientHandshake(&hCredential, &hContext)) {
		FreeCredentialsHandle(&hCredential);
		closesocket(g_soc);
		WSACleanup();
		MessageBox(NULL, TEXT("認証に失敗しました。"), NULL, MB_ICONWARNING);
		return 0;
	}
	
	MessageBox(NULL, TEXT("認証に成功しました。"), TEXT("クライアント"), MB_OK);
	
	SendSignData(&hContext, szData, sizeof(szData));

	DeleteSecurityContext(&hContext);
	FreeCredentialsHandle(&hCredential);
	closesocket(g_soc);
	WSACleanup();

	return 0;
}

BOOL ClientHandshake(PCredHandle phCredential, PCtxtHandle phContext)
{
	BOOL            bFirst = TRUE;
	ULONG           uSize;
	ULONG           uAttributes = ISC_REQ_STREAM | ISC_REQ_CONFIDENTIALITY;
	SecBuffer       sbOut[1];
	SecBuffer       sbIn[1];
	SecBufferDesc   sbdOut;
	SecBufferDesc   sbdIn;
	SECURITY_STATUS ss = SEC_I_CONTINUE_NEEDED;
	
	while (ss == SEC_I_CONTINUE_NEEDED) {
		if (!bFirst) {
			sbIn[0].pvBuffer   = ReceiveData(&uSize);
			sbIn[0].cbBuffer   = uSize;
			sbIn[0].BufferType = SECBUFFER_TOKEN;

			sbdIn.ulVersion = SECBUFFER_VERSION;
			sbdIn.cBuffers  = 1;
			sbdIn.pBuffers  = sbIn;
		}
		
		sbOut[0].cbBuffer   = 0;
		sbOut[0].BufferType = SECBUFFER_TOKEN;
		sbOut[0].pvBuffer   = NULL;

		sbdOut.ulVersion = SECBUFFER_VERSION;
		sbdOut.cBuffers  = 1;
		sbdOut.pBuffers  = sbOut;
		
		ss = InitializeSecurityContext(phCredential, bFirst ? NULL : phContext, NULL, uAttributes | ISC_REQ_ALLOCATE_MEMORY,
			0, SECURITY_NETWORK_DREP, bFirst ? NULL : &sbdIn, 0, phContext, &sbdOut, &uAttributes, NULL);
		
		if (sbOut[0].cbBuffer != 0) {
			SendData(sbOut[0].pvBuffer, sbOut[0].cbBuffer);
			FreeContextBuffer(sbOut[0].pvBuffer);
		}
		
		if (!bFirst)
			HeapFree(GetProcessHeap(), 0, sbIn[0].pvBuffer);

		bFirst = FALSE;
	}

	return ss == SEC_E_OK;
}

void SendSignData(PCtxtHandle phContext, LPVOID lpData, ULONG uSize)
{
	LPVOID              lpSig;
	SecBuffer           sb[2];
	SecBufferDesc       sbd;
	SecPkgContext_Sizes size;
	
	QueryContextAttributes(phContext, SECPKG_ATTR_SIZES, &size);

	lpSig = HeapAlloc(GetProcessHeap(), 0, size.cbSecurityTrailer);

	sb[0].cbBuffer   = size.cbSecurityTrailer;
	sb[0].BufferType = SECBUFFER_TOKEN;
	sb[0].pvBuffer   = lpSig;

	sb[1].cbBuffer   = uSize;
	sb[1].BufferType = SECBUFFER_DATA;
	sb[1].pvBuffer   = lpData;

	sbd.ulVersion = SECBUFFER_VERSION;
	sbd.cBuffers  = 2;
	sbd.pBuffers  = sb;

	MakeSignature(phContext, 0, &sbd, 0);

	SendData(sb[0].pvBuffer, sb[0].cbBuffer);
	SendData(sb[1].pvBuffer, sb[1].cbBuffer);
	
	HeapFree(GetProcessHeap(), 0, lpSig);
}

void SendData(LPVOID lpData, ULONG uSize)
{
	send(g_soc, (char *)&uSize, sizeof(ULONG), 0);
	send(g_soc, (char *)lpData, uSize, 0);
}

LPVOID ReceiveData(ULONG *lpuSize)
{
	ULONG  uSize;
	LPVOID lpData;

	recv(g_soc, (char *)&uSize, sizeof(ULONG), 0);
	lpData = HeapAlloc(GetProcessHeap(), 0, uSize);
	recv(g_soc, (char *)lpData, uSize, 0);
	
	*lpuSize = uSize;

	return lpData;
}

SOCKET InitializeWinsock(LPSTR lpszServerName, LPSTR lpszPort)
{
	WSADATA    wsaData;
	ADDRINFO   addrHints;
	LPADDRINFO lpAddrList;
	SOCKET     soc;
	
	WSAStartup(MAKEWORD(2, 2), &wsaData);

	ZeroMemory(&addrHints, sizeof(addrinfo));
	addrHints.ai_family   = AF_INET;
	addrHints.ai_socktype = SOCK_STREAM;
	addrHints.ai_protocol = IPPROTO_TCP;

	if (getaddrinfo(lpszServerName, lpszPort, &addrHints, &lpAddrList) != 0) {
		MessageBox(NULL, TEXT("ホスト情報からアドレスの取得に失敗しました。"), NULL, MB_ICONWARNING);
		WSACleanup();
		return INVALID_SOCKET;
	}

	soc = socket(lpAddrList->ai_family, lpAddrList->ai_socktype, lpAddrList->ai_protocol);

	if (connect(soc, lpAddrList->ai_addr, (int)lpAddrList->ai_addrlen) == SOCKET_ERROR) {
		int nError = WSAGetLastError();
		if (nError == WSAECONNREFUSED)
			MessageBox(NULL, TEXT("サーバーがリッスンしていません。"), NULL, MB_ICONWARNING);
		else if (nError == WSAEHOSTUNREACH)
			MessageBox(NULL, TEXT("サーバーが存在しません。"), NULL, MB_ICONWARNING);
		else
			MessageBox(NULL, TEXT("サーバーへの接続が失敗しました。"), NULL, MB_ICONWARNING);
		freeaddrinfo(lpAddrList);
		closesocket(soc);
		WSACleanup();
		return INVALID_SOCKET;
	}
	
	freeaddrinfo(lpAddrList);

	return soc;
}

プログラムの内容は、暗号化を行った時のクライアントプログラムとほぼ同一です。 SendSignDataという自作関数を作成し、そこでMakeSignatureを呼び出しています。 署名に必要なデータは、署名バッファと送信データです。 署名バッファを確保するために、QueryContextAttributesを呼び出してサイズを取得し、 確保したメモリをSecBuffer構造体の配列に指定します。 MakeSignatureの呼び出しを終えたら、署名バッファとデータをSendDataで送信します。


戻る