EternalWindows
証明書検証 / WinTrust

これまで行ってきた証明書の検証は、証明書が単一のデータとして存在する場合を常に想定していました。 しかし、実際には証明書は署名と共にファイルに格納されるようなこともあり、 このような場合は、ファイルから署名を取得、署名から証明書を取得、 そして証明書の検証といったように、多くの手順を踏まなければなりません。 こうした問題を解決する場合、WinTrustと呼ばれる関数群を利用するのは非常に効果的です。 それは、WinTrustが信頼プロバイダーと呼ばれるDLLへのアクセスを提供しているからであり、 システムに既定で存在する信頼プロバイダーが、証明書の検証機能を備えているからです。

信頼プロバイダーは、ファイルなどから証明書を取得しなければなりませんが、 これは内部的には、SIP(Subject Interface Package)の呼び出しで実現していることがあります。 SIPはファイルのフォーマットを理解し、署名を取得できますから、 信頼プロバイダーはその署名から証明書を取得し、検証を行うことになります。 また、信頼プロバイダーはユーザーとの対話機能を備えることが可能で、 たとえば、ファイルに格納されている署名が有効であるかを表示するようなことができます。

このようなダイアログは、ネットワーク上からファイルをダウンロードする際に見かけることがあります。 ファイルに署名が格納されており、問題がなければ上記のようにファイルの公開元が表示されるため、 ユーザーは安心してインストールすることができます。 このように、信頼プロバイダーは証明書の検証と対話機能を実装しているため、 適切な信頼プロバイダーを選択することで、 アプリケーションが自ら検証処理を行う必要はなくなります。

信頼プロバイダーを利用した検証を行うには、WinVerifyTrustを呼び出します。

HRESULT WINAPI WinVerifyTrust(
  HWND hWnd, 
  GUID *pgActionID, 
  WINTRUST_DATA *pWinTrustData 
);

hWndは、UIを表示するために使う親ウインドウのハンドルを指定します。 親ウインドウをデスクトップとする場合は、NULLで構いません。 また、UIの表示を予定しない場合は、INVALID_HANDLE_VALUEを指定します。 pgActionIDは、使用する信頼プロバイダーのGUIDを指定します。 pWinTrustDataは、信頼プロバイダーに渡すべきデータを表すWINTRUST_DATA構造体のアドレスを指定します。

システムに既定でインストールされている信頼プロバイダーは、 そのGUIDが専用の定義として存在します。 定義されているGUIDの一部を示します。

GUID 説明
DRIVER_ACTION_VERIFY ドライバがWHQLの条件を満たしているかを検証する。
HTTPSPROV_ACTION IEで行われているSSL/PCT接続について検証する。
OFFICESIGN_ACTION_VERIFY MicrosoftのOffice製品を検証する。
WINTRUST_ACTION_GENERIC_VERIFY_V2 証明書を格納したファイルなどを検証する。

WINTRUST_DATA構造体は、次のように定義されています。

typedef struct _WINTRUST_DATA {
  DWORD cbStruct;
  LPVOID pPolicyCallbackData;
  LPVOID pSIPClientData;
  DWORD dwUIChoice;
  DWORD fdwRevocationChecks;
  DWORD dwUnionChoice;
  union {
    struct WINTRUST_FILE_INFO_* pFile;
    struct WINTRUST_CATALOG_INFO_* pCatalog;
    struct WINTRUST_BLOB_INFO_* pBlob;
    struct WINTRUST_SGNR_INFO_* pSgnr;
    struct WINTRUST_CERT_INFO_* pCert;
  };
  DWORD dwStateAction;
  HANDLE hWVTStateData;
  WCHAR* pwszURLReference;
  DWORD dwProvFlags;
  DWORD dwUIContext;
} WINTRUST_DATA,  *PWINTRUST_DATA;

cbStructは、この構造体のサイズをバイト単位で指定します。 pPolicyCallbackDataは、信頼プロバイダーに渡したいデータを指定します。 pSIPClientDataは、SIPに渡したいデータを指定します。 dwUIChoiceは、表示するUIの種類を表す定数を指定します。 fdwRevocationChecksは、証明書が失効されているかを確認する定数を指定します。 WTD_REVOKE_NONEを指定した場合は、失効の確認を行います。 dwUnionChoiceは、検証対象とするオブジェクトの種類を表す定数を指定します。 WTD_CHOICE_FILEを指定した場合、pFileを初期化します。 dwStateActionは、状態ハンドルに関する定数を指定します。 hWVTStateDataは、dwStateActionにWTD_STATEACTION_VERIFYを指定した場合に状態ハンドルが返ります。 pwszURLReferenceは、予約されているためNULLを指定します。 dwProvFlagsは、信頼プロバイダーの動作を表す定数を指定します。 dwUIContextは、WTD_UICONTEXT_EXECUTEまたは、WTD_UICONTEXT_INSTALLを指定します。 前者の場合、表示されるダイアログはファイル実行の可否を尋ねるものになり、 後者の場合はインストールの可否を尋ねるダイアログになります。

