EternalWindows
ファイル署名 / GUIによる署名

署名するファイル名や署名に使う証明書などをコード上に記述するのではなく、 GUIでユーザーが選択できるようになると非常に便利です。 今回紹介するCryptUIWizDigitalSignを呼び出すと、次のようなダイアログが表示されます。

署名に使うファイルを入力すると、上図のような署名に使う証明書を選択するダイアログが表示されます。 ここで、「ストアをから選択」を押すと、MY証明書に存在する証明書が列挙されることになるため、 適切な証明書を選択し、CSP情報が関連付けられている場合は先に進むことができます。 ちなみに、オプションとしてカスタムを選択している場合は、 「ファイルから選択」を押すとことも可能になり、 証明書の代わりに.spcファイルを選択できるようになります。 また、次のようなダイアログが表示されます。

このダイアログでは、秘密鍵の格納場所を.pvkファイルか鍵コンテナのどちらでも選択することができるため、 秘密鍵の管理方法を問うことはありません。

CryptUIWizDigitalSignは、次のように定義されています。

BOOL WINAPI CryptUIWizDigitalSign(
  DWORD dwFlags,
  HWND hwndParent,
  LPCWSTR pwszWizardTitle,
  PCCRYPTUI_WIZ_DIGITAL_SIGN_INFO pDigitalSignInfo,
  PCCRYPTUI_WIZ_DIGITAL_SIGN_CONTEXT* ppSignContext
);

dwFlagsは、0またはCRYPTUI_WIZ_NO_UIを指定します。 CRYPTUI_WIZ_NO_UIを指定すると、UIを表示せずに署名を行うことができます。 hwndParentは、ダイアログの親ウインドウのハンドルを指定します。 不要な場合は、NULLで問題ありません。 pwszWizardTitleは、ダイアログのタイトルする文字列を指定します。 NULLを指定した場合、デフォルトのタイトルとなります。 pDigitalSignInfoは、署名に使うファイルの種類などを表すCRYPTUI_WIZ_DIGITAL_SIGN_INFO構造体のアドレスを指定します。 ppSignContextは、署名されたブロブを受け取るPCCRYPTUI_WIZ_DIGITAL_SIGN_CONTEXTのアドレスを指定します。 ブロブを受け取る場合は、CryptUIWizFreeDigitalSignContextで開放しなければなりません。 ブロブが不要な場合は、NULLを指定することができます。

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

typedef struct _CRYPTUI_WIZ_DIGITAL_SIGN_INFO {
  DWORD dwSize;
  DWORD dwSubjectChoice;
  union {
    LPCWSTR pwszFileName;
    PCCRYPTUI_WIZ_DIGITAL_SIGN_BLOB_INFO pSignBlobInfo;
  } ;
  DWORD dwSigningCertChoice;
  union {
    PCCERT_CONTEXT pSigningCertContext;
    PCCRYPTUI_WIZ_DIGITAL_SIGN_STORE_INFO pSigningCertStore;
    PCCRYPTUI_WIZ_DIGITAL_SIGN_CERT_PVK_INFO pSigningCertPvkInfo;
  } ;
  LPCWSTR pwszTimestampURL;
  DWORD dwAdditionalCertChoice;
  PCCRYPTUI_WIZ_DIGITAL_SIGN_EXTENDED_INFO pSignExtInfo;
} CRYPTUI_WIZ_DIGITAL_SIGN_INFO,  *PCRYPTUI_WIZ_DIGITAL_SIGN_INFO;

dwSizeは、構造体のサイズを指定します。 dwSubjectChoiceは、UIによる署名では基本的に0を指定しますが、 CRYPTUI_WIZ_DIGITAL_SIGN_SUBJECT_FILEを指定することもできます。 この場合、pwszFileNameがデフォルトで選択されているファイルとなります。 dwSigningCertChoiceもUIによる署名では基本的に0を指定すると思われます。 この場合、署名に使用する証明書や秘密鍵をUIで決定することになります。 pwszTimestampURLは、タイムスタンプサーバーのURLを指定します。 dwAdditionalCertChoiceは、署名に含める証明書を判断する定数を指定します。 CRYPTUI_WIZ_DIGITAL_SIGN_ADD_CHAINを指定すると、 CAの証明書などを含むことができるようになります。 pSignExtInfoは、署名アルゴリズムや属性を表すCRYPTUI_WIZ_DIGITAL_SIGN_EXTENDED_INFO構造体のアドレスを指定します。

今回のプログラムは、CryptUIWizDigitalSignでUIを表示した署名を行います。

#include <windows.h>
#include <cryptuiapi.h>

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

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
{
	CRYPTUI_WIZ_DIGITAL_SIGN_INFO signInfo;

	signInfo.dwSize                 = sizeof(CRYPTUI_WIZ_DIGITAL_SIGN_INFO);
	signInfo.dwSubjectChoice        = 0;
	signInfo.dwSigningCertChoice    = 0;
	signInfo.pwszTimestampURL       = NULL;
	signInfo.dwAdditionalCertChoice = CRYPTUI_WIZ_DIGITAL_SIGN_ADD_CHAIN;
	signInfo.pSignExtInfo           = NULL;

	CryptUIWizDigitalSign(0, NULL, NULL, &signInfo, NULL);

	return 0;
}

