ホーム 主筆 その他ソフト その他情報 Syuhitu.org English

Windows関連

スクリーンセーバー作成法

半透明ウインドウの性能

bootfont.bin

キャビネット形式

ウインドウスタイルをいじる

Java製ソフトをServiceに登録する

イベントログにメッセージを出力する

コントロールパネルにアイコンを追加する

スクリプトによる拡張1

スクリプトによる拡張2

ガジェットの作成

大容量メモリ

メモリ搭載量の下限に挑む

スパースファイルにする

表示されるアイコンの種類を調べてみた

メモリマップIOとエラー処理

ファイルを作る順番と速度の関係

Cryptography API: Next Generationを使う

Windows 10のアクセントカラー

iSCSIディスクにバックアップを取る

サーバプロセスを分離して実装する

サーバプロセスを分離して実装する - F#

レジストリに大量に書き込む

Solaris関連

OpenGL

Solaris設定

ディレクトリの読み込み

主筆プラグイン開発

マルチスレッドでの開発

door

音を出す

Blade100の正しい虐め方

パッケージの作成

画像入出力

BMPファイル

ICOファイル

ANIファイル

JPEGファイル

減色アルゴリズム

減色アルゴリズムの並列化

その他アルゴリズムなど

自由軸回転

Base64

文字列操作

CPU利用率の取得

正規表現ライブラリ

メタボールを作る

メタボールを作る2

正規表現とNFA・DFA

C言語の構文解析

液晶ディスプレイを解体してみた

iSCSIの理論と実装

単一フォルダにファイルを沢山作る

USB-HUBのカスケード接続

SafeIntの性能

VHDファイルのフォーマット

USBメモリに書き込み続けてみた

CNG:公開鍵暗号で暗号化

2016年2月13日公開

CNGで公開鍵暗号による暗号と復号を行う。

概要

まずは委細を省いて、とりあえず暗号と復号を行う手順の概要を示す。

大まかな手順としては、

  1. アルゴリズムプロバイダを構築する
  2. 鍵のペアを生成する
  3. 暗号/復号を行う

となる。

アルゴリズムプロバイダは、乱数ハッシュ値の生成と同じでBCryptOpenAlgorithmProvider関数を呼び出してやればいい。

これだけならむしろ共通鍵で暗号化する場合よりも簡単なのか?

鍵の生成

鍵のペアを作るためには、少なくとも下記2つの関数を呼び出してやる必要がある。

  1. BCryptGenerateKeyPair関数
  2. BCryptFinalizeKeyPair関数

BCryptGenerateKeyPair関数で鍵のサイズを指定して鍵のハンドルを取得する。

  // 公開鍵と秘密鍵のペアを生成する
  NTSTATUS r = BCryptGenerateKeyPair( hAlgorithm, &hKey, 2048, 0 );
  if ( r != 0 )
    printf( "BCryptGenerateKeyPair失敗\n" );
  else
    printf( "BCryptGenerateKeyPair成功\n" );

第三引数の2048というのが鍵のサイズである。この例ではRSA暗号を想定してこのようにしている。第四引数の0には指定するものはないらしく常に0だという。

BCryptFinalizeKeyPair関数で鍵の生成を終了する。

  // 鍵の構築を終了する
  r = BCryptFinalizeKeyPair( hKey, 0 );
  if ( r != 0 )
    printf( "BCryptFinalizeKeyPair失敗\n" );
  else
    printf( "BCryptFinalizeKeyPair成功\n" );

第二引数の0というのは、上と同じように固定値で指定しておくものらしい。

MSDNの記載によれば、BCryptGenerateKeyPair関数とBCryptFinalizeKeyPair関数の間で、BCryptSetProperty関数により生成した鍵に対してプロパティを指定しろと書かれている。だが、指定できる値の一覧を見ても、とりわけ設定しなければならない項目があるようにも見えない。なのでここではこのまま先に進むことにする。

暗号/復号

できた鍵がどんなもんなのかとか、公開する鍵と秘密にする鍵はどっちがどっちでとか、鍵を配信することとか、そういった面倒くさいことはすべて忘れてとりあえず暗号化してみる。

