EternalWindows
ファイル署名 / 署名の取得

特定のファイル形式をサポートできるSIPのサブジェクトGUIDを取得できれば、 署名の取得や格納を行えるようになります。 次に示すCryptSIPGetSignedDataMsgは、ファイルから署名を取得します。

BOOL WINAPI CryptSIPGetSignedDataMsg(
  SIP_SUBJECTINFO* pSubjectInfo,
  DWORD* pdwEncodingType,
  DWORD dwIndex,
  DWORD* pcbSignedDataMsg,
  BYTE* pbSignedDataMsg
);

pSubjectInfoは、SIP_SUBJECTINFO構造体のアドレスを指定します。 pdwEncodingTypeは、エンコーディングタイプを受け取る変数のアドレスを指定します。 dwIndexは、取得対象とする署名のインデックスを指定します。 pcbSignedDataMsgは、署名のサイズを受け取る変数のアドレスを指定します。 pbSignedDataMsgは、署名を受け取るバッファを指定します。

今回のプログラムはファイルから署名を取得し、それをまた別のファイルに保存します。

#include <windows.h>
#include <mssip.h>

#pragma comment (lib, "crypt32.lib")

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
{
	WCHAR           szFileName[] = L"sample.exe";
	GUID            guidSubject;
	SIP_SUBJECTINFO subejectInfo;
	DWORD           dwEncodingType;
	LPBYTE          lpSignedDataMsg;
	DWORD           dwSignedDataMsgSize;
	HANDLE          hFile;
	DWORD           dwWriteByte;

	if (!CryptSIPRetrieveSubjectGuidForCatalogFile(szFileName, NULL, &guidSubject))
		return 0;

	ZeroMemory(&subejectInfo, sizeof(SIP_SUBJECTINFO));
	subejectInfo.cbSize        = sizeof(SIP_SUBJECTINFO);
	subejectInfo.pgSubjectType = &guidSubject;
	subejectInfo.hFile         = INVALID_HANDLE_VALUE;
	subejectInfo.pwsFileName   = szFileName;

	if (!CryptSIPGetSignedDataMsg(&subejectInfo, &dwEncodingType, 0, &dwSignedDataMsgSize, NULL))
		dwSignedDataMsgSize = 10 * 1024;
	lpSignedDataMsg = (LPBYTE)HeapAlloc(GetProcessHeap(), 0, dwSignedDataMsgSize);

	if (!CryptSIPGetSignedDataMsg(&subejectInfo, &dwEncodingType, 0, &dwSignedDataMsgSize, lpSignedDataMsg)) {
		MessageBox(NULL, TEXT("署名の取得に失敗しました。"), NULL, MB_ICONWARNING);
		HeapFree(GetProcessHeap(), 0, lpSignedDataMsg);
		return 0;
	}

	hFile = CreateFile(TEXT("sample.p7b"), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
	WriteFile(hFile, lpSignedDataMsg, dwSignedDataMsgSize, &dwWriteByte, NULL);
	CloseHandle(hFile);
	
	MessageBox(NULL, TEXT("終了します。"), TEXT("OK"), MB_OK);

	HeapFree(GetProcessHeap(), 0, lpSignedDataMsg);

	return 0;
}

szFileNameに指定しているファイルには、既に署名が格納されているものとします。 まず、CryptSIPRetrieveSubjectGuidForCatalogFileにファイル名を指定し、 そのファイル形式をサポートしているSIPのサブジェクトGUIDを取得します。 続いて、CryptSIPGetSignedDataMsgを呼び出すための必要なSIP_SUBJECTINFO構造体を初期化します。

ZeroMemory(&subejectInfo, sizeof(SIP_SUBJECTINFO));
subejectInfo.cbSize        = sizeof(SIP_SUBJECTINFO);
subejectInfo.pgSubjectType = &guidSubject;
subejectInfo.hFile         = INVALID_HANDLE_VALUE;
subejectInfo.pwsFileName   = szFileName;

SIP_SUBJECTINFO構造体は、最初に全てのメンバを0で初期化するべきとされているので、 ZeroMemoryを呼び出し、必要なメンバのみを初期化することになります。 今回はファイルをファイル名で識別しているので、pwsFileNameメンバを初期化し、 hFileメンバは使用しないということで、INVALID_HANDLE_VALUEを指定します。 その他、サイズの初期化とサブジェクトGUIDを指定も忘れてはいけません。

CryptSIPGetSignedDataMsgの呼び出しは、次のようになっています。

f (!CryptSIPGetSignedDataMsg(&subejectInfo, &dwEncodingType, 0, &dwSignedDataMsgSize, NULL))
	dwSignedDataMsgSize = 10 * 1024;
lpSignedDataMsg = (LPBYTE)HeapAlloc(GetProcessHeap(), 0, dwSignedDataMsgSize);

if (!CryptSIPGetSignedDataMsg(&subejectInfo, &dwEncodingType, 0, &dwSignedDataMsgSize, lpSignedDataMsg)) {
	MessageBox(NULL, TEXT("署名の取得に失敗しました。"), NULL, MB_ICONWARNING);
	HeapFree(GetProcessHeap(), 0, lpSignedDataMsg);
	return 0;
}

1回目のCryptSIPGetSignedDataMsgの呼び出しでは、ファイルに格納されている署名のサイズが分からないため、 第5引数にNULLを指定し、第4引数でサイズを受け取ることに専念します。 しかし、ここで少し問題が発生することがあります。 どうやら、SIPによっては、署名のサイズを計算できない場合があり、 そのような場合は関数が失敗してしまうことになります。 今回は、10Kbという値を署名のサイズと仮定していますが、 あまりよい方法といえないと思われます。 ちなみに、1回目の呼び出しで失敗したのは.exeだけであり、 .msiと.cabについて正常にサイズを取得することができました。

CryptSIPGetSignedDataMsgで取得した署名をファイルに保存するとは、一体どういうことでしょうか。 実は、ファイルに格納されている署名の正体は、SignedData型のデータに過ぎません。 これは、PKCS #7形式であるため、p7bファイルとして保存することができ、 ファイルを開くことで署名に含まれる証明書を確認することができます。

CryptQueryObjectによる署名の取得

CryptQueryObjectは、何らかのファイルやデータから特定のデータを取得できる関数です。 この関数は、ファイルから署名を取得する機能も持っているので、 その利用方法を検討します。

#include <windows.h>

#pragma comment (lib, "crypt32.lib")

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
{
	WCHAR     szFileName[] = L"sample.exe";
	LPBYTE    lpSignedDataMsg;
	DWORD     dwSignedDataMsgSize;
	HANDLE    hFile;
	DWORD     dwWriteByte;
	BOOL      bResult;
	HCRYPTMSG hMsg;

	bResult = CryptQueryObject(CERT_QUERY_OBJECT_FILE, szFileName, CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED,
		CERT_QUERY_FORMAT_FLAG_BINARY, 0, NULL, NULL, NULL, NULL, &hMsg, NULL);
	if (!bResult) {
		MessageBox(NULL, TEXT("署名の取得に失敗しました。"), NULL, MB_ICONWARNING);
		return 0;
	}
	
	CryptMsgGetParam(hMsg, CMSG_ENCODED_MESSAGE, 0, NULL, &dwSignedDataMsgSize);
	lpSignedDataMsg = (LPBYTE)HeapAlloc(GetProcessHeap(), 0, dwSignedDataMsgSize);
	CryptMsgGetParam(hMsg, CMSG_ENCODED_MESSAGE, 0, lpSignedDataMsg, &dwSignedDataMsgSize);
	
	hFile = CreateFile(TEXT("sample.p7b"), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
	WriteFile(hFile, lpSignedDataMsg, dwSignedDataMsgSize, &dwWriteByte, NULL);
	CloseHandle(hFile);
	
	MessageBox(NULL, TEXT("終了します。"), TEXT("OK"), MB_OK);

	HeapFree(GetProcessHeap(), 0, lpSignedDataMsg);

	return 0;
}

ファイルから特定のデータを取得する場合は、第1引数にCERT_QUERY_OBJECT_FILEを指定し、第2引数にファイル名を指定します。 第3引数は、取得するデータの種類で、CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBEDはファイルに格納された署名を表します。 第4引数のCERT_QUERY_FORMAT_FLAG_BINARYは、データをバイナリで取得することを意味しています。 hMsgを除いた残りの引数以外はNULLを指定して問題ありません。 hMsgは、いわばCryptMsgOpenToDecodeが返したハンドルであるため、 CMSG_ENCODED_MESSAGEでデータを再エンコードすることにより、署名を取得することができます。 CMSG_CONTENT_PARAMを指定するとコンテンツが返るため注意してください。



戻る