UIによる署名では、このような少ないコードで記述することができます。 署名するファイルが予め分かっている場合は、dwSubjectChoiceにCRYPTUI_WIZ_DIGITAL_SIGN_SUBJECT_FILEを指定し、 pwszFileNameにファイル名を指定してもよいでしょう。

UIを表示しない署名

CryptUIWizDigitalSignの第1引数にCRYPTUI_WIZ_NO_UIを指定した場合は、 UIを表示せずに署名を行うことができますが、この機能は非常に強力です。 SignerSignによる署名に比べて、独自にヘッダーファイルを定義する必要もないので、 UIを表示しない署名の場合でも、できるだけこの関数を利用するとよいでしょう。 次に、.spcと.pvkをベースとした署名の例を示します。

#include <windows.h>
#include <cryptuiapi.h>

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

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
{
	CRYPTUI_WIZ_DIGITAL_SIGN_INFO          signInfo;
	CRYPTUI_WIZ_DIGITAL_SIGN_CERT_PVK_INFO pvkInfo;
	CRYPTUI_WIZ_DIGITAL_SIGN_PVK_FILE_INFO pvkFileInfo;
	BOOL                                   bResult;

	pvkFileInfo.dwSize          = sizeof(CRYPTUI_WIZ_DIGITAL_SIGN_PVK_FILE_INFO);
	pvkFileInfo.pwszPvkFileName = L"sample.pvk";
	pvkFileInfo.pwszProvName    = NULL;
	pvkFileInfo.dwProvType      = PROV_RSA_FULL;

	pvkInfo.dwSize                  = sizeof(CRYPTUI_WIZ_DIGITAL_SIGN_CERT_PVK_INFO);
	pvkInfo.pwszSigningCertFileName = L"sample.spc";
	pvkInfo.dwPvkChoice             = CRYPTUI_WIZ_DIGITAL_SIGN_PVK_FILE;
	pvkInfo.pPvkFileInfo            = &pvkFileInfo;
	
	signInfo.dwSize                 = sizeof(CRYPTUI_WIZ_DIGITAL_SIGN_INFO);
	signInfo.dwSubjectChoice        = CRYPTUI_WIZ_DIGITAL_SIGN_SUBJECT_FILE;
	signInfo.pwszFileName           = L"sample.exe";
	signInfo.dwSigningCertChoice    = CRYPTUI_WIZ_DIGITAL_SIGN_PVK;
	signInfo.pSigningCertPvkInfo    = &pvkInfo;
	signInfo.pwszTimestampURL       = NULL;
	signInfo.dwAdditionalCertChoice = CRYPTUI_WIZ_DIGITAL_SIGN_ADD_CHAIN;
	signInfo.pSignExtInfo           = NULL;

	bResult = CryptUIWizDigitalSign(CRYPTUI_WIZ_NO_UI, NULL, NULL, &signInfo, NULL);
	if (bResult)
		MessageBox(NULL, TEXT("署名を格納しました。"), TEXT("OK"), MB_OK);
	else
		MessageBox(NULL, TEXT("署名の格納に失敗しました。"), NULL, MB_ICONWARNING);

	return 0;
}

CryptUIWizDigitalSignにCRYPTUI_WIZ_NO_UIを指定する場合は、 署名対象となるファイル名と秘密鍵情報は必ず指定しなければなりません。 まず、dwSubjectChoiceにCRYPTUI_WIZ_DIGITAL_SIGN_SUBJECT_FILEを指定し、 pwszFileNameに署名対象のファイル名を指定します。 さらに、dwSigningCertChoiceにCRYPTUI_WIZ_DIGITAL_SIGN_PVKを指定して、 pSigningCertPvkInfoを初期化します。 ここで指定しているpvkInfoは、次のように初期化されています。

pvkFileInfo.dwSize          = sizeof(CRYPTUI_WIZ_DIGITAL_SIGN_PVK_FILE_INFO);
pvkFileInfo.pwszPvkFileName = L"sample.pvk";
pvkFileInfo.pwszProvName    = NULL;
pvkFileInfo.dwProvType      = PROV_RSA_FULL;

pvkInfo.dwSize                  = sizeof(CRYPTUI_WIZ_DIGITAL_SIGN_CERT_PVK_INFO);
pvkInfo.pwszSigningCertFileName = L"sample.spc";
pvkInfo.dwPvkChoice             = CRYPTUI_WIZ_DIGITAL_SIGN_PVK_FILE;
pvkInfo.pPvkFileInfo            = &pvkFileInfo;

pvkInfoの型は、CRYPTUI_WIZ_DIGITAL_SIGN_CERT_PVK_INFO構造体であり、 署名に格納する証明書と秘密鍵の格納場所を指定します。 dwPvkChoiceにCRYPTUI_WIZ_DIGITAL_SIGN_PVK_FILEを指定した場合、 pPvkFileInfoにCRYPTUI_WIZ_DIGITAL_SIGN_PVK_FILE_INFO構造体のアドレスを指定することになり、 このpwszPvkFileNameに.pvkファイルの名前を指定することになります。 ちなみに、dwPvkChoiceにCRYPTUI_WIZ_DIGITAL_SIGN_PVK_PROVを指定すると、 pPvkProvInfoというメンバにCRYPT_KEY_PROV_INFO構造体のアドレスを指定することになります。 この場合、鍵コンテナに格納されている秘密鍵が利用されることになります。



戻る