暗号にするときは共通鍵と同じでBCryptEncrypt関数を使用する。

  // 暗号文のバイト長を取得する
  r = BCryptEncrypt(
    hKey,                                            // 鍵のハンドル
    (unsigned char*)Hirabun1, sizeof( Hirabun1 ),    // 平文のバッファとバイト長
    nullptr,                                         // パディングの情報(ここでは使用しない)
    nullptr, 0,                                      // イニシャルベクタ(使用しない)
    nullptr, 0,                                      // 暗号文のバッファとバイト長
    &Result,                                         // 暗号文のバイト長を受け取る変数
    BCRYPT_PAD_PKCS1                                 // パディングの方式
  );
  if ( r != 0 )
    printf( "BCryptEncrypt失敗\n" );
  else
    printf( "BCryptEncrypt成功 暗号文のバイト長=%d\n", Result );

  // 暗号化する
  r = BCryptEncrypt(
    hKey,                                            // 鍵のハンドル
    (unsigned char*)Hirabun1, sizeof( Hirabun1 ),    // 平文のバッファとバイト長
    nullptr,                                         // パディングの情報(ここでは使用しない)
    nullptr, 0,                                      // イニシャルベクタ(使用しない)
    (unsigned char*)Angoubun, sizeof( Angoubun ),    // 暗号文のバッファとバイト長
    &Result,                                         // 暗号文のバイト長を受け取る変数
    BCRYPT_PAD_PKCS1                                 // パディングの方式
  );
  if ( r != 0 )
    printf( "BCryptEncrypt失敗\n" );
  else
    printf( "BCryptEncrypt成功 暗号文=……" );

面倒なことに2回呼び出している。1回目と2回目の違いは、暗号文が書き込まれるバッファを指定しているか否かである。

これは、共通鍵の場合は、まぁ平文の長さとアルゴリズムの特性から暗号文の長さが推定できようというものだが、共通鍵の場合はまったくわからないことから、こんなことになっている。

BCryptEncrypt関数は、暗号文を受け取るバッファを指定しないと暗号化後のバイト数のみを返すので、その値で必要なバッファを確保してから、再び暗号化を試みるという手順を踏むのである。

上記のコードでは、変数Angonbunのバッファを確保するコードを書いていないが、これは単に横着しただけである。本当ならばここにmallocやnewが必要になると思われる。

次に復号してみる。

復号する場合も、共通鍵と同じでBCryptDecrypt関数を使用する。

  // 平文のバイト長を取得する
  r = BCryptDecrypt(
    hKey,                                            // 鍵のハンドル
    (unsigned char*)Angoubun, AngoubunSize,          // 暗号文のバッファとバイト長
    nullptr,                                         // パディングの情報(ここでは使用しない)
    nullptr, 0,                                      // イニシャルベクタ(使用しない)
    nullptr, 0,                                      // 平文のバッファとバイト長
    &Result,                                         // 平文のバイト長を受け取る変数
    BCRYPT_PAD_PKCS1                                 // パディングの方式
  );
  if ( r != 0 )
    printf( "BCryptDecrypt失敗\n" );
  else
    printf( "BCryptDecrypt成功 平文のバイト長=%d\n", Result );
 
  // 復号する
  r = BCryptDecrypt(
    hKey,                                            // 鍵のハンドル
    (unsigned char*)Angoubun, AngoubunSize,          // 暗号文のバッファとバイト長
    nullptr,                                         // パディングの情報(ここでは使用しない)
    nullptr, 0,                                      // イニシャルベクタ(使用しない)
    (unsigned char*)Hirabun2, Result,                // 平文のバッファとバイト長
    &Result,                                         // 平文のバイト長を受け取る変数
    BCRYPT_PAD_PKCS1                                 // パディングの方式
  );
  if ( r != 0 )
    printf( "BCryptDecrypt失敗\n" );
  else
    printf( "BCryptDecrypt成功 平文=%s\n", Hirabun2 );

2回呼び出しているのは、暗号化するときと同じである。1回目で平文のバイト長を算出してから、2回目で実際に復号を行っている。

プログラム例

ここまでで一旦、まとめた形でプログラム例を示す。

