前のページ <<<
1.14 ブロック暗号化アルゴリズムによる簡単な暗号化
ブロック暗号アルゴリズム AES(Advanced Encryption Standard)による暗号化の解説です。従来のCryptoAPIを使ったサンプルで説明します。
Microsoftは、CryptoAPIに替わる次世代暗号化(Cryptography Next Genaration; CNG)APIをサポートしています。CNG APIの解説[http://www.trsustss.co.jp/cng/0000.html]こちらでもAESでのブロック暗号化の解説をしています、ご参照ください。
C#(.NET Framework)での暗号化の手順は、サンプルと共に8.C#による簡単な暗号化で説明しています。
Microsoftは、CryptoAPIに替わる次世代暗号化(Cryptography Next Genaration; CNG)APIをサポートしています。CNG APIの解説[http://www.trsustss.co.jp/cng/0000.html]こちらでもAESでのブロック暗号化の解説をしています、ご参照ください。
C#(.NET Framework)での暗号化の手順は、サンプルと共に8.C#による簡単な暗号化で説明しています。
1.14.1 概要
ここでは、共通鍵によるAESブロック暗号化アルゴリズムを使った暗号化・復号の方法を解説します。ブロック暗号化は、被暗号化データをブロックサイズごとに切り返し暗号化します。なお、ストリーミング暗号化は簡単な暗号化[http://www.trustss.co.jp/smnEncrypt010.html]を参照してください。
AESブロック暗号化アルゴリズムによる暗号化は、以下の関数を使います。
AESブロック暗号化アルゴリズムによる暗号化は、以下の関数を使います。
- CryptAcquireContext
- CryptCreateHash
- CryptHashData
- CryptDeriveKey
- CryptSetKeyParam
- CryptEncrypt / CryptDecrypt
- CSPを準備する。
- ハッシュ計算の準備をする。
- パスワードからハッシュ値を算出する。
- 計算されたハッシュ値を鍵(AES暗号鍵)に変換する。
- 鍵および初期化ベクタを設定する。
- 目的のデータを暗号化(復号)する。
1.14.2 キーコンテナー を準備する
ストリーミング暗号化アルゴリズムと同様ですが、CSPはAESブロック暗号化が使えるものを選択します。詳しくは、簡単な暗号化[http://www.trustss.co.jp/smnEncrypt010.html]を参照してください。
ここでは、以下のCSPでAES暗号化アルゴリズムを使って説明します。
ここでは、以下のCSPでAES暗号化アルゴリズムを使って説明します。
"Microsoft AES Cryptographic Provider" Type: PROV_RSA_AES Name: MS_ENH_RSA_AES_PROV
#include <WinCrypt.h> // キーコンテナーの取得 BOOL bResult; HCRYPTPROV hProv; bResult = CryptAcquireContext( &hProv, // ハンドルが戻ります NULL, // 既定のユーザーのCSPを使います MS_ENH_RSA_AES_PROV, // バンドルされたMicrosoftのCSPを指定 PROV_RSA_AES, // タイプを指定 0); // キーコンテナーがある場合の指定です if(!bResult) { if(!CryptAcquireContext(&hProv,NULL,MS_ENH_RSA_AES_PROV,PROV_RSA_AES, CRYPT_NEWKEYSET)) { printf("CryptAcquireContext error\n"); return; } }
1.14.3 ハッシュ計算のインスタンスを生成する
暗号化の鍵は、ハッシュ値から生成します。ストリーミング暗号化とまったく同じ手順です。詳細は、簡単な暗号化[http://www.trustss.co.jp/smnEncrypt010.html]を参照してください。
HCRYPTHASH hHash; bResult = CryptCreateHash( hProv, // ハッシュ値を計算するCSPのハンドル CALG_SHA, // ハッシュ値の計算アルゴリズム 0, // (後述します) 0, // 未使用、0(ゼロ)をセット &hHash); // 求めるインスタンスのハンドル if(!bResult) { printf("CryptCreateHash error\n"); return; }
ハッシュ値の計算アルゴリズムは、以下の値が利用できます。
CALG_HMAC 鍵つきハッシュアルゴリズム CALG_MAC メッセージ認証鍵つきハッシュアルゴリズム CALG_MD2 MD2ハッシュアルゴリズム CALG_MD4 MD4ハッシュアルゴリズム CALG_MD5 MD5ハッシュアルゴリズム CALG_SHA SHA-1ハッシュアルゴリズム CALG_SHA1 SHA-1ハッシュアルゴリズム(CALG_SHAと同じ) CALG_SSL3_SHAMD5 SSLクライアント認証 CALG_SHA_256 256ビットSHAハッシュアルゴリズム CALG_SHA_384 384ビットSHAハッシュアルゴリズム CALG_SHA_512 512ビットSHAハッシュアルゴリズム (ただし、CALG_SHA_256、CALG_SHA_384、CALG_SHA_512は Windows XP/200/NTでは利用できません。ここでは、MD5もしくは、SHA-1を選択するべきでしょう。鍵を生成するためのハッシュ計算ですのでアルゴリズムの選択には神経質になる必要はないでしょう。
1.14.4 ハッシュ値を計算する
ここもストリーミング暗号化とまったく同じ手順です。詳細は、簡単な暗号化[http://www.trustss.co.jp/smnEncrypt010.html]を参照してください。
#define PASSWORD "password" if(!CryptHashData( hHash, // ハッシュ計算インスタンスのハンドル (BYTE*)PASSWORD, // 実際のパスワードを指定 (DWORD)strlen(PASSWORD), // パスワードのバイト長 0)) { printf("CryptHashData error\n"); return; }
1.14.5 AES鍵の生成
AESの「鍵」を生成します。ここでは、256ビット長の鍵を使って説明します。
以下のようにします。
以下のようにします。
#define KEYLENGTH_256 256 * 0x10000 // 256-bit長 HCRYPTKEY hKey; if(!CryptDeriveKey( hProv, // CSPのハンドル CALG_AES_256, // 暗号化のアルゴリズム hHash, // ハッシュ値のハンドル KEYLENGTH_256, // 暗号化鍵のビット長とフラグ &hKey)) // 鍵のハンドル { printf("CryptDeriveKey error\n"); return; }
第2引数には、AESブロック暗号のアルゴリズムを指定します。他のブロック暗号化のアルゴリズムは、以下のとおり用意されています。CSPそれぞれで利用できるアルゴリズムが違いますので注意してください。詳細は、1.12 CSPについて[http://www.TrustSS.co.jp/smnEasyEnc1C0.html]を参照してください。
CALG_DES DES暗号化アルゴリズム CALG_DESX DESX暗号化アルゴリズム CALG_3DES トリプルDES暗号化アルゴリズム CALG_3DES_112 112ビット2キーDES暗号化アルゴリズム CALG_AES AES暗号化アルゴリズム CALG_AES_128 128ビットAES暗号化アルゴリズム CALG_AES_192 192ビットAES暗号化アルゴリズム CALG_AES_256 256ビットAES暗号化アルゴリズム CALS_RC2 RC2ブロック暗号化アルゴリズム CALG_RC5 RC5ブロック暗号化アルゴリズム CALG_SEAL SEAL暗号化アルゴリズム CALG_SKIPJACK Skipjackブロック暗号化アルゴリズム
1.14.6 パディングモードの指定
ブロック暗号化の場合は、被暗号化データのサイズをブロックサイズごとに暗号化しますので、被暗号化データのサイズはブロックサイズの整数倍でなければなりません。ブロック暗号化のアプリケーションでは、暗号化の前に被暗号化データに対してこの処理をしますが、自動でデータのサイズを伸張させることができます。さらに、伸張したデータには規格(PKCS#5)に従ったデータでパディング(Padding)されます。ここでは、その処理をするパディングモードを指定します。
// ブロック暗号化のパディングを設定 DWORD padding_mode=PKCS5_PADDING; if(!CryptSetKeyParam( hKey, // 鍵のハンドル KP_PADDING, // パディング (BYTE*)&padding_mode, // PKCS#5のパディングモード 0)) { printf("CryptSetKeyParam error\n"); return; }鍵オブジェクトにパディングの指定をします。MicrosoftのCSPでは、PKCS5_PADDING以外はサポートされていません。
1.14.7 初期化ベクタ
ブロック暗号では、初期化ベクタを指定できます。初期化ベクタを変えて暗号化すると、同じ鍵を使った場合でも暗号結果を得ます。すなわち、初期化ベクタは、暗号化の鍵を推測しにくくする効果があります。
なお、初期化ベクタを指定しない場合は、値ゼロが使われます。
なお、初期化ベクタを指定しない場合は、値ゼロが使われます。
// 初期ベクタ BYTE iv[]={0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}; if(!CryptSetKeyParam( hKey, // 鍵のハンドル KP_IV, // 初期化ベクタ指定 iv, // 初期化ベクタ値 0)) { printf("CryptSetKeyParam error\n"); return 1; }初期化ベクタのサイズは、暗号化アルゴリズムごとに決まっています。このサンプルの場合は、16バイトですので、上記のようにしています。なお、ivはブロックの暗号化ごとに変化します。暗号化後に鍵オブジェクトから関数CryptGetKeyParam( )を使って新しいivを取得できます。
1.14.8 暗号モードの指定
暗号モードは、暗号を解読しにくくするように暗号化されたデータウィさらに変えるために使われます。例えば、暗号化されrデータが類似したパターンを含む場合、結果として生ずるデータのいくつかのパターンは暗号を解読する手がかりとなることがあります。暗号のモードはこの種類の攻撃に役立ちます。
以下のモードを設定できます。
この設定をしなかった場合は、CBCモードが利用されます。以下にコードを記します。
以下のモードを設定できます。
ECB (Electronic CodeBook mode) 基本モード CBC (Cipher Block Chaining mode) 暗号文ブロック連鎖モード CFB (Cipher-FeedBack mode) 暗号文フィードバックモード OFB (Output-FeedBack mode) 出力フィードバックモード CTS (Cipher Text mode) テキストスティーリングモードMicrosoftのCSPは、OFBモードをサポートしていませんので、ご注意ください。
この設定をしなかった場合は、CBCモードが利用されます。以下にコードを記します。
// Cipherモードを設定 DWORD mode = CRYPT_MODE_CBC; if(!CryptSetKeyParam( hKey, // 鍵のハンドル KP_MODE, // 暗号モード指定 (BYTE*)&mode, // 暗号モード 0)) { tprintf("CryptSetKeyParam error\n"); return; }
1.14.9 復号用に鍵オブジェクトを複製する
解説では、同じ鍵を使って簡単に復号するために、鍵オブジェクトを複製しておきます。ブロック暗号化では、ブロック暗号化ごとに初期化ベクタが変化するために、暗号化後の鍵オブジェクトを使って暗号化データを復号できません。そのために、鍵オブジェクトを利用前に複製しておきます。
実際のアプリケーションでは、鍵オブジェクトを暗号化と同じ手順で生成します。
実際のアプリケーションでは、鍵オブジェクトを暗号化と同じ手順で生成します。
// 復号用にキーを複製 HCRYPTKEY hdKey; if(!CryptDuplicateKey( hKey, NULL, 0, &hdKey)) { printf("CryptDuplicateKey error\n"); return; }関数の第2、3引数は予約された引数ですので、上記のように指定してください。新しい鍵オブジェクトが生成されますので、利用後には関数CryptDestroyKey( )で削除しなければなりません。
1.14.10 暗号化
暗号化します。ここでは、簡単のために暗号化に十分なバッファサイズを用意して実施しました。しかしながら、実際に利用する場合には、データが大きくて1度の関数呼び出しだけでは不足する場合もあります。その場合は、後述のCryptEncrypt( )関数の大3引数をfalseにして必要なだけ関数をコールします。そして最後の関数コールで第3引数をtrueにすれば暗号化できます。復号の場合も同じです。
以下に、暗号化のコードを記します。
以下に、暗号化のコードを記します。
// 暗号化 #define BUF_LEN 100 BYTE pbData[BUF_LEN]="This is a palin text."; DWORD dwDataLen=(DWORD)(strlen((char*)pbData)+1); if(!CryptEncrypt( hKey, // 鍵のハンドル 0, // 暗号化と同時にハッシュ化する場合に指定 TRUE, // 最後のデータを渡すときに真 0, // ゼロを指定 pbData, // 暗号化されるデータのポインター &dwDataLen, // 暗号化データのバイト長データの格納ポインター (DWORD)BUF_LEN)) // データバッファーの大きさ { printf("CryptEncrypt error\n"); return; }
第2引数には、暗号化されるデータを暗号化と同時にハッシュ値にする場合に設定します。電子署名のアプリケーションなどで利用されます。今回のようなアプリケーションでは、その必要がありませんので、0(ゼロ)をセットします。
第3引数は、大量のデータを分割して関数に渡す場合に利用します。例えば、2回に分けて渡す場合は、最初のデータを渡すときにFALSEを指定し、2つ目のデータを渡すときにTRUEを指定します。
第4引数は、特殊な暗号化を行う場合に指定します。ここでは、0(ゼロ)を指定します。
第5引数には、暗号化するデータのポインターが入ります。戻りデータの大きさがわからないとき、で解説しますが、NULLが設定されてもエラーになりませんのでご注意ください。
第6引数には、データの長さが入ります。
第7引数には、暗号化データが格納されるバッファーのバイト長を指定します。
暗号化データは、pbDataに格納され、そのバイト長は、dwDataLenに格納されます。
第3引数は、大量のデータを分割して関数に渡す場合に利用します。例えば、2回に分けて渡す場合は、最初のデータを渡すときにFALSEを指定し、2つ目のデータを渡すときにTRUEを指定します。
第4引数は、特殊な暗号化を行う場合に指定します。ここでは、0(ゼロ)を指定します。
第5引数には、暗号化するデータのポインターが入ります。戻りデータの大きさがわからないとき、で解説しますが、NULLが設定されてもエラーになりませんのでご注意ください。
第6引数には、データの長さが入ります。
第7引数には、暗号化データが格納されるバッファーのバイト長を指定します。
暗号化データは、pbDataに格納され、そのバイト長は、dwDataLenに格納されます。
1.14.11 復号
次は、暗号化したデータを復号します。
復号の場合も、暗号化と同じ手順で鍵オブジェクトを生成します。ここでは、簡単のために先に複製した鍵オブジェクトを使って復号します。
復号の場合も、暗号化と同じ手順で鍵オブジェクトを生成します。ここでは、簡単のために先に複製した鍵オブジェクトを使って復号します。
if(!CryptDecrypt( hKey, // 鍵のハンドル 0, // 復号と同時にハッシュ計算をする場合に指定 TRUE, // 最後のデータを渡すときに真、他は偽 0, // 特殊な服後をする場合にフラグをセットする pbData, // [in]暗号化データ/[out]復号したデータ &dwDataLen)) // [in]暗号化データのバイト長 /[out]復号したデータのバイト長 { printf("CryptDecrypt error\n"); return; } printf("データは、「%s」です。\n", pbData)
第2引数には、ハッシュ計算のインスタンスのハンドルを渡します。復号と同時にハッシュ値が計算されます。このハッシュ値をもとのハッシュ値と比較するような電子署名のアプリケーションで利用します。ここでは、その機能を利用しませんので0(ゼロ)を指定します。
第3引数は、大量のデータを渡す場合に利用します。データを分割して関数に渡す場合、残りのデータがある場合は、FALSEをセットし、残りのデータがなくなった最後のデータを渡すときにTRUEを渡します。
第4引数は、特殊な復号を行う場合にフラグをセットしますが、こんかいのアプリケーションでは使用しませんので、0(ゼロ)をセットします。
第5引数には、データのポインターを渡します。入力では、暗号化したデータが入ります。出力では、復号されたデータが入ります。戻りデータの大きさがわからないとき、で解説しますが、NULLが設定されてもエラーになりませんのでご注意ください。
第5引数には、データのバイト長をセットします。もし、復号されたデータが、暗号化されたデータと違う場合(ブロック暗号化の場合)は、この値が関数によって変更されますので第3引数のTRUEを渡した時に確認が必要です。
第3引数は、大量のデータを渡す場合に利用します。データを分割して関数に渡す場合、残りのデータがある場合は、FALSEをセットし、残りのデータがなくなった最後のデータを渡すときにTRUEを渡します。
第4引数は、特殊な復号を行う場合にフラグをセットしますが、こんかいのアプリケーションでは使用しませんので、0(ゼロ)をセットします。
第5引数には、データのポインターを渡します。入力では、暗号化したデータが入ります。出力では、復号されたデータが入ります。戻りデータの大きさがわからないとき、で解説しますが、NULLが設定されてもエラーになりませんのでご注意ください。
第5引数には、データのバイト長をセットします。もし、復号されたデータが、暗号化されたデータと違う場合(ブロック暗号化の場合)は、この値が関数によって変更されますので第3引数のTRUEを渡した時に確認が必要です。
1.14.12 リソースの開放
暗号化や復号の処理が終わりましたら、利用したリソースを開放します。
鍵オブジェクトやハッシュ計算のインスタンスハンドルは、以下の方法で開放します。
鍵オブジェクトやハッシュ計算のインスタンスハンドルは、以下の方法で開放します。
if(!CryptDestroyKey(hdKey)) // 複製された鍵オブジェクト { printf("CryptDestroyKey error\n"); return; } if(!CryptDestroyKey(hKey)) // オリジナルの鍵オブジェクト { printf("CryptDestroyKey error\n"); return; } if(!CryptDestroyHash(hHash)) { printf("CryptDestroyHash error\n"); return; }引数に、ハッシュ計算のハンドルを指定します。
続いて CSP のハンドルを開放します。
if(!CryptReleaseContext(hProv, 0)) { fprintf(stderr, "CryptReleaseContext error\n"); return; }
第1引数に CSP のハンドルを指定します。
第2引数には、0(ゼロ)を指定します。
また、暗号化や復号処理のために確保した領域も開放するのを忘れないようにしてください。
第2引数には、0(ゼロ)を指定します。
また、暗号化や復号処理のために確保した領域も開放するのを忘れないようにしてください。
1.14.13 サンプル・コード
サンプル・コードです。Microsoft Visual Studio 2005 のプロジェクトです。
サンプルの商業利用および転載を禁止します。
サンプルの商業利用および転載を禁止します。
1.14.14 ご質問・ご要望
ご質問やご要望は、こちらからお送りください。(匿名でも可能です。)ご感想などもお聞かせください。
(記載の会社名および製品名は、各社の登録商標および商標です。)