EternalWindows
PKCS #7 / EnvelopedData型と複合化

EnvelopedData型の暗号化データを複合化するには、CryptDecryptMessageを呼び出します。

BOOL WINAPI CryptDecryptMessage(
  PCRYPT_DECRYPT_MESSAGE_PARA pDecryptPara,
  const BYTE *pbEncryptedBlob,
  DWORD cbEncryptedBlob,
  BYTE *pbDecrypted,
  DWORD *pcbDecrypted,
  PCCERT_CONTEXT *ppXchgCert
);

pDecryptParaは、複合化情報を格納するCRYPT_DECRYPT_MESSAGE_PARA構造体のアドレスを指定します。 pbEncryptedBlobは、暗号化されたデータを指定します。 cbEncryptedBlobは、暗号化されたデータのサイズを指定します。 pbDecryptedは、復号化されたデータを受け取るバッファを指定します。 pcbDecryptedは、復号化されたデータのサイズを格納する変数のアドレスを指定します。 pbDecryptedがNULLの場合、データのサイズを受け取ることになります。 ppXchgCertは、複合化に使用した秘密鍵に関連する公開鍵を含む証明書コンテキストが返ります。 不要な場合は、NULLを指定することができます。

CRYPT_DECRYPT_MESSAGE_PARA構造体の定義を次に示します。

typedef struct _CRYPT_DECRYPT_MESSAGE_PARA {
  DWORD                       cbSize;
  DWORD                       dwMsgAndCertEncodingType;
  DWORD                       cCertStore;
  HCERTSTORE*                 rghCertStore;
#ifdef CRYPT_DECRYPT_MESSAGE_PARA_HAS_EXTRA_FIELDS
  DWORD                       dwFlags;
#endif
} CRYPT_DECRYPT_MESSAGE_PARA,  *PCRYPT_DECRYPT_MESSAGE_PARA;

cbSizeは、構造体のサイズをバイト単位で指定します。 dwMsgAndCertEncodingTypeは、エンコーディングタイプを指定します。 PKCS #7形式で暗号化されデータを複合化するには、PKCS_7_ASN_ENCODINGを指定します。 cCertStoreは、rghCertStoreの要素数を指定します。 rghCertStoreは、証明書ストアの配列を指定します。 この証明書ストアからデータを複合化できる証明書が検索され、 その証明書に関連付けられた秘密鍵が使用されることになります。 実際に参照された証明書は、CryptDecryptMessageの最終引数に返ります。 dwFlagsは、CRYPT_MESSAGE_SILENT_KEYSET_FLAGという定数を指定することができます。 この定数を指定すると、CSPによって作成されたUIが表示される可能性がある場合に、 関数を失敗させることができます。 dwFlagsを利用する場合は、CRYPT_DECRYPT_MESSAGE_PARA_HAS_EXTRA_FIELDSという 定数を自ら定義する必要があります。

今回のプログラムは、前節で作成したencrypt.datのデータを読み取り、 そのデータをCryptDecryptMessageで複合化します。

#include <windows.h>

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

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
{
	HANDLE                     hFile;
	DWORD                      dwReadByte;
	CRYPT_DECRYPT_MESSAGE_PARA decryptPara;
	HCERTSTORE                 hStore;
	BOOL                       bResult;
	LPBYTE                     lpEncryptedBlob;
	DWORD                      dwEncryptedBlobSize;
	LPBYTE                     lpData;
	DWORD                      dwDataSize;

	hFile = CreateFile(TEXT("encrypt.dat"), GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
	if (hFile == INVALID_HANDLE_VALUE)
		return 0;

	dwEncryptedBlobSize = GetFileSize(hFile, NULL);
	lpEncryptedBlob = (LPBYTE)HeapAlloc(GetProcessHeap(), 0, dwEncryptedBlobSize);
	ReadFile(hFile, lpEncryptedBlob, dwEncryptedBlobSize, &dwReadByte, NULL);
	CloseHandle(hFile);

	hStore = CertOpenSystemStore(0, TEXT("MY"));
	if (hStore == NULL) {
		HeapFree(GetProcessHeap(), 0, lpEncryptedBlob);
		return 0;
	}
	
	decryptPara.cbSize                   = sizeof(CRYPT_DECRYPT_MESSAGE_PARA);
	decryptPara.dwMsgAndCertEncodingType = PKCS_7_ASN_ENCODING;
	decryptPara.cCertStore               = 1;
	decryptPara.rghCertStore             = &hStore;

	bResult = CryptDecryptMessage(&decryptPara, lpEncryptedBlob, dwEncryptedBlobSize, NULL, &dwDataSize, NULL);
	if (!bResult) {
		MessageBox(NULL, TEXT("複合化に失敗しました。"), NULL, MB_ICONWARNING);
		HeapFree(GetProcessHeap(), 0, lpEncryptedBlob);
		CertCloseStore(hStore, CERT_CLOSE_STORE_CHECK_FLAG);
		return 0;
	}

	lpData = (LPBYTE)HeapAlloc(GetProcessHeap(), 0, dwDataSize);
 
	bResult = CryptDecryptMessage(&decryptPara, lpEncryptedBlob, dwEncryptedBlobSize, lpData, &dwDataSize, NULL);
	if (!bResult) {
		MessageBox(NULL, TEXT("複合化に失敗しました。"), NULL, MB_ICONWARNING);
		HeapFree(GetProcessHeap(), 0, lpEncryptedBlob);
		HeapFree(GetProcessHeap(), 0, lpData);
		CertCloseStore(hStore, CERT_CLOSE_STORE_CHECK_FLAG);
		return 0;
	}

	MessageBox(NULL, (LPTSTR)lpData, TEXT("OK"), MB_OK);

	HeapFree(GetProcessHeap(), 0, lpEncryptedBlob);
	HeapFree(GetProcessHeap(), 0, lpData);
	CertCloseStore(hStore, CERT_CLOSE_STORE_CHECK_FLAG);

	return 0;
}

前節で作成したencrypt.datには暗号化データのみを書き込んでいたため、 GetFileSizeが返した値を暗号化データのサイズとすることができます。 そして、このサイズだけファイルを読み取れば、暗号化データを取得できます。 CryptDecryptMessageの呼び出しに関わるコードを次に示します。

hStore = CertOpenSystemStore(0, TEXT("MY"));
if (hStore == NULL) {
	HeapFree(GetProcessHeap(), 0, lpEncryptedBlob);
	return 0;
}

decryptPara.cbSize                   = sizeof(CRYPT_DECRYPT_MESSAGE_PARA);
decryptPara.dwMsgAndCertEncodingType = PKCS_7_ASN_ENCODING;
decryptPara.cCertStore               = 1;
decryptPara.rghCertStore             = &hStore;

bResult = CryptDecryptMessage(&decryptPara, lpEncryptedBlob, dwEncryptedBlobSize, NULL, &dwDataSize, NULL);

rghCertStoreに指定する証明書ストアは、前節の暗号化アプリケーションが手にした証明書を格納している必要があります。 また、その証明書には秘密鍵が関連付けられていなければなりません。 そのような証明書は、基本的にMY証明書ストアに存在しますから、 CertOpenSystemStoreの第2引数には"MY"を指定しています。 このCryptDecryptMessageの呼び出しで複合化されたときのデータのサイズが取得でき、 後はその分のメモリを確保すれば、2回目の呼び出しで複合化されたデータを取得できます。


戻る