#include <stdio.h>
#include <stdlib.h>
#include <tchar.h>
#include <Windows.h>
 
// CNGのヘッダファイル
#include <Bcrypt.h>
 
int _tmain(int argc, _TCHAR* argv[])
{
  // アルゴリズムプロバイダのハンドル
  BCRYPT_ALG_HANDLE hAlgorithm;
 
  // 鍵のハンドル
  BCRYPT_KEY_HANDLE hKey;
 
  // 平文のデータ
  char Hirabun1[64] = "abcdefghijklmnopqrstuvwxyz";
 
  // 暗号文
  char Angoubun[4096];
 
  // 復号した平文
  char Hirabun2[64];
 
  // 作業用
  unsigned long Result = 0;
 
  // アルゴリズムプロバイダのハンドルを取得する
  NTSTATUS r = BCryptOpenAlgorithmProvider(
    &hAlgorithm, BCRYPT_RSA_ALGORITHM, MS_PRIMITIVE_PROVIDER, 0
  );
  if ( r != 0 )
    printf( "BCryptOpenAlgorithmProvider失敗\n" );
  else
    printf( "BCryptOpenAlgorithmProvider成功\n" );
 
  // 公開鍵と秘密鍵のペアを生成する
  r = BCryptGenerateKeyPair( hAlgorithm, &hKey, 2048, 0 );
  if ( r != 0 )
    printf( "BCryptGenerateKeyPair失敗\n" );
  else
    printf( "BCryptGenerateKeyPair成功\n" );
 
  // 鍵の構築を終了する
  r = BCryptFinalizeKeyPair( hKey, 0 );
  if ( r != 0 )
    printf( "BCryptFinalizeKeyPair失敗\n" );
  else
    printf( "BCryptFinalizeKeyPair成功\n" );
 
  printf( "--- 暗号化 -----------------------------------------------\n" );
 
  // 暗号文のバイト長を取得する
  r = BCryptEncrypt(
    hKey,                                            // 鍵のハンドル
    (unsigned char*)Hirabun1, sizeof( Hirabun1 ),    // 平文のバッファとバイト長
    nullptr,                                         // パディングの情報(ここでは使用しない)
    nullptr, 0,                                      // イニシャルベクタ(使用しない)
    nullptr, 0,                                      // 暗号文のバッファとバイト長
    &Result,                                         // 暗号文のバイト長を受け取る変数
    BCRYPT_PAD_PKCS1                                 // パディングの方式
  );
  if ( r != 0 )
    printf( "BCryptEncrypt失敗\n" );
  else
    printf( "BCryptEncrypt成功 暗号文のバイト長=%d\n", Result );

  // 本当はここでバッファを確保する処理が必要だが、
  // Angoubunに必要十分な長さを用意しているということで省略する

  // 暗号化する
  r = BCryptEncrypt(
    hKey,                                            // 鍵のハンドル
    (unsigned char*)Hirabun1, sizeof( Hirabun1 ),    // 平文のバッファとバイト長
    nullptr,                                         // パディングの情報(ここでは使用しない)
    nullptr, 0,                                      // イニシャルベクタ(使用しない)
    (unsigned char*)Angoubun, sizeof( Angoubun ),    // 暗号文のバッファとバイト長
    &Result,                                         // 暗号文のバイト長を受け取る変数
    BCRYPT_PAD_PKCS1                                 // パディングの方式
  );
  if ( r != 0 )
    printf( "BCryptEncrypt失敗\n" );
  else {
    printf( "BCryptEncrypt成功 暗号文=" );
    for ( int j = 0; j < Result; j++ )
      printf( "%02X", (unsigned char)Angoubun[j] );
    printf( "\n" );
  }
 
  printf( "--- 復号 -----------------------------------------------\n" );
  unsigned long AngoubunSize = Result;
 
  // 平文のバイト長を取得する
  r = BCryptDecrypt(
    hKey,                                            // 鍵のハンドル
    (unsigned char*)Angoubun, AngoubunSize,          // 暗号文のバッファとバイト長
    nullptr,                                         // パディングの情報(ここでは使用しない)
    nullptr, 0,                                      // イニシャルベクタ(使用しない)
    nullptr, 0,                                      // 平文のバッファとバイト長
    &Result,                                         // 平文のバイト長を受け取る変数
    BCRYPT_PAD_PKCS1                                 // パディングの方式
  );
  if ( r != 0 )
    printf( "BCryptDecrypt失敗\n" );
  else
    printf( "BCryptDecrypt成功 平文のバイト長=%d\n", Result );

  // 本当はここでバッファを確保する処理が必要だが、
  // Hirabun2に必要十分な長さを用意しているということで省略する
 
  // 復号する
  r = BCryptDecrypt(
    hKey,                                            // 鍵のハンドル
    (unsigned char*)Angoubun, AngoubunSize,          // 暗号文のバッファとバイト長
    nullptr,                                         // パディングの情報(ここでは使用しない)
    nullptr, 0,                                      // イニシャルベクタ(使用しない)
    (unsigned char*)Hirabun2, Result,                // 平文のバッファとバイト長
    &Result,                                         // 平文のバイト長を受け取る変数
    BCRYPT_PAD_PKCS1                                 // パディングの方式
  );
  if ( r != 0 )
    printf( "BCryptDecrypt失敗\n" );
  else
    printf( "BCryptDecrypt成功 平文=%s\n", Hirabun2 );
 
  // 鍵を破棄する
  r = BCryptDestroyKey( hKey );
  if ( r != 0 )
    printf( "BCryptDestroyKey失敗\n" );
  else
    printf( "BCryptDestroyKey成功\n" );
 
  // アルゴリズムプロバイダを破棄する
  r = BCryptCloseAlgorithmProvider( hAlgorithm, 0 );
  if ( r != 0 )
    printf( "BCryptCloseAlgorithmProvider失敗\n" );
  else
    printf( "BCryptCloseAlgorithmProvider成功\n" );

  return 0;
}