今回のプログラムは、WINTRUST_ACTION_GENERIC_VERIFY_V2で表される 信頼プロバイダーを利用する例を示しています。

#include <windows.h>
#include <softpub.h>

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

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
{
	GUID               guidAction = WINTRUST_ACTION_GENERIC_VERIFY_V2;
	WINTRUST_DATA      trustData;
	WINTRUST_FILE_INFO fileInfo;
	
	fileInfo.cbStruct       = sizeof(WINTRUST_FILE_INFO);
	fileInfo.hFile          = NULL;
	fileInfo.pcwszFilePath  = L"sample.exe";
	fileInfo.pgKnownSubject = NULL;
	
	trustData.cbStruct            = sizeof(WINTRUST_DATA);
	trustData.pPolicyCallbackData = NULL;
	trustData.pSIPClientData      = NULL;
	trustData.dwUIChoice          = WTD_UI_ALL;
	trustData.pFile               = &fileInfo;
	trustData.fdwRevocationChecks = WTD_REVOKE_NONE;
	trustData.dwUnionChoice       = WTD_CHOICE_FILE;
	trustData.dwStateAction       = WTD_STATEACTION_IGNORE;
	trustData.hWVTStateData       = NULL;
	trustData.pwszURLReference    = NULL;
	trustData.dwProvFlags         = 0;
	trustData.dwUIContext         = WTD_UICONTEXT_EXECUTE;

	if (WinVerifyTrust(NULL, &guidAction, &trustData) == ERROR_SUCCESS)
		MessageBox(NULL, TEXT("ファイルを実行しました。"), TEXT("OK"), MB_OK);
	else
		MessageBox(NULL, TEXT("ファイルを実行しませんでした。"), NULL, MB_ICONWARNING);

	return 0;
}

WINTRUST_ACTION_GENERIC_VERIFY_V2を指定する場合は、 ファイルに格納されている署名を検証することになるため、 WINTRUST_FILE_INFO構造体を事前に初期化しておくことになります。 これは、単純にpcwszFilePathにファイル名を指定するだけで構いません。 次に、WINTRUST_DATA構造体の初期化を確認します。

trustData.cbStruct            = sizeof(WINTRUST_DATA);
trustData.pPolicyCallbackData = NULL;
trustData.pSIPClientData      = NULL;
trustData.dwUIChoice          = WTD_UI_ALL;
trustData.fdwRevocationChecks = WTD_REVOKE_NONE;
trustData.dwUnionChoice       = WTD_CHOICE_FILE;
trustData.pFile               = &fileInfo;
trustData.dwStateAction       = WTD_STATEACTION_IGNORE;
trustData.hWVTStateData       = NULL;
trustData.pwszURLReference    = NULL;
trustData.dwProvFlags         = 0;
trustData.dwUIContext         = WTD_UICONTEXT_EXECUTE;

pPolicyCallbackDataとpSIPClientDataは、それぞれ信頼プロバイダーとSIPに渡すデータとなりますが、 通常はNULLを指定します。 dwUIChoiceのWTD_UI_ALLはUIを表示することを意味し、WINTRUST_ACTION_GENERIC_VERIFY_V2におけるUIとは、 先に示した署名の検証結果を表示するダイアログです。 fdwRevocationChecksのWTD_REVOKE_NONEは、証明書の失効確認を行わないことを意味します。 dwUnionChoiceのWTD_CHOICE_FILEは、pFileにWINTRUST_FILE_INFO構造体を指定することを意味しています。 dwStateActionは、状態ハンドルに関するメンバですが、 今回は状態ハンドルを使用しないのでWTD_STATEACTION_IGNOREを指定します。 また、状態ハンドルを表すhWVTStateDataはNULLを指定しています。 pwszURLReferenceは、予約されているためNULLを指定します。 dwProvFlagsは、信頼プロバイダーに特別な動作を与えないということで0を指定しています。 dwUIContextはWTD_UICONTEXT_EXECUTEとしているため、 表示されるダイアログはファイルの実行を尋ねるものになります。 次に、WinVerifyTrustの呼び出しを示します。

if (WinVerifyTrust(NULL, &guidAction, &trustData) == ERROR_SUCCESS)
	MessageBox(NULL, TEXT("ファイルを実行しました。"), TEXT("OK"), MB_OK);
else
	MessageBox(NULL, TEXT("ファイルを実行しませんでした。"), NULL, MB_ICONWARNING);

このコードでは、メッセージの内容を実行の有無にしていますが、 これはあまり好ましくないとも言えるでしょう。 たとえば、dwUIChoiceにWTD_UI_NONEを指定した場合はUIが表示されませんから、 メッセージの内容は署名の検証結果とするべきです。 また、dwUIChoiceにWTD_UI_ALLを指定しても、dwProvFlagsにWTD_SAFER_FLAGを指定している場合は、 UIが表示されないことがあります。 WTD_SAFER_FLAGは、対象となるファイルなどが署名を持たない場合には、UIを表示せずにエラーと見なすからです。


戻る