2.4 電子証明書を使った暗号化
公開鍵(非対称鍵)を使った暗号化の方法を説明します。また、暗号化されたデータの形式は、RFC3852によって標準化されています。ここでは、公開鍵やデータ形式を簡単に説明した後に実際の暗号化の方法を説明します。
2.4.1 公開鍵について
公開鍵(非対称鍵)暗号方式とは、暗号化と復号で違う鍵を使って行う暗号方式です。暗号用の鍵と、復号用の鍵が対となることから、2つの鍵を「鍵ペア」と呼びます。
この鍵ペアのうち、一方を「私有鍵(秘密鍵)」他方を「公開鍵」といいます。
読んでで字のごとく、「私有鍵(秘密鍵)」は所有者以外が利用できないように秘匿しておく鍵のことです。「公開鍵」は、誰でも利用できるような場所に公開しておく鍵です。
私有鍵(秘密鍵)で暗号化すると、公開鍵以外では復号することができません。同様に、公開鍵で暗号化すると、私有鍵(秘密鍵)以外では復号することができません。
これを利用して、前者を「電子証明」に、後者を「暗号化」に利用します。
この鍵ペアのうち、一方を「私有鍵(秘密鍵)」他方を「公開鍵」といいます。
読んでで字のごとく、「私有鍵(秘密鍵)」は所有者以外が利用できないように秘匿しておく鍵のことです。「公開鍵」は、誰でも利用できるような場所に公開しておく鍵です。
私有鍵(秘密鍵)で暗号化すると、公開鍵以外では復号することができません。同様に、公開鍵で暗号化すると、私有鍵(秘密鍵)以外では復号することができません。
これを利用して、前者を「電子証明」に、後者を「暗号化」に利用します。
2.4.2 暗号化における公開鍵と私有鍵
暗号化を公開鍵(私有鍵ではなく)で行うのは、その暗号化されたデータを私有鍵を所持する者以外が復号できなくするためです。例えば、Aさんの公開鍵は誰でも使えますので、Aさん向けの暗号化データは誰でも作れます。ところがそのデータは、出来上がったデータがAさんに届いて、Aさんの私有鍵を使ったときだけデータを復号できます。Aさん以外の誰もが、復号できませんので、そのデータは安全であるということになります。
ところがこのようにして、暗号化データを作成しますと、暗号化データを復号する人用に違った暗号化データを作成しなければならなくなり大変な作業(データ量)になってしまいます。
そこで、実際は、暗号化すべきデータ(本文)を共通鍵暗号化(暗号化と復号で同じ鍵を使う暗号方式)で暗号化し、その暗号化に使った共通鍵を(暗号化データを)受け取る人の公開鍵で暗号化し、その暗号化データとともに渡します。このとき、暗号化データを受け取る人を「受取人(Recipient)」といいます。
受取人は自身の私有鍵を使って、暗号化された共通鍵を復号し、その復号された鍵を用い本文を復号します。この方法によれば、本文を共通にし、たくさんの受取人用に暗号化した共通鍵を作成すればよいことになります。
ところがこのようにして、暗号化データを作成しますと、暗号化データを復号する人用に違った暗号化データを作成しなければならなくなり大変な作業(データ量)になってしまいます。
そこで、実際は、暗号化すべきデータ(本文)を共通鍵暗号化(暗号化と復号で同じ鍵を使う暗号方式)で暗号化し、その暗号化に使った共通鍵を(暗号化データを)受け取る人の公開鍵で暗号化し、その暗号化データとともに渡します。このとき、暗号化データを受け取る人を「受取人(Recipient)」といいます。
受取人は自身の私有鍵を使って、暗号化された共通鍵を復号し、その復号された鍵を用い本文を復号します。この方法によれば、本文を共通にし、たくさんの受取人用に暗号化した共通鍵を作成すればよいことになります。
2.4.3 電子署名における公開鍵と私有鍵
電子署名は、暗号化されたデータを誰もが復号できなければなりません。また、署名した人を後々に特定できなければなりませんし、その人以外が署名できないことを立証できなければなりません。
電子署名の場合は、データそのものを暗号化する必要はありません。そのため、本文自体は暗号化されません。暗号化されるのは、本文から算出されたハッシュ値です。
ハッシュ値とは、データを16バイトや20バイト程度のデータに非可逆圧縮したデータで、違うデータから同じハッシュ値が算出させる確率が非常に低い計算で算出された値です。
電子署名では、このハッシュ値を署名者(電子署名する人)の私有鍵で暗号化します。その署名を検証(署名の真正性を確認すること)する場合は、公開されている署名者の公開鍵で暗号化データを復号し、本文から計算されたハッシュ値と比較します。
違うデータから、同じハッシュ値が算出される可能性が極めて低いので、2つのハッシュ値が同一であれば、そのハッシュ値の暗号化データが、署名者によって作成されたことを立証できます。それは、その暗号化データを作成できるのは、私有鍵を所有する署名者以外にいないためです。
このように、電子署名は、署名者自身が署名したことを立証するとともに、署名者以外に電子署名を行うことができないことも立証します。
電子署名の場合は、データそのものを暗号化する必要はありません。そのため、本文自体は暗号化されません。暗号化されるのは、本文から算出されたハッシュ値です。
ハッシュ値とは、データを16バイトや20バイト程度のデータに非可逆圧縮したデータで、違うデータから同じハッシュ値が算出させる確率が非常に低い計算で算出された値です。
電子署名では、このハッシュ値を署名者(電子署名する人)の私有鍵で暗号化します。その署名を検証(署名の真正性を確認すること)する場合は、公開されている署名者の公開鍵で暗号化データを復号し、本文から計算されたハッシュ値と比較します。
違うデータから、同じハッシュ値が算出される可能性が極めて低いので、2つのハッシュ値が同一であれば、そのハッシュ値の暗号化データが、署名者によって作成されたことを立証できます。それは、その暗号化データを作成できるのは、私有鍵を所有する署名者以外にいないためです。
このように、電子署名は、署名者自身が署名したことを立証するとともに、署名者以外に電子署名を行うことができないことも立証します。
2.4.4 電子証明書の準備
暗号化に利用する電子証明書を準備します。復号するには、この電子証明書と対となる私有鍵が必要です。私有鍵がない場合は、暗号化したデータを復号できませんので注意してください。
ここでは、私有鍵を所持している自分自身の電子証明書を使って暗号化を実施します。
電子証明書を "MY" 証明書ストアから取り出しておいてください。
ここでは、私有鍵を所持している自分自身の電子証明書を使って暗号化を実施します。
電子証明書を "MY" 証明書ストアから取り出しておいてください。
参考:
電子証明書を取り出す方法は、2.3.3電子証明書を取り出す を参照してください。
GUIを使って電子証明書を取り出す場合は、2.5.3電子証明書を選択する を参照してください。
WindowsXP、WindowsVista以外でGUIを利用する場合は、Windows98で利用可能なダイアログを参照してください。
電子証明書を取り出す方法は、2.3.3電子証明書を取り出す を参照してください。
GUIを使って電子証明書を取り出す場合は、2.5.3電子証明書を選択する を参照してください。
WindowsXP、WindowsVista以外でGUIを利用する場合は、Windows98で利用可能なダイアログを参照してください。
2.4.5 暗号化の準備
データを暗号化するためには、その詳細な情報をパラメーターに準備します。
暗号化のパラメーターに"szOID_RSA_RC4"をセットしています。これは、""1.2.840.113549.3.4""という文字列で共通鍵暗号化方式を示すOID(オブジェクト識別子)です。
この共通鍵暗号化に使用した共通鍵を受取人の公開鍵で暗号化されます、サンプルには、1名の受取人がしてされていますが、2名以上であればそれそれの公開鍵を使って暗号化され、それらが暗号化データに含まれます。
暗号化を実施しますので、暗号化のパラメーターには、CSPのハンドルが含まれます。
// 暗号化の準備 PCCERT_CONTEXT recipients[1]; // 受取人の電子証明書 CRYPT_ALGORITHM_IDENTIFIER encAlg; // 暗号化のアルゴリズム CRYPT_ENCRYPT_MESSAGE_PARA para; // 暗号化のパラメーター recipients[0] = pcCert; // 受取人リストに電子証明書をセットする memset(&encAlg, 0, sizeof(encAlg)); encAlg.pszObjId = szOID_RSA_RC4; // 暗号化の方式として、RC4を設定 memset(¶, 0, sizeof(para)); para.cbSize = (DWORD)sizeof(para); para.dwMsgEncodingType = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING; para.hCryptProv = hProv; // CSPのハンドル para.ContentEncryptionAlgorithm = encAlg; // データを暗号化するアルゴリズム準備が必要なのは、受取人(2.4.2を参照してください)の電子証明書情報、と暗号化のパラメータです。
暗号化のパラメーターに"szOID_RSA_RC4"をセットしています。これは、""1.2.840.113549.3.4""という文字列で共通鍵暗号化方式を示すOID(オブジェクト識別子)です。
この共通鍵暗号化に使用した共通鍵を受取人の公開鍵で暗号化されます、サンプルには、1名の受取人がしてされていますが、2名以上であればそれそれの公開鍵を使って暗号化され、それらが暗号化データに含まれます。
暗号化を実施しますので、暗号化のパラメーターには、CSPのハンドルが含まれます。
2.4.6 暗号化
データを公開鍵で暗号化します。暗号化の関数を2回コールします。1回目は、暗号化データのサイズがわかりませんので、それを知るためにコールします。この結果で、暗号化データの格納領域を生成してから、2回目をコールします。これによって、暗号化データは確保された格納領域に生成されます。
第1引数には、先に準備したパラメータを指定します。
第2引数には、受取人の数を指定します。
第3引数には、指定した受け取り人数に相当する電子証明書のリストを渡します。
第4引数には、暗号化される(平文)データ
第5引数には、そのバイト数
第6引数には、暗号化されたデータを格納する領域のポインター
第7引数には、暗号化されたデータのバイト数が返されます。
返されるデータのサイズがわからないので、「関数から戻されるデータの大きさがわからないとき」で説明しました方法で、データを受け取ります。
暗号化したデータは、pEencBlobに格納されています。.サンプルコードでは、暗号化データのサイズを表示しています。原文の大きさに比べて大変大きな値になっています。これは、このデータがRFC3852(Cryptographic Message Syntax;CMS)によって既定されたデータ形式にエンコードされているためです。
この形式で暗号化されたデータであれば、暗号化したアプリケーションでなくとも、復号できるようなデータ形式の規格です。
RFCでは、共通鍵で暗号化されたデータもその形式が既定されています。しかし、CryptoAPIでは、そのようなデータを直接作成できません。ちなみに、CryptEncrypt 関数は、単純に暗号化されたデータを生成しますが、データ形式はRFCに準拠したものではありません。
// 暗号化 BYTE *pbCont = (BYTE*)"このデータを暗号化します。"; //暗号化されるデータ DWORD cbCont = (DWORD)strlen((char*)pbCont) + 1; //データのサイズ BYTE *pbEncBlob; //暗号化データの格納域 DWORD cbEncBlob; //暗号化データのサイズ if(!CryptEncryptMessage(¶, // 暗号化パラメーター 1, // 受取人の数 recipients, // 受取人の証明書アレー pbCont, // 暗号化されるデータ cbCont, // そのサイズ NULL, // 暗号化データのサイズが // わからないのでNULLを指定 &cbEncBlob)) // 暗号化データのサイズが戻される { fprintf(stderr, "暗号化に失敗しました。\n"); return 4; } // 暗号化データの格納領域を確保してから再度暗号化を実施 pbEncBlob = new BYTE [cbEncBlob]; if(!CryptEncryptMessage(¶, // 暗号化パラメーター 1, // 受取人の数 recipients, // 受取人の証明書アレー pbCont, // 暗号化されるデータ cbCont, // そのサイズ pbEncBlob, // 暗号化データの格納域ポインター &cbEncBlob)) // 暗号化データのサイズが戻される { fprintf(stderr, "暗号化に失敗しました。\n"); return 5; }CryptEncryptMessageには、暗号化されるデータを全て渡します。分割して渡すことはできません。
第1引数には、先に準備したパラメータを指定します。
第2引数には、受取人の数を指定します。
第3引数には、指定した受け取り人数に相当する電子証明書のリストを渡します。
第4引数には、暗号化される(平文)データ
第5引数には、そのバイト数
第6引数には、暗号化されたデータを格納する領域のポインター
第7引数には、暗号化されたデータのバイト数が返されます。
返されるデータのサイズがわからないので、「関数から戻されるデータの大きさがわからないとき」で説明しました方法で、データを受け取ります。
暗号化したデータは、pEencBlobに格納されています。.サンプルコードでは、暗号化データのサイズを表示しています。原文の大きさに比べて大変大きな値になっています。これは、このデータがRFC3852(Cryptographic Message Syntax;CMS)によって既定されたデータ形式にエンコードされているためです。
この形式で暗号化されたデータであれば、暗号化したアプリケーションでなくとも、復号できるようなデータ形式の規格です。
RFCでは、共通鍵で暗号化されたデータもその形式が既定されています。しかし、CryptoAPIでは、そのようなデータを直接作成できません。ちなみに、CryptEncrypt 関数は、単純に暗号化されたデータを生成しますが、データ形式はRFCに準拠したものではありません。
2.4.7 復号の準備
暗号化データを復号するためのパラメーターを準備します。
証明書ストアの配列には、復号できる私有鍵と対となる電子証明書が格納されているストアのハンドルを格納します。例の場合は、“MY”証明書ストアに証明書はありますので、そのストアを指定しました。
暗号化の際には、受取人や暗号化アルゴリズムを指定しましたが、復号ではそれらを必要としません。暗号化データには、受取人の情報や、暗号化アルゴリズムの情報が含まれていますので、パラメーターとして必要としません。
HCERTSTORE certStores[1]; // 証明書ストアの配列 CRYPT_DECRYPT_MESSAGE_PARA decPara; // 復号のパラメーター DWORD cbDecMsg; BYTE *pbDecMsg; certStores[0] = hStore; // 証明書ストアを配列に準備 memset(&decPara, 0, sizeof(decPara)); decPara.cbSize = sizeof(decPara); decPara.dwMsgAndCertEncodingType = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING; decPara.cCertStore = 1; // 準備した証明書ストアの数 decPara.rghCertStore = certStores; // 証明書ストアの配列暗号化に比較して、復号の場合は、準備する項目が少なくなります。
証明書ストアの配列には、復号できる私有鍵と対となる電子証明書が格納されているストアのハンドルを格納します。例の場合は、“MY”証明書ストアに証明書はありますので、そのストアを指定しました。
暗号化の際には、受取人や暗号化アルゴリズムを指定しましたが、復号ではそれらを必要としません。暗号化データには、受取人の情報や、暗号化アルゴリズムの情報が含まれていますので、パラメーターとして必要としません。
2.4.8 復号
// 復号 if(!CryptDecryptMessage( &decPara, // パラメーター pbEncBlob, // 暗号化データの格納ポインター cbEncBlob, // 暗号化データのサイズ NULL, // データの大きさがわからないのでNULL &cbDecMsg, // 大きさが戻る NULL)) // 暗号化されたデータに含まれている電子証明書 { fprintf(stderr, "復号に失敗しました。\n"); return 6; } // 復号したデータを格納する領域を確保して再度復号 pbDecMsg = new BYTE [cbDecMsg]; if(!CryptDecryptMessage( &decPara, pbEncBlob, cbEncBlob, pbDecMsg, // 確保した格納域を指定 &cbDecMsg, NULL)) { fprintf(stderr, "復号に失敗しました。\n"); return 7; } printf("復号したデータは、「%s」です。\n", pbDecMsg);復号の関数の
第1引数には、準備したパラメーターのポインター
第2引数には、暗号化データのポインター
第3引数には、暗号化データのバイト数
第4引数には、復号されたデータのポインター
第5引数には、復号されたデータのサイズを格納する領域のポインター
第6引数には、電子証明書のポインターを格納する領域のポインター
を指定します。
最後の電子証明書のポインターには、復号に使った鍵の公開鍵証明書のポインターが入ります。その証明書が不要な場合は、NULLを指定します。
2.4.9 後始末
確保したハンドルや、格納領域を全て開放します。
delete [] pbDecMsg; // 復号されたデータの格納領域 delete [] pbEncBlob; // 暗号化データの格納領域 CertFreeCertificateContext(pcCert); // 電子証明書のポインター CertCloseStore(hStore, 0); // 証明書ストアのハンドル CryptReleaseContext(hProv, 0); // CSPのハンドル電子証明書のハンドルは、サンプルでは1つだけですが、実際は2つ以上の場合もありますのですべてを開放します。
2.4.10 サンプルコード
2.4.11 ご質問・ご要望
ご質問やご要望は、こちらからお送りください。(匿名でも可能です。)
(記載の会社名および製品名は、各社の登録商標および商標です。)