上記の実行結果を示す。

BCryptOpenAlgorithmProvider成功
BCryptGenerateKeyPair成功
BCryptFinalizeKeyPair成功
--- 暗号化 -----------------------------------------------
BCryptEncrypt成功 暗号文のバイト長=256
BCryptEncrypt成功 暗号文=A7FDD773FA9A82A5F99A015DF4C1212D514033B6C
31E73D69B52D3FF908FA52E57FCA92589A395C50627617C7E430D9F6A54B5324DD
5E03C55222DC2E764A5D8845FD314E831EFA5780B23CF96E4A9C1DE200E22BFBF5
52AE1F36669AABE13F3DDE14E668152B11FB269F297E89FA940B5E707DFD7CA7EA
A18CE026B90BA031A55ACD956A179E6AB9176248E6A3517E5E987DE1C88591F714
122473438C12C1A4C2EA26132AA683B4D84095F0FF02E707E48A76520B09DB6E99
1BAA9A32DBE208461C40E11EB65A7D740B22FAC2FC9F279D0547BEA719A85E85B7
FD3BFF5422A69D35AA3E80643D6845DB49F81FDF3DE26EE004C2D4FBD9A0694B30
29AAD3ABE
--- 復号 -----------------------------------------------
BCryptDecrypt成功 平文のバイト長=64
BCryptDecrypt成功 平文=abcdefghijklmnopqrstuvwxyz
BCryptDestroyKey成功
BCryptCloseAlgorithmProvider成功

鍵のエクスポートとインポート

上記のプログラムだと、自分で生成したなんだかよくわからない鍵で暗号化して、それを直後に復号している。でもって、使った鍵はすぐに破棄している。

だがこれだと意味がない。

常識的に言って、暗号化する主体と復号する主体は、地理的・時間的に異なっているはずである。具体的には、暗号化したデータをディスクに保存して後から復号するとか、暗号文をネットワークで送信して、別のマシンで復号するとか、そうなるはずである。

となると、なんだかよくわからん鍵を身勝手に作ったのであれば、それを外部に出力できなければ何の意味もないので、鍵のエクスポートとインポートを行う方法について述べる。

エクスポートはBCryptExportKey関数で行う。

