EternalWindows
ファイル署名 / SIPとサブジェクトGUID

ファイルに署名を格納するに当たって最も考慮すべき事は、 その署名をファイル内のどこに格納すればよいかということです。 各種ファイルはそれぞれのフォーマットを持っており、 アプリケーションがそれらのフォーマットを事前に把握しておけばよいわけですが、 このような事は非常に困難です。 SIP(Subject Interface Package)とは、単一または複数のファイル形式を理解できるDLLであり、 このDLLが実装する関数を呼び出すことで、アプリケーションは各種ファイル形式の違いを意識せず、 署名を取得したり格納したりすることができます。 SIPとして登録されているDLLは、次のレジストリキーから確認することができます。

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography\OID\EncodingType 0

レジストリエディタで確認すると次のようになっています。

このキーにはCertDllOpenStoreProvを除いて、SIPが実装すべき関数名のサブキーが用意されています。 関数名のサブキーを開いてみると新たなサブキーが現れ、これはSIPのサブジェクトGUIDとなります。 サブジェクトGUIDは、SIPを一意に識別する数値のことです。 各種SIPは、自分が実装する関数名を持ったサブキーに自身のサブジェクトGUIDを追加し、 エントリして自身のDLL名と実装する関数名を設定します。 アプリケーションがSIP関数、たとえばCryptSIPGetSignedDataMsgを呼び出した場合、 CryptSIPDllGetSignedDataMsgキーから引数として受け取った サブジェクトGUIDと一致するサブキーが検出され、 設定されているDLLの関数が呼び出される仕組みになっています。

アプリケーションがSIPの関数を呼び出すためには、まず使用するSIPのサブジェクトGUIDを取得しなければなりません。 使用するSIPというのは、これから署名を取得、または格納しようとするファイル形式を理解できるSIPでなければならず、 どのSIPがどのファイル形式をサポートしているかは、上述したレジストリキーでは確認することができません。 しかし、次に示すCryptSIPRetrieveSubjectGuidForCatalogFileを呼び出せば、 指定したファイル名またはファイルハンドルから、それをサポートするSIPのサブジェクトGUIDが返されることになっています。

BOOL WINAPI CryptSIPRetrieveSubjectGuidForCatalogFile(
  LPCWSTR FileName,
  HANDLE hFileIn,
  GUID *pgSubject
);

FileNameは、署名を取得または格納しようとするファイルのパスを指定します。 この引数を利用する場合は、hFileInにNULLを指定することができます。 hFileInは、署名を取得または格納しようとするファイルのハンドルを指定します。 この引数を利用する場合は、FileNameにNULLを指定することができます。 pgSubjectは、指定したファイルをサポートするサブジェクトGUIDが返ります。

今回のプログラムは、CryptSIPRetrieveSubjectGuidForCatalogFileが返したGUIDを表示します。

#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;
	WCHAR szGuid[256];

	if (!CryptSIPRetrieveSubjectGuidForCatalogFile(szFileName, NULL, &guidSubject))
		return 0;
	
	StringFromGUID2(guidSubject, szGuid, sizeof(szGuid) / sizeof(GUID));

	MessageBoxW(NULL, szGuid, TEXT("OK"), MB_OK);
	
	return 0;
}

SIPの関数を呼び出すには、mssip.hのインクルードが必要です。 CryptSIPRetrieveSubjectGuidForCatalogFileが返したGUIDを確認すると、 それが実際にレジストリキーとして存在することが分かりますが、 関数が成功したからといって、そのファイル形式をサポートできるとは限らないことに注意してください。 どうやら、サポートできないにも関わらず、GUIDが返ることがあるようです。 また、.msiのMSIファイルに関しては、サポートできるSIPが存在するにも関わらず、 そのSIPのサブジェクトGUIDではないGUIDが返ってくるようです。 .msiをサポートするGUIDはレジストリエディタから推測できるため、 次のようなコードを書くことができます。

if (CLSIDFromString(L"{000C10F1-0000-0000-C000-000000000046}", &guidSubject) != NOERROR)
	return 0;

このコードでは、.msiのGUID文字列をGUIDに変換しています。 .msiファイルを対象する場合は、CryptSIPRetrieveSubjectGuidForCatalogFileの変わりに、 このようなコードを実行することになるでしょう。


戻る