前のページ <<<
5.4 暗号化データとCryptoAPI
Crypto APIは、RFC3852で規定されたCMSデータを取り扱います(4.3電子署名データの形式および4.4 暗号化データの形式参照)。電子署名のデータは、Signed-data型が実装されています。また、暗号データは、Enveloped-data型が実装されています。ここでは、暗号データのEnveloped-data型を説明します。
このデータ形式を理解しますと、暗号化されたデータだけではなく、それ以外の様々な情報を追加することができるようになります。
このデータ形式を理解しますと、暗号化されたデータだけではなく、それ以外の様々な情報を追加することができるようになります。
5.4.1 暗号データの生成と概要
まず暗号化されたデータを用意し、その内容を順に解析します。
簡単な電子署名の方法は、2.4電子証明書を使った暗号化 で説明しています。ここでは、同様のコードを使って暗号データを生成し、その内容を簡単に解析します。
暗号データを生成するコードは、こちらから確認してください。ここでは、その結果のみを記します。
なお、このコードは、共通関数を使用し、さらにCrypt32.lib, Cryptui.libを使用します。
結果(暗号化されたデータの例)は、以下のとおりです。
先頭のオクテットが0x30ですので、SEQUENCEデータであることがわかります。そのデータ長は、0x82、0x01、0x40の3オクテットからわかります。先頭の0x82は、それに続く2オクテットでデータ長を表すことを示しています。従いまして、データ長は、0x0140オクテットとなり、第5オクテットから最後までがこのデータ(ContentInfo情報)ということになります。ContentInfoは、contentTypeとcontentで構成されています。5番目のオクテットは、0x06ですのでオブジェクト識別子です。これをデコードしますと、"1.2.840.113549.1.7.3"となります。このOIDは、Enveloped-Dataであることを示しています。contentは、コンテキスト番号0(ゼロ、値は、0xa0)で示され、0x10オクテット目がそのデータの開始位置です。
contentは、SEQUENCEですので、0x10オクテット目を0x30と読み替えます。すると、データ長が0x0131オクテットであることがわかりますので、残りの全てがcontentであることがわかります。
contentは、4.4.4Enveloped-data型で説明されていますようなデータ型で構成されています。順番に1オクテットづつ解析することで全てのデータを解析できます。しかしながら、CryptoAPIには、このデータから、必要な情報を取り出す関数(CryptGetMsgParam)が用意されています。以下では、CryptGetMsgParamを使って暗号化データから情報を取り出す方法を説明します。
簡単な電子署名の方法は、2.4電子証明書を使った暗号化 で説明しています。ここでは、同様のコードを使って暗号データを生成し、その内容を簡単に解析します。
暗号データを生成するコードは、こちらから確認してください。ここでは、その結果のみを記します。
なお、このコードは、共通関数を使用し、さらにCrypt32.lib, Cryptui.libを使用します。
結果(暗号化されたデータの例)は、以下のとおりです。
Value = 0000 30 82 01 40 06 09 2a 86 48 86 f7 0d 01 07 03 a0 0..@..*.H....... 0010 82 01 31 30 82 01 2d 02 01 00 31 81 ef 30 81 ec ..10..-...1..0.. 0020 02 01 00 30 55 30 47 31 12 30 10 06 0a 09 92 26 ...0U0G1.0.....& 0030 89 93 f2 2c 64 01 19 16 02 6a 70 31 17 30 15 06 ...,d....jp1.0.. 0040 0a 09 92 26 89 93 f2 2c 64 01 19 16 07 54 72 75 ...&...,d....Tru 0050 73 74 53 53 31 18 30 16 06 03 55 04 03 13 0f 54 stSS1.0...U....T 0060 72 75 73 74 53 6f 66 74 53 79 73 43 41 38 02 0a rustSoftSysCA8.. 0070 61 1b 41 e7 00 00 00 00 00 09 30 0d 06 09 2a 86 a.A.......0...*. 0080 48 86 f7 0d 01 01 01 05 00 04 81 80 5d fc 50 49 H...........].PI 0090 6b ec e9 69 0a 24 92 ec d4 c5 5e c3 5e 5a 97 d2 k..i.$....^.^Z.. 00a0 b6 d7 3d 9b 42 32 42 d3 06 c0 4c 5c 3b 95 5b 9c ..=.B2B...L\;.[. 00b0 b1 f4 60 bb a5 c3 e2 6e 32 43 f6 5e ce 86 fe 36 ..`....n2C.^...6 00c0 6e 2c 09 f3 9c 7f e8 ec f0 88 f2 52 17 4b 9e ed n,.........R.K.. 00d0 f6 3c e5 5b b6 9a da 96 1d c9 94 72 ff 79 96 ca .<.[.......r.y.. 00e0 56 74 bb ec 6b b8 b8 56 a1 0d 01 55 3d 71 83 b5 Vt..k..V...U=q.. 00f0 a4 ed e3 02 74 42 a3 2b a6 5e 28 3c f8 e0 f9 c5 ....tB.+.^(<.... 0100 b5 c0 1d 39 4b ba d2 8d 45 08 fd a0 30 36 06 09 ...9K...E...06.. 0110 2a 86 48 86 f7 0d 01 07 01 30 0c 06 08 2a 86 48 *.H......0...*.H 0120 86 f7 0d 03 04 05 00 80 1b 04 6c 18 63 64 b1 7d ..........l.cd.} 0130 22 4e ad 17 09 82 66 61 3e 3b b5 36 07 35 ab c0 "N....fa>;.6.5.. 0140 ba ab 3e e3 ..>.得られたデータを解析します。
先頭のオクテットが0x30ですので、SEQUENCEデータであることがわかります。そのデータ長は、0x82、0x01、0x40の3オクテットからわかります。先頭の0x82は、それに続く2オクテットでデータ長を表すことを示しています。従いまして、データ長は、0x0140オクテットとなり、第5オクテットから最後までがこのデータ(ContentInfo情報)ということになります。ContentInfoは、contentTypeとcontentで構成されています。5番目のオクテットは、0x06ですのでオブジェクト識別子です。これをデコードしますと、"1.2.840.113549.1.7.3"となります。このOIDは、Enveloped-Dataであることを示しています。contentは、コンテキスト番号0(ゼロ、値は、0xa0)で示され、0x10オクテット目がそのデータの開始位置です。
contentは、SEQUENCEですので、0x10オクテット目を0x30と読み替えます。すると、データ長が0x0131オクテットであることがわかりますので、残りの全てがcontentであることがわかります。
contentは、4.4.4Enveloped-data型で説明されていますようなデータ型で構成されています。順番に1オクテットづつ解析することで全てのデータを解析できます。しかしながら、CryptoAPIには、このデータから、必要な情報を取り出す関数(CryptGetMsgParam)が用意されています。以下では、CryptGetMsgParamを使って暗号化データから情報を取り出す方法を説明します。
5.4.2 データ解析の準備 (CryptMsgOpenToDecode)
まず暗号化したデータを解析するためのインスタンスを生成します。
関数には、以下のようにデータを設定します。
第1引数には、エンコードの形式を設定します。
第2引数には、指定する必要がありませんので0(ゼロ)にします。
第3引数には、メッセージタイプは、ヘッダーから取り出せますので、0(ゼロ)を指定
第4引数には、CSPを指定します。
第5引数は、将来への予約です。
第6引数は、ストリーミングの場合に指定します。
なお、暗号化したデータに様々な情報を追加する場合は、Enveloped-DataのUnprotectedDataにセットされます、この場合は、CryptMsgOpenToEncode関数を使ってオブジェクトをオープンしますが、この方法は別項で説明します。
// 暗号化データの解析準備 HCRYPTMSG hMsg; if(!(hMsg = CryptMsgOpenToDecode( X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, 0, hProv, NULL, NULL))) { fprintf(stderr, "CryptMsgOpenToDecode error\n"); return 4; }HCRYPTMSGは、CMSデータ解析用オブジェクトのハンドルです。このhMsgを介してデータをセットし、デコードした情報を取り出します。
関数には、以下のようにデータを設定します。
第1引数には、エンコードの形式を設定します。
第2引数には、指定する必要がありませんので0(ゼロ)にします。
第3引数には、メッセージタイプは、ヘッダーから取り出せますので、0(ゼロ)を指定
第4引数には、CSPを指定します。
第5引数は、将来への予約です。
第6引数は、ストリーミングの場合に指定します。
なお、暗号化したデータに様々な情報を追加する場合は、Enveloped-DataのUnprotectedDataにセットされます、この場合は、CryptMsgOpenToEncode関数を使ってオブジェクトをオープンしますが、この方法は別項で説明します。
5.4.3 暗号化データのセット (CryptMsgUpdate)
5.4.1で取り出したデータを利用します。このデータは、以下の変数に格納されています。
BYTE *pbEncBlob; // 暗号化データのポインター DWORD cbEncBlob; // 暗号化データのバイト数以下のコードを使って、暗号データをセットします。これによって、暗号化されたデータは、必要な情報に変換されます。
// 暗号化データのセット if(!CryptMsgUpdate(hMsg, pbEncBlob, cbEncBlob, TRUE)) { fprintf(stderr, "CryptMsgUpdate error\n"); return 5; }
5.4.4 受取人数 (Recipients count)
暗号化されたデータの受取人数を取り出します。
受取人とは、暗号化されたデータを復号するための私有鍵を所持している者を言います。暗号化されたデータは、一人だけが復号できるデータではなく、指定した複数の者によって復号できるように設定できます。暗号化されたデータには、この受け取り者の人数が含まれていますので、その人数を確認します。
CryptoAPIは、独自の受取人情報の他にCMSの受取人情報(RecipientInfo)を取り出せます。ここでは、CMSの受取人情報を対象にします。
第1引数には、CMSデータ解析用オブジェクトのハンドルを指定します。
第2引数には、受取人を取り出すためのフラグCMSG_CMS_RECIPIENT_COUNT_PARAMを指定します。
第3引数には、インデックスを指定しますが、今回は不要ですので、0を指定します。
第4引数には、受取人数が入るDWORD型の領域を指定します。
第5引数には、受取人数が入る領域のサイズが戻ります。
上記の例の場合は、受取人を1人(自分自身)だけ指定していますので
受取人とは、暗号化されたデータを復号するための私有鍵を所持している者を言います。暗号化されたデータは、一人だけが復号できるデータではなく、指定した複数の者によって復号できるように設定できます。暗号化されたデータには、この受け取り者の人数が含まれていますので、その人数を確認します。
CryptoAPIは、独自の受取人情報の他にCMSの受取人情報(RecipientInfo)を取り出せます。ここでは、CMSの受取人情報を対象にします。
// 暗号化データから、受取人数を取り出す DWORD rcData; DWORD cbData; if(!CryptMsgGetParam( hMsg, CMSG_CMS_RECIPIENT_COUNT_PARAM, 0, &rcData, &cbData)) { fprintf(stderr, "CryptMsgGetParam error1\n"); return 6; } printf("Recipient Count = %d\n", rcData);関数には、以下のように設定します。
第1引数には、CMSデータ解析用オブジェクトのハンドルを指定します。
第2引数には、受取人を取り出すためのフラグCMSG_CMS_RECIPIENT_COUNT_PARAMを指定します。
第3引数には、インデックスを指定しますが、今回は不要ですので、0を指定します。
第4引数には、受取人数が入るDWORD型の領域を指定します。
第5引数には、受取人数が入る領域のサイズが戻ります。
上記の例の場合は、受取人を1人(自分自身)だけ指定していますので
Recipient Count = 1という結果になります。
5.4.5 受取人情報 (RecipientInfo)
CMSの受取人情報を取り出します。
フラグに、CMSG_CMS_RECIPIENT_INFO_PARAMを指定して取り出します。
第1引数には、CMSデータ解析用オブジェクトのハンドルを指定します。
第2引数には、フラグ CMSG_CMS_RECIPIENT_INFO_PARAM を指定します。
第3引数には、インデックスを指定しますが、不要ですので、0を指定します。
第4引数には、受取人情報が入るBYTEのデータ領域を指定します。
第5引数には、受け取るデータのサイズが戻ります。
構造体CMSG_CMS_RECIPIENT_INFO_PARAMは、以下のように定義されています。
フラグに、CMSG_CMS_RECIPIENT_INFO_PARAMを指定して取り出します。
BYTE *ciData; CryptMsgGetParam( hMsg, CMSG_CMS_RECIPIENT_INFO_PARAM, 0, ciData, &cbData);関数の
第1引数には、CMSデータ解析用オブジェクトのハンドルを指定します。
第2引数には、フラグ CMSG_CMS_RECIPIENT_INFO_PARAM を指定します。
第3引数には、インデックスを指定しますが、不要ですので、0を指定します。
第4引数には、受取人情報が入るBYTEのデータ領域を指定します。
第5引数には、受け取るデータのサイズが戻ります。
構造体CMSG_CMS_RECIPIENT_INFO_PARAMは、以下のように定義されています。
typedef struct _CMSG_CMS_RECIPIENT_INFO { DWORD dwRecipientChoice; union { // CMSG_KEY_TRANS_RECIPIENT PCMSG_KEY_TRANS_RECIPIENT_INFO pKeyTrans; // CMSG_KEY_AGREE_RECIPIENT PCMSG_KEY_AGREE_RECIPIENT_INFO pKeyAgree; // CMSG_MAIL_LIST_RECIPIENT PCMSG_MAIL_LIST_RECIPIENT_INFO pMailList; }; } CMSG_CMS_RECIPIENT_INFO, *PCMSG_CMS_RECIPIENT_INFO;この情報の詳細は、4.4.5受取人情報を確認してください。
5.4.6 受取人数 (RecipientsCount)
CryptoAPIを使って暗号データを復号するためには、CryptoAPI用の受取人情報を取り出す必要があります。
まずは、受取人数を取り出します。
第1引数には、CMSデータ解析用オブジェクトのハンドルを指定します。
第2引数には、フラグ CMSG_RECIPIENT_COUNT_PARAM を設定します。
第3引数には、0を設定
第4引数には、人数の入るDWORDのポインターをしています。
第5引数には、DWORDのサイズが戻ります。
受取人は、自分自身だけですので、Recipients Count = 1 となります。
まずは、受取人数を取り出します。
// 暗号化データから、受取人数を取り出す (RecipientsCount) if(!CryptMsgGetParam( hMsg, CMSG_RECIPIENT_COUNT_PARAM, 0, &rcData, &cbData)) { fprintf(stderr, "CryptMsgGetParam error1\n"); return 6; } printf("Recipients Count = %d\n", rcData);関数の、
第1引数には、CMSデータ解析用オブジェクトのハンドルを指定します。
第2引数には、フラグ CMSG_RECIPIENT_COUNT_PARAM を設定します。
第3引数には、0を設定
第4引数には、人数の入るDWORDのポインターをしています。
第5引数には、DWORDのサイズが戻ります。
受取人は、自分自身だけですので、Recipients Count = 1 となります。
5.4.7 受取人情報 (RecipientInfo)
CryptoAPIでは、受取人情報として、受取人の電子証明書情報を取り出すことができます。
第1引数には、 CMSデータ解析用オブジェクトのハンドルを指定します。
第2引数には、フラグ CMSG_RECIPIENT_INFO_PARAM を設定します。
第3引数には、0を設定
第4引数には、受取人情報が入るBYTEの領域を指定します。
第5引数には、データのサイズが戻ります。
受け取ったデータは、CERT_INFOの配列のポインターですので型キャストすれば内容を確認できます。ただし、ここで受け取るCERT_INFO情報は、発行者(Issuer)とシリアル番号(Serial Number)だけが有効な情報です。それぞれの情報は、5.1.7発行者名および5.1.5シリアル番号の表示で確認できます。
// 暗号化データから、受取人情報を取り出す (RecipientInfo) PCERT_INFO pci; CryptMsgGetParam(hMsg, CMSG_RECIPIENT_INFO_PARAM, 0, ciData, &cbData); pci = (CERT_INFO*)ciData;関数の、
第1引数には、 CMSデータ解析用オブジェクトのハンドルを指定します。
第2引数には、フラグ CMSG_RECIPIENT_INFO_PARAM を設定します。
第3引数には、0を設定
第4引数には、受取人情報が入るBYTEの領域を指定します。
第5引数には、データのサイズが戻ります。
受け取ったデータは、CERT_INFOの配列のポインターですので型キャストすれば内容を確認できます。ただし、ここで受け取るCERT_INFO情報は、発行者(Issuer)とシリアル番号(Serial Number)だけが有効な情報です。それぞれの情報は、5.1.7発行者名および5.1.5シリアル番号の表示で確認できます。
5.4.8 ご質問・ご要望
ご質問やご要望は、こちらからお送りください。(匿名でも可能です。)