r = BCryptExportKey(
  hKey,                                       // 鍵のペア
  nullptr,                                    // AES wrap keyは使わない
  BCRYPT_PUBLIC_KEY_BLOB,                     // 公開鍵を指定する
  PublicKeyBuffer, sizeof( PublicKeyBuffer ), // バッファとバイト数
  &Result,                                    // エクスポートしたバイト数
  0                                           // フラグ
);

BCRYPT_PUBLIC_KEY_BLOBの代わりに、BCRYPT_PRIVATE_KEY_BLOBを指定することで、秘密鍵の方をエクスポートすることができる。

インポートはBCryptImportKeyPair関数で行う。

r = BCryptImportKeyPair(
  hAlgorithm,                                  // アルゴリズムプロバイダ
  nullptr,                                     // NULLを指定する
  BCRYPT_PUBLIC_KEY_BLOB,                      // 公開鍵を指定する
  &hKey,                                       // インポートした鍵
  PublicKeyBuffer, PublicKeySize,              // バッファとバイト数
  0                                            // フラグ
);

エクスポートと同様に、BCRYPT_PUBLIC_KEY_BLOBの代わりに、BCRYPT_PRIVATE_KEY_BLOBを指定することで、秘密鍵の方をインポートすることができる。

鍵の生成・暗号・復号を分けたプログラム例

鍵の生成とエクスポート、公開鍵のインポートと暗号化、秘密鍵のインポートと復号を分離したプログラム例を示す。

///////////////////////////////////////////////////////////////////////////////
// プログラムの外部に出力されるものと仮定したバッファ
// (例題として、ファイルに入出力しているとわかりにくくなるため)
 
// 公開鍵のバッファ
unsigned char PublicKeyBuffer[4096];
 
// 公開鍵のバイト長
unsigned long PublicKeySize;
 
// 秘密鍵のバッファ
unsigned char PrivateKeyBuffer[4096];
 
// 秘密鍵のバイト長
unsigned long PrivateKeySize;
 
// 暗号文
char Angoubun[4096];
 
// 暗号文のバイト長
unsigned long AngoubunSize;
 
///////////////////////////////////////////////////////////////////////////////
// 鍵の生成
void CreateKey()
{
  BCRYPT_ALG_HANDLE hAlgorithm;	// アルゴリズムプロバイダ
  BCRYPT_KEY_HANDLE hKey;       // 鍵
  unsigned long Result = 0;
 
  printf( "### 鍵の生成 開始 ########################################################\n" );
 
  // アルゴリズムプロバイダのハンドルを取得する
  NTSTATUS r = BCryptOpenAlgorithmProvider(
    &hAlgorithm, BCRYPT_RSA_ALGORITHM, MS_PRIMITIVE_PROVIDER, 0
  );
  if ( r != 0 )
    printf( "BCryptOpenAlgorithmProvider失敗\n" );
  else
    printf( "BCryptOpenAlgorithmProvider成功\n" );
 
  // 公開鍵と秘密鍵のペアを生成する
  r = BCryptGenerateKeyPair( hAlgorithm, &hKey, 2048, 0 );
  if ( r != 0 )
    printf( "BCryptGenerateKeyPair失敗\n" );
  else
    printf( "BCryptGenerateKeyPair成功\n" );
 
  // 鍵の構築を終了する
  r = BCryptFinalizeKeyPair( hKey, 0 );
  if ( r != 0 )
    printf( "BCryptFinalizeKeyPair失敗\n" );
  else
    printf( "BCryptFinalizeKeyPair成功\n" );
 
  // 公開鍵のエクスポートで必要になるバッファ長を取得する
  r = BCryptExportKey(
    hKey,                                       // 鍵のペア
    nullptr,                                    // AES wrap keyは使わない
    BCRYPT_PUBLIC_KEY_BLOB,                     // 公開鍵を指定する
    nullptr, 0,                                 // バッファとバイト数
    &Result,                                    // エクスポートしたバイト数
    0                                           // フラグ
  );
  if ( r != 0 )
    printf( "BCryptExportKey失敗\n" );
  else
    printf( "BCryptExportKey成功 バイト数=%d\n", Result );
 
  // 確認する
  assert( sizeof( PublicKeyBuffer ) > Result );
 
  // 公開鍵をエクスポートする
  r = BCryptExportKey(
    hKey,                                       // 鍵のペア
    nullptr,                                    // AES wrap keyは使わない
    BCRYPT_PUBLIC_KEY_BLOB,                     // 公開鍵を指定する
    PublicKeyBuffer, sizeof( PublicKeyBuffer ), // バッファとバイト数
    &Result,                                    // エクスポートしたバイト数
    0                                           // フラグ
  );
  if ( r != 0 )
    printf( "BCryptExportKey失敗\n" );
  else
    printf( "BCryptExportKey成功 バイト数=%d\n", Result );
 
  // エクスポートされた公開鍵のバイト長も保存しておく
  PublicKeySize = Result;
 
  // 秘密鍵のエクスポートで必要になるバッファ長を取得する
  r = BCryptExportKey(
    hKey,                                         // 鍵のペア
    nullptr,                                      // AES wrap keyは使わない
    BCRYPT_PRIVATE_KEY_BLOB,                      // 秘密鍵を指定する
    nullptr, 0,                                   // バッファとバイト数
    &Result,                                      // エクスポートしたバイト数
    0                                             // フラグ
  );
  if ( r != 0 )
    printf( "BCryptExportKey失敗\n" );
  else
    printf( "BCryptExportKey成功 バイト数=%d\n", Result );
 
  // 確認する
  assert( sizeof( PrivateKeyBuffer ) > Result );
 
  // 秘密鍵をエクスポートする
  r = BCryptExportKey(
    hKey,                                         // 鍵のペア
    nullptr,                                      // AES wrap keyは使わない
    BCRYPT_PRIVATE_KEY_BLOB,                      // 秘密鍵を指定する
    PrivateKeyBuffer, sizeof( PrivateKeyBuffer ), // バッファとバイト数
    &Result,                                      // エクスポートしたバイト数
    0                                             // フラグ
  );
  if ( r != 0 )
    printf( "BCryptExportKey失敗\n" );
  else
    printf( "BCryptExportKey成功 バイト数=%d\n", Result );
 
  // エクスポートされた秘密鍵のバイト長も保存しておく
  PrivateKeySize = Result;
 
  // アルゴリズムプロバイダを破棄する
  r = BCryptCloseAlgorithmProvider( hAlgorithm, 0 );
  if ( r != 0 )
    printf( "BCryptCloseAlgorithmProvider失敗\n" );
  else
    printf( "BCryptCloseAlgorithmProvider成功\n" );
 
  printf( "### 鍵の生成 終了 ########################################################\n" );
 
}
 
///////////////////////////////////////////////////////////////////////////////
// 暗号化
void Encrypt()
{
  BCRYPT_ALG_HANDLE hAlgorithm;// アルゴリズムプロバイダ
  BCRYPT_KEY_HANDLE hKey;      // 鍵
  unsigned long Result = 0;
  char Hirabun1[64] = "abcdefghijklmnopqrstuvwxyz"// 平文
 
  printf( "### 暗号化 開始 ##########################################################\n" );
 
  // アルゴリズムプロバイダのハンドルを取得する
  NTSTATUS r = BCryptOpenAlgorithmProvider(
    &hAlgorithm, BCRYPT_RSA_ALGORITHM, MS_PRIMITIVE_PROVIDER, 0
  );
  if ( r != 0 )
    printf( "BCryptOpenAlgorithmProvider失敗\n" );
  else
    printf( "BCryptOpenAlgorithmProvider成功\n" );
 
  // 公開鍵をインポートする
  r = BCryptImportKeyPair(
    hAlgorithm,                                  // アルゴリズムプロバイダ
    nullptr,                                     // NULLを指定する
    BCRYPT_PUBLIC_KEY_BLOB,                      // 公開鍵を指定する
    &hKey,                                       // インポートした鍵
    PublicKeyBuffer, PublicKeySize,              // バッファとバイト数
	0                                       // フラグ
  );
  if ( r != 0 )
    printf( "BCryptImportKeyPair失敗\n" );
  else
    printf( "BCryptImportKeyPair成功\n" );
 
  // 暗号文のバイト長を取得する
  r = BCryptEncrypt(
    hKey,                                         // 鍵
    (unsigned char*)Hirabun1, sizeof( Hirabun1 ), // 平文とバイト長
    nullptr,                                      // パディングの情報
    nullptr, 0,                                   // イニシャルベクタ
    nullptr, 0,                                   // 暗号文
    &Result,                                      // 暗号文のバイト長
    BCRYPT_PAD_PKCS1                              // パディングの方法
  );
  if ( r != 0 )
    printf( "BCryptEncrypt失敗\n" );
  else
    printf( "BCryptEncrypt成功 暗号文のバイト長=%d\n", Result );
 
  // 一応確認しておく
  assert( sizeof( Angoubun ) > Result );
 
  // 暗号化する
  r = BCryptEncrypt(
    hKey,                                         // 鍵
    (unsigned char*)Hirabun1, sizeof( Hirabun1 ), // 平文とバイト長
    nullptr,                                      // パディングの情報
    nullptr, 0,                                   // イニシャルベクタ
    (unsigned char*)Angoubun, sizeof( Angoubun ), // 暗号文とバッファ長
    &Result,                                      // 暗号文のバイト長
    BCRYPT_PAD_PKCS1                              // パディングの方法
  );
  if ( r != 0 )
    printf( "BCryptEncrypt失敗\n" );
  else {
    printf( "BCryptEncrypt成功 暗号文=" );
    for ( unsigned int j = 0; j < Result; j++ )
      printf( "%02X", (unsigned char)Angoubun[j] );
    printf( "\n" );
  }
  AngoubunSize = Result;
 
  // 鍵を破棄する
  r = BCryptDestroyKey( hKey );
  if ( r != 0 )
    printf( "BCryptDestroyKey失敗\n" );
  else
    printf( "BCryptDestroyKey成功\n" );
 
  // アルゴリズムプロバイダを破棄する
  r = BCryptCloseAlgorithmProvider( hAlgorithm, 0 );
  if ( r != 0 )
    printf( "BCryptCloseAlgorithmProvider失敗\n" );
  else
    printf( "BCryptCloseAlgorithmProvider成功\n" );
 
  printf( "### 暗号化 終了 ##########################################################\n" );
}
 
///////////////////////////////////////////////////////////////////////////////
// 復号
void Decrypt()
{
  BCRYPT_ALG_HANDLE hAlgorithm;	// アルゴリズムプロバイダ
  BCRYPT_KEY_HANDLE hKey;       // 鍵
  unsigned long Result = 0;
  char Hirabun2[64];            // 復号した平文
 
  printf( "### 復号 開始 ############################################################\n" );
 
  // アルゴリズムプロバイダのハンドルを取得する
  NTSTATUS r = BCryptOpenAlgorithmProvider(
    &hAlgorithm, BCRYPT_RSA_ALGORITHM, MS_PRIMITIVE_PROVIDER, 0
  );
  if ( r != 0 )
    printf( "BCryptOpenAlgorithmProvider失敗\n" );
  else
    printf( "BCryptOpenAlgorithmProvider成功\n" );
 
  // 秘密鍵をインポートする
  r = BCryptImportKeyPair(
    hAlgorithm,                                  // アルゴリズムプロバイダ
    nullptr,                                     // NULLを指定する
    BCRYPT_PRIVATE_KEY_BLOB,                     // 秘密鍵を指定する
    &hKey,                                       // インポートした鍵
    PrivateKeyBuffer, PrivateKeySize,            // バッファとバイト数
	0                                            // フラグ
  );
  if ( r != 0 )
    printf( "BCryptImportKeyPair失敗\n" );
  else
    printf( "BCryptImportKeyPair成功\n" );
 
  // 平文のバイト長を取得する
  r = BCryptDecrypt(
    hKey,                                         // 鍵
    (unsigned char*)Angoubun, AngoubunSize,       // 暗号文とバイト長
    nullptr,                                      // パディングの情報
    nullptr, 0,                                   // イニシャルベクタ
    nullptr, 0,                                   // 平文
    &Result,                                      // 平文のバイト長
    BCRYPT_PAD_PKCS1                              // パディングの方法
  );
  if ( r != 0 )
    printf( "BCryptDecrypt失敗\n" );
  else
    printf( "BCryptDecrypt成功 平文のバイト長=%d\n", Result );
 
  // 復号する
  r = BCryptDecrypt(
    hKey,
    (unsigned char*)Angoubun, AngoubunSize,
    nullptr,
    nullptr, 0,
    (unsigned char*)Hirabun2, sizeof( Hirabun2 ),
    &Result,
    BCRYPT_PAD_PKCS1
  );
  if ( r != 0 )
    printf( "BCryptDecrypt失敗\n" );
  else
    printf( "BCryptDecrypt成功 平文=%s\n", Hirabun2 );
 
  // 鍵を破棄する
  r = BCryptDestroyKey( hKey );
  if ( r != 0 )
    printf( "BCryptDestroyKey失敗\n" );
  else
    printf( "BCryptDestroyKey成功\n" );
 
  // アルゴリズムプロバイダを破棄する
  r = BCryptCloseAlgorithmProvider( hAlgorithm, 0 );
  if ( r != 0 )
    printf( "BCryptCloseAlgorithmProvider失敗\n" );
  else
    printf( "BCryptCloseAlgorithmProvider成功\n" );
 
  printf( "### 復号 終了 ############################################################\n" );
}
 
int _tmain(int argc, _TCHAR* argv[])
{
  // 鍵を生成する
  CreateKey();
 
  // 暗号化する
  Encrypt();
 
  // 復号する
  Decrypt();
 
  return 0;
}

実行結果を示す。

### 鍵の生成 開始 ########################################################
BCryptOpenAlgorithmProvider成功
BCryptGenerateKeyPair成功
BCryptFinalizeKeyPair成功
BCryptExportKey成功 バイト数=283
BCryptExportKey成功 バイト数=283
BCryptExportKey成功 バイト数=539
BCryptExportKey成功 バイト数=539
BCryptCloseAlgorithmProvider成功
### 鍵の生成 終了 ########################################################
### 暗号化 開始 ##########################################################
BCryptOpenAlgorithmProvider成功
BCryptImportKeyPair成功
BCryptEncrypt成功 暗号文のバイト長=256
BCryptEncrypt成功 暗号文=2C80087287780BA972A8927A36A2A6F0
321EB0FBF08C2C20BA8DAD92BE04CC60FFCCE80180E95475381D5
B8EB391AA41EAA9B69BA310B35EAC47F48C6DE0EA2BF04FCEADD
48CE6BD604A55E0DF39BB0E0232DAD5FDB92DF85269CBC60F9761
C86303FF59337A503D5812E8CD785136B05DC58174F8FBD2A32A7A
56E7F9429CFB2F454C9ED8251D8E428A0D4F74A73ABCE266F19BED
566D6875DE7F8B4733D7CEF48EC81B3FE9FB629A945817973766841
B034895771141835FD7679B754420C77934CE4CF1599411CDB8A220
A414D1D08FE4D73BDD10EEB833E501661A376045F1996C66C6FFFB
24E908269EC4030076DD83FE624FCE847EB961343B3F3F63DD

BCryptDestroyKey成功
BCryptCloseAlgorithmProvider成功
### 暗号化 終了 ##########################################################
### 復号 開始 ############################################################
BCryptOpenAlgorithmProvider成功
BCryptImportKeyPair成功
BCryptDecrypt成功 平文のバイト長=64
BCryptDecrypt成功 平文=abcdefghijklmnopqrstuvwxyz
BCryptDestroyKey成功
BCryptCloseAlgorithmProvider成功
### 復号 終了 ############################################################

上記は、アルゴリズムプロバイダも使いまわすことなく、その都度取得して破棄しているため、大分冗長な処理になっている。しかし、先に記載した通り、通常、鍵の生成・暗号化・復号はそれぞれ異なるマシンで行われるだろうと考えれば、むしろこれが正しいはずである。

まぁもっとも、分けたと主張したところで、同一プロセスで行っている時点で何の意味もないわけだが。

<< 「Cryptography API: Next Generationを使う」に戻る


連絡先 - サイトマップ - 更新履歴
Copyright (C)  2000 - 2016 nabiki_t All Rights Reserved.