ホーム 主筆 その他ソフト その他情報 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の鍵交換アルゴリズムによる共有鍵の生成と暗号・復号を行ってみる。

必要性

今更ここで述べることでもない。

共通鍵暗号の例では、暗号と復号で同一の鍵を使いまわしていた。だがこれは、同一プロセス内で暗号と復号を行っていたからにほかならず、現実的にはありえない。

秘密の何かを暗号化するのは、安全ではない方法で保管するか伝達するかするためである。しかし結局のところ、鍵を安全に配送する方法が無ければ、暗号化は意味をなさない。

そこで使うのが鍵交換アルゴリズムというやつである。

処理の流れ

かなり込み入っているため、図で説明する。

どうでもいいが俺の名前はBobではないし、Aliceという名前の彼女もいない。

  1. AliceとBobの双方が、鍵交換アルゴリズムによる公開鍵と秘密鍵のペアを生成する。
  2. 公開鍵を(安全ではない方法で)交換する。この際、公開鍵は外部に漏えいしても問題ではないため、暗号化などは考慮しなくてもよい。
  3. AliceはAliceの秘密鍵とBobの公開鍵でシークレットを生成する。Bobは逆に、Bobの秘密鍵とAliceの公開鍵でシークレットを生成する。驚いたことに、このシークレットは同一の値となる。
  4. シークレットから暗号鍵を生成する。同じシークレットから生成した以上、AliceとBobは同一の暗号鍵を生成することになる。
  5. Aliceは平文を暗号化しBobに送信する。BobはAliceから受信した暗号文を復号する。

上記のシークレットを生成するまで(つまり3まで)が鍵交換アルゴリズムとか、鍵共有アルゴリズムといわれるものである。それ以降は、普通の共通鍵暗号による暗号化と復号である。

CNGでの実現方法

アルゴリズムそのものの説明はほかに譲る。ここではCNGでのAPIの使い方について焦点を当てる。

公開鍵と秘密鍵のペアは下記で生成する。

// アルゴリズムプロバイダをオープンする
NTSTATUS r = BCryptOpenAlgorithmProvider(
  &hAlgorithm, BCRYPT_ECDH_P521_ALGORITHM, MS_PRIMITIVE_PROVIDER, 0
);
if ( r != 0 ) printf( "BCryptOpenAlgorithmProvider失敗\n" );

// 公開鍵と秘密鍵のペアを生成する
r = BCryptGenerateKeyPair( hAlgorithm, &hKey, 521, 0 );
if ( r != 0 ) printf( "BCryptGenerateKeyPair失敗\n" );
 
// 鍵の構築を終了する
r = BCryptFinalizeKeyPair( hKey, 0 );
if ( r != 0 ) printf( "BCryptFinalizeKeyPair失敗\n" );

アルゴリズムプロバイダで指定するアルゴリズムがBCRYPT_ECDH_P521_ALGORITHMで、鍵の長さが521bitという中途半端な数値であることを除けば、公開鍵暗号の場合と何らの違いもない。

少なくとも公開鍵は相手に送らなければならない以上エクスポートする必要がある。方法は公開鍵暗号の場合と同じである。

// 公開鍵をエクスポートする
r = BCryptExportKey(
  hKey,                                        // 鍵のペア
  nullptr,                                     // AES wrap keyは使わない
  BCRYPT_PUBLIC_KEY_BLOB,                      // 公開鍵を指定する
  Alice_PublicKey, sizeof( Alice_PublicKey ),  // バッファとバイト数
  &Alice_PublicKeySize,                        // エクスポートしたバイト数
  0                                            // フラグ
);
if ( r != 0 )
  printf( "BCryptExportKey失敗\n" );
else
  printf( "BCryptExportKey成功 Alice側公開鍵のサイズ=%d\n", Alice_PublicKeySize );

エクスポートした鍵はインポートする必要がある。これもまた、公開鍵暗号の例で示したものと同じである。

// 公開鍵をインポートする
r = BCryptImportKeyPair(
  hAlgorithm,                                  // アルゴリズムプロバイダ
  nullptr,                                     // NULLを指定する
  BCRYPT_PUBLIC_KEY_BLOB,                      // 公開鍵を指定する
  &hAlicePublicKey,                            // インポートした鍵
  Alice_PublicKey, Alice_PublicKeySize,        // バッファとバイト数
  0                                            // フラグ
);
if ( r != 0 ) printf( "BCryptImportKeyPair失敗\n" );

自分が生成した秘密鍵と、インポートしてきた相手側の公開鍵でシークレットを生成するためには、BCryptSecretAgreement関数とBCryptDeriveKey関数を用いる。

// シークレットを生成する
r = BCryptSecretAgreement(
  hAlicePrivateKey,
  hBobPublicKey,
  &hSecret,
  0
);
if ( r != 0 ) printf( "BCryptSecretAgreement失敗\n" );

// 共通鍵の生成に使用するシークレットを取得する
r = BCryptDeriveKey(
  hSecret,                                     // シークレットのハンドル
  BCRYPT_KDF_HASH,                             // とりあえずSHA1
  nullptr,                                     // 面倒だから省略
  Alice_SecretBuffer,                          // シークレットのバッファ
  sizeof( Alice_SecretBuffer ),                // シークレットのバッファ
  &Alice_SecretSize,                           // サイズ
  0                                            // フラグ
);
if ( r != 0 )
  printf( "BCryptDeriveKey失敗\n" );
else
  printf( "BCryptDeriveKey成功 ……\n" );

ここまでで、上記の例であれば「Alice_SecretBuffer」というバッファに20バイトの乱数(のようなものが)が生成される。これが、公開鍵を交換した両社で同一の値になるのだという。

プログラムの例

長いが、上記をまとめたサンプルの実装例を示す。

#include "stdafx.h"
 
#include <stdio.h>
#include <stdlib.h>
#include <tchar.h>
#include <time.h>
#include <assert.h>
#include <Windows.h>
 
// CNGのヘッダファイル
#include <Bcrypt.h>

///////////////////////////////////////////////////////////////////////////////
// グローバル変数

unsigned char Alice_PublicKey[512];     // Alice側公開鍵
unsigned long Alice_PublicKeySize;      // Alice側公開鍵のバイト長
 
unsigned char Alice_PrivateKey[512];    // Alice側秘密鍵
unsigned long Alice_PrivateKeySize;     // Alice側秘密鍵のバイト長
 
unsigned char Alice_SecretBuffer[128];  // Alice側シークレット
unsigned long Alice_SecretSize;         // Alice側シークレットのバイト長
 
unsigned char Bob_PublicKey[512];       // Bob側公開鍵
unsigned long Bob_PublicKeySize;        // Bob側公開鍵のバイト長
 
unsigned char Bob_PrivateKey[512];      // Bob側秘密鍵
unsigned long Bob_PrivateKeySize;       // Bob側秘密鍵のバイト長
 
unsigned char Bob_SecretBuffer[128];    // Bob側シークレット
unsigned long Bob_SecretSize;           // Bob側シークレットのバイト長
 
unsigned char EncryptData[128];         // 暗号文
unsigned long EncryptDataSize;          // 暗号文のサイズ
 
///////////////////////////////////////////////////////////////////////////////
// Alice側の鍵生成
 
void CreateKeyPair_Alice()
{
  BCRYPT_ALG_HANDLE hAlgorithm;	// アルゴリズムプロバイダ
  BCRYPT_KEY_HANDLE hKey;       // 鍵
 
  printf( "### Alice側の鍵ペアの生成 開始 ############################################\n" );
 
  // アルゴリズムプロバイダをオープンする
  NTSTATUS r = BCryptOpenAlgorithmProvider(
    &hAlgorithm, BCRYPT_ECDH_P521_ALGORITHM, MS_PRIMITIVE_PROVIDER, 0
  );
  if ( r != 0 ) printf( "BCryptOpenAlgorithmProvider失敗\n" );
 
  // 公開鍵と秘密鍵のペアを生成する
  r = BCryptGenerateKeyPair( hAlgorithm, &hKey, 521, 0 );
  if ( r != 0 ) printf( "BCryptGenerateKeyPair失敗\n" );
 
  // 鍵の構築を終了する
  r = BCryptFinalizeKeyPair( hKey, 0 );
  if ( r != 0 ) printf( "BCryptFinalizeKeyPair失敗\n" );
 
  // 公開鍵をエクスポートする
  r = BCryptExportKey(
    hKey,                                        // 鍵のペア
    nullptr,                                     // AES wrap keyは使わない
    BCRYPT_PUBLIC_KEY_BLOB,                      // 公開鍵を指定する
    Alice_PublicKey, sizeof( Alice_PublicKey ),  // バッファとバイト数
    &Alice_PublicKeySize,                        // エクスポートしたバイト数
    0                                            // フラグ
  );
  if ( r != 0 )
    printf( "BCryptExportKey失敗\n" );
  else
    printf( "BCryptExportKey成功 Alice側公開鍵のサイズ=%d\n", Alice_PublicKeySize );
 
  // 秘密鍵をエクスポートする
  r = BCryptExportKey(
    hKey,                                         // 鍵のペア
    nullptr,                                      // AES wrap keyは使わない
    BCRYPT_PRIVATE_KEY_BLOB,                      // 秘密鍵を指定する
    Alice_PrivateKey, sizeof( Alice_PrivateKey ), // バッファとバイト数
    &Alice_PrivateKeySize,                        // エクスポートしたバイト数
    0                                             // フラグ
  );
  if ( r != 0 )
    printf( "BCryptExportKey失敗\n" );
  else
    printf( "BCryptExportKey成功 Alice側秘密鍵のサイズ=%d\n", Alice_PrivateKeySize );
 
  // 一旦鍵を破棄してしまう
  r = BCryptDestroyKey( hKey );
  if ( r != 0 ) printf( "BCryptDestroyKey失敗\n" );
 
  // アルゴリズムプロバイダを破棄する
  r = BCryptCloseAlgorithmProvider( hAlgorithm, 0 );
  if ( r != 0 ) printf( "BCryptCloseAlgorithmProvider失敗\n" );
 
  printf( "### Alice側の鍵ペアの生成 終了 ############################################\n" );
 
}
 
///////////////////////////////////////////////////////////////////////////////
// Bob側の鍵生成
// (Aliceの鍵生成とまったく同じ処理だが、わかりやすさのため繰り返し書いておく)
 
void CreateKeyPair_Bob()
{
  BCRYPT_ALG_HANDLE hAlgorithm;	// アルゴリズムプロバイダ
  BCRYPT_KEY_HANDLE hKey;       // 鍵
 
  printf( "### Bob側の鍵ペアの生成 開始 ##############################################\n" );
  
  // アルゴリズムプロバイダをオープンする
  NTSTATUS r = BCryptOpenAlgorithmProvider(
    &hAlgorithm, BCRYPT_ECDH_P521_ALGORITHM, MS_PRIMITIVE_PROVIDER, 0
  );
  if ( r != 0 ) printf( "BCryptOpenAlgorithmProvider失敗\n" );
 
  // 公開鍵と秘密鍵のペアを生成する
  r = BCryptGenerateKeyPair( hAlgorithm, &hKey, 521, 0 );
  if ( r != 0 ) printf( "BCryptGenerateKeyPair失敗\n" );
 
  // 鍵の構築を終了する
  r = BCryptFinalizeKeyPair( hKey, 0 );
  if ( r != 0 ) printf( "BCryptFinalizeKeyPair失敗\n" );
 
  // 公開鍵をエクスポートする
  r = BCryptExportKey(
    hKey,                                        // 鍵のペア
    nullptr,                                     // AES wrap keyは使わない
    BCRYPT_PUBLIC_KEY_BLOB,                      // 公開鍵を指定する
    Bob_PublicKey, sizeof( Bob_PublicKey ),      // バッファとバイト数
    &Bob_PublicKeySize,                          // エクスポートしたバイト数
    0                                            // フラグ
  );
  if ( r != 0 )
    printf( "BCryptExportKey失敗\n" );
  else
    printf( "BCryptExportKey成功 Bob側公開鍵のサイズ=%d\n", Bob_PublicKeySize );
 
  // 秘密鍵をエクスポートする
  r = BCryptExportKey(
    hKey,                                         // 鍵のペア
    nullptr,                                      // AES wrap keyは使わない
    BCRYPT_PRIVATE_KEY_BLOB,                      // 秘密鍵を指定する
    Bob_PrivateKey, sizeof( Bob_PrivateKey ),     // バッファとバイト数
    &Bob_PrivateKeySize,                          // エクスポートしたバイト数
    0                                             // フラグ
  );
  if ( r != 0 )
    printf( "BCryptExportKey失敗\n" );
  else
    printf( "BCryptExportKey成功 Bob側秘密鍵のサイズ=%d\n", Bob_PrivateKeySize );
 
  // 一旦鍵を破棄してしまう
  r = BCryptDestroyKey( hKey );
  if ( r != 0 ) printf( "BCryptDestroyKey失敗\n" );
 
  // アルゴリズムプロバイダを破棄する
  r = BCryptCloseAlgorithmProvider( hAlgorithm, 0 );
  if ( r != 0 ) printf( "BCryptCloseAlgorithmProvider失敗\n" );
 
  printf( "### Bob側の鍵ペアの生成 終了 ##############################################\n" );
 
}
 
///////////////////////////////////////////////////////////////////////////////
// Alice側のシークレット生成
 
void GenSecert_Alice()
{
  BCRYPT_ALG_HANDLE hAlgorithm;	       // アルゴリズムプロバイダ
  BCRYPT_KEY_HANDLE hAlicePrivateKey;  // Alice側の秘密鍵
  BCRYPT_KEY_HANDLE hBobPublicKey;     // Bob側の公開鍵
  BCRYPT_SECRET_HANDLE hSecret;        // シークレットのハンドル
 
  printf( "### Alice側のシークレット生成 開始 ########################################\n" );
 
  // アルゴリズムプロバイダをオープンする
  NTSTATUS r = BCryptOpenAlgorithmProvider(
    &hAlgorithm, BCRYPT_ECDH_P521_ALGORITHM, MS_PRIMITIVE_PROVIDER, 0
  );
  if ( r != 0 ) printf( "BCryptOpenAlgorithmProvider失敗\n" );
 
  // Alice側秘密鍵をインポートする
  r = BCryptImportKeyPair(
    hAlgorithm,                                  // アルゴリズムプロバイダ
    nullptr,                                     // NULLを指定する
    BCRYPT_PRIVATE_KEY_BLOB,                     // 秘密鍵を指定する
    &hAlicePrivateKey,                           // インポートした鍵
    Alice_PrivateKey, Alice_PrivateKeySize,      // バッファとバイト数
    0                                            // フラグ
  );
  if ( r != 0 ) printf( "BCryptImportKeyPair失敗\n" );
 
  // Bob側公開鍵をインポートする
  r = BCryptImportKeyPair(
    hAlgorithm,                                  // アルゴリズムプロバイダ
    nullptr,                                     // NULLを指定する
    BCRYPT_PUBLIC_KEY_BLOB,                      // 秘密鍵を指定する
    &hBobPublicKey,                              // インポートした鍵
    Bob_PublicKey, Bob_PublicKeySize,            // バッファとバイト数
    0                                            // フラグ
  );
  if ( r != 0 ) printf( "BCryptImportKeyPair失敗\n" );
 
  // Aliceの秘密鍵とBobの公開鍵からシークレットを生成する
  r = BCryptSecretAgreement(
    hAlicePrivateKey,
    hBobPublicKey,
    &hSecret,
    0
  );
  if ( r != 0 ) printf( "BCryptSecretAgreement失敗\n" );
 
  // 共通鍵の生成に使用するシークレットを取得する
  r = BCryptDeriveKey(
    hSecret,                                     // シークレットのハンドル
    BCRYPT_KDF_HASH,                             // とりあえずSHA1
    nullptr,                                     // 面倒だから省略
    Alice_SecretBuffer,                          // シークレットのバッファ
    sizeof( Alice_SecretBuffer ),                // シークレットのバッファ
    &Alice_SecretSize,                           // サイズ
    0                                            // フラグ
  );
  if ( r != 0 )
	  printf( "BCryptDeriveKey失敗\n" );
   else {
      printf( "BCryptDeriveKey成功 Alice側シークレット=" );
      for ( unsigned int i = 0; i < Alice_SecretSize; i++ )
        printf( "%02X", Alice_SecretBuffer[i] );
      printf( "\n" );
      printf( "Alice側シークレットのバイト長=%d\n", Alice_SecretSize );
 
      // デフォルトでSHA1が使われることから、シークレットは20バイトしかないことに注意
   }
 
  // 鍵を破棄する
  r = BCryptDestroyKey( hAlicePrivateKey );
  if ( r != 0 ) printf( "BCryptDestroyKey失敗\n" );
  r = BCryptDestroyKey( hBobPublicKey );
  if ( r != 0 ) printf( "BCryptDestroyKey失敗\n" );
 
  // シークレットのハンドルを破棄する
  r = BCryptDestroySecret( hSecret );
  if ( r != 0 ) printf( "BCryptDestroySecret失敗\n" );
 
  // アルゴリズムプロバイダを破棄する
  r = BCryptCloseAlgorithmProvider( hAlgorithm, 0 );
  if ( r != 0 ) printf( "BCryptCloseAlgorithmProvider失敗\n" );
 
  printf( "### Alice側のシークレット生成 終了 ########################################\n" );
}
 
///////////////////////////////////////////////////////////////////////////////
// Bob側のシークレット生成
 
void GenSecert_Bob()
{
  BCRYPT_ALG_HANDLE hAlgorithm;	       // アルゴリズムプロバイダ
  BCRYPT_KEY_HANDLE hBobPrivateKey;    // Bob側の秘密鍵
  BCRYPT_KEY_HANDLE hAlicePublicKey;   // Alice側の公開鍵
  BCRYPT_SECRET_HANDLE hSecret;        // シークレットのハンドル
 
  printf( "### Bob側のシークレット生成 開始 ##########################################\n" );
 
  // アルゴリズムプロバイダをオープンする
  NTSTATUS r = BCryptOpenAlgorithmProvider(
    &hAlgorithm, BCRYPT_ECDH_P521_ALGORITHM, MS_PRIMITIVE_PROVIDER, 0
  );
  if ( r != 0 ) printf( "BCryptOpenAlgorithmProvider失敗\n" );
 
  // Bob側秘密鍵をインポートする
  r = BCryptImportKeyPair(
    hAlgorithm,                                  // アルゴリズムプロバイダ
    nullptr,                                     // NULLを指定する
    BCRYPT_PRIVATE_KEY_BLOB,                     // 秘密鍵を指定する
    &hBobPrivateKey,                             // インポートした鍵
    Bob_PrivateKey, Bob_PrivateKeySize,          // バッファとバイト数
    0                                            // フラグ
  );
  if ( r != 0 ) printf( "BCryptImportKeyPair失敗\n" );
 
  // Alice側公開鍵をインポートする
  r = BCryptImportKeyPair(
    hAlgorithm,                                  // アルゴリズムプロバイダ
    nullptr,                                     // NULLを指定する
    BCRYPT_PUBLIC_KEY_BLOB,                      // 公開鍵を指定する
    &hAlicePublicKey,                            // インポートした鍵
    Alice_PublicKey, Alice_PublicKeySize,        // バッファとバイト数
    0                                            // フラグ
  );
  if ( r != 0 ) printf( "BCryptImportKeyPair失敗\n" );
 
  // Aliceの秘密鍵とBobの公開鍵からシークレットを生成する
  r = BCryptSecretAgreement(
    hBobPrivateKey,
    hAlicePublicKey,
    &hSecret,
    0
  );
  if ( r != 0 ) printf( "BCryptSecretAgreement失敗\n" );
 
  // 共通鍵の生成に使用するシークレットを取得する
  r = BCryptDeriveKey(
    hSecret,                                     // シークレットのハンドル
    BCRYPT_KDF_HASH,                             // とりあえずSHA1
    nullptr,                                     // 面倒だから省略
    Bob_SecretBuffer,                            // シークレットのバッファ
    sizeof( Bob_SecretBuffer ),                  // シークレットのバッファ
    &Bob_SecretSize,                             // サイズ
    0                                            // フラグ
  );
  if ( r != 0 )
    printf( "BCryptDeriveKey失敗\n" );
   else {
      printf( "BCryptDeriveKey成功   Bob側シークレット=" );
      for ( unsigned int i = 0; i < Bob_SecretSize; i++ )
        printf( "%02X", Bob_SecretBuffer[i] );
      printf( "\n" );
      printf( "Bob側シークレットのバイト長=%d\n", Bob_SecretSize );
 
      // デフォルトでSHA1が使われることから、シークレットは20バイトしかないことに注意
  }
 
  // 鍵を破棄する
  r = BCryptDestroyKey( hBobPrivateKey );
  if ( r != 0 ) printf( "BCryptDestroyKey失敗\n" );
  r = BCryptDestroyKey( hAlicePublicKey );
  if ( r != 0 ) printf( "BCryptDestroyKey失敗\n" );
 
  // シークレットのハンドルを破棄する
  r = BCryptDestroySecret( hSecret );
  if ( r != 0 ) printf( "BCryptDestroySecret失敗\n" );
 
  // アルゴリズムプロバイダを破棄する
  r = BCryptCloseAlgorithmProvider( hAlgorithm, 0 );
  if ( r != 0 ) printf( "BCryptCloseAlgorithmProvider失敗\n" );
 
  printf( "### Bob側のシークレット生成 終了 ##########################################\n" );
}
 
///////////////////////////////////////////////////////////////////////////////
// Alice側の暗号化
 
void Encrypt_Alice()
{
  BCRYPT_ALG_HANDLE hAlgorithm;	      // アルゴリズムプロバイダ
  BCRYPT_KEY_HANDLE hKey;             // 対称鍵
  unsigned char KeyObject[1024];      // キーオブジェクト
  unsigned long KeyObjectSize;        // キーオブジェクトのサイズ
  BCRYPT_KEY_LENGTHS_STRUCT KeyLength;// 暗号鍵のバイト長
  unsigned long WorkKeyLength;        // 暗号鍵のバイト長(作業用)
  unsigned char vIV[128];             // イニシャルベクタ
  unsigned long IVSize;               // イニシャルベクタのサイズ
  unsigned long Result;
  char vPlainText[64] = "abcdefghijklmnopqrstuvwxyz"// 平文
 
  printf( "### Alice側の暗号化 開始 ##################################################\n" );
 
  // アルゴリズムプロバイダをオープンする
  NTSTATUS r = BCryptOpenAlgorithmProvider(
    &hAlgorithm, BCRYPT_AES_ALGORITHM, MS_PRIMITIVE_PROVIDER, 0
  );
  if ( r != 0 )
    printf( "BCryptOpenAlgorithmProvider失敗\n" );
 
  // キーオブジェクトのサイズを取得する
  r = BCryptGetProperty(
    hAlgorithm, BCRYPT_OBJECT_LENGTH, (unsigned char*)( &KeyObjectSize ), sizeof( KeyObjectSize ), &Result, 0
  );
  if ( r != 0 )
    printf( "BCryptGetProperty失敗\n" );
 
  // イニシャルベクタのサイズを取得する
  r = BCryptGetProperty(
    hAlgorithm, BCRYPT_BLOCK_LENGTH, (unsigned char*)( &IVSize ), sizeof( IVSize ), &Result, 0
  );
  if ( r != 0 )
    printf( "BCryptGetProperty失敗\n" );
 
  // キーのサイズを取得する
  r = BCryptGetProperty(
    hAlgorithm, BCRYPT_KEY_LENGTHS, (unsigned char*)( &KeyLength ), sizeof( KeyLength ), &Result, 0
  );
  if ( r != 0 )
    printf( "BCryptGetProperty失敗\n" );
 
  // 使用するシークレットのサイズを決定する
  // (※シークレットは20バイトしかない。
  //      AESだと最小のシークレット長が16バイトであることから
  //      下記の処理で間に合うことに注意)
  WorkKeyLength = KeyLength.dwMinLength / 8;
 
  // 対称鍵を生成する
  r = BCryptGenerateSymmetricKey(
    hAlgorithm, &hKey, KeyObject, KeyObjectSize, Alice_SecretBuffer, WorkKeyLength, 0
  );
  if ( r != 0 )
    printf( "BCryptGenerateSymmetricKey失敗\n" );
 
  // イニシャルベクタを生成する
  for ( unsigned long i = 0; i < IVSize; i++ )
      vIV[i] = (unsigned char)i;    // 本当は乱数にする
 
  // 暗号化する
  r = BCryptEncrypt(
    hKey,                        // 鍵
    (unsigned char*)vPlainText,  // 平文
    sizeof( vPlainText ),        // 平文のサイズ
    nullptr,                     // 認証情報
    vIV, IVSize,                 // イニシャルベクタ
    EncryptData,                 // 暗号文
    sizeof( EncryptData ),       // 暗号文のバッファ長
    &EncryptDataSize,            // 出力された暗号文のバイト長
    0                            // フラグ
  );
  if ( r != 0 ) printf( "BCryptEncrypt失敗\n" );
 
  // 鍵を破棄する
  r = BCryptDestroyKey( hKey );
  if ( r != 0 ) printf( "BCryptDestroyKey失敗\n" );
 
  // アルゴリズムプロバイダを破棄する
  r = BCryptCloseAlgorithmProvider( hAlgorithm, 0 );
  if ( r != 0 ) printf( "BCryptCloseAlgorithmProvider失敗\n" );
 
  printf( "### Alice側の暗号化 終了 ##################################################\n" );
}
 
///////////////////////////////////////////////////////////////////////////////
// Bob側の復号
 
void Decrypt_Bob()
{
  BCRYPT_ALG_HANDLE hAlgorithm;	      // アルゴリズムプロバイダ
  BCRYPT_KEY_HANDLE hKey;             // 対称鍵
  unsigned char KeyObject[1024];      // キーオブジェクト
  unsigned long KeyObjectSize;        // キーオブジェクトのサイズ
  BCRYPT_KEY_LENGTHS_STRUCT KeyLength;// 暗号鍵のバイト長
  unsigned long WorkKeyLength;        // 暗号鍵のバイト長(作業用)
  unsigned char vIV[128];             // イニシャルベクタ
  unsigned long IVSize;               // イニシャルベクタのサイズ
  unsigned long Result;
  char vPlainText[128];               // 平文
 
  printf( "### Bob側の復号 開始 ######################################################\n" );
 
  // アルゴリズムプロバイダをオープンする
  NTSTATUS r = BCryptOpenAlgorithmProvider(
    &hAlgorithm, BCRYPT_AES_ALGORITHM, MS_PRIMITIVE_PROVIDER, 0
  );
  if ( r != 0 )
    printf( "BCryptOpenAlgorithmProvider失敗\n" );
 
  // キーオブジェクトのサイズを取得する
  r = BCryptGetProperty(
    hAlgorithm, BCRYPT_OBJECT_LENGTH, (unsigned char*)( &KeyObjectSize ), sizeof( KeyObjectSize ), &Result, 0
  );
  if ( r != 0 )
    printf( "BCryptGetProperty失敗\n" );
 
  // イニシャルベクタのサイズを取得する
  r = BCryptGetProperty(
    hAlgorithm, BCRYPT_BLOCK_LENGTH, (unsigned char*)( &IVSize ), sizeof( IVSize ), &Result, 0
  );
  if ( r != 0 )
    printf( "BCryptGetProperty失敗\n" );
 
  // キーのサイズを取得する
  r = BCryptGetProperty(
    hAlgorithm, BCRYPT_KEY_LENGTHS, (unsigned char*)( &KeyLength ), sizeof( KeyLength ), &Result, 0
  );
  if ( r != 0 )
    printf( "BCryptGetProperty失敗\n" );
 
  // 使用するシークレットのサイズを決定する
  // (※シークレットは20バイトしかない。
  //      AESだと最小のシークレット長が16バイトであることから
  //      下記の処理で間に合うことに注意)
  WorkKeyLength = KeyLength.dwMinLength / 8;
 
  // 対称鍵を生成する
  r = BCryptGenerateSymmetricKey(
    hAlgorithm, &hKey, KeyObject, KeyObjectSize, Bob_SecretBuffer, WorkKeyLength, 0
  );
  if ( r != 0 )
    printf( "BCryptGenerateSymmetricKey失敗\n" );
 
  // イニシャルベクタを生成する
  for ( unsigned long i = 0; i < IVSize; i++ )
      vIV[i] = (unsigned char)i;    // 本当は乱数にする
 
  // 復号する
  r = BCryptDecrypt(
    hKey,                        // 鍵
    EncryptData,                 // 暗号文
    EncryptDataSize,             // 暗号文のサイズ
    nullptr,                     // 認証情報
    vIV, IVSize,                 // イニシャルベクタ
    (unsigned char*)vPlainText,  // 平文
    sizeof( vPlainText ),        // 平文のサイズ
    &Result,                     // 出力された暗号文のバイト長
    0                            // フラグ
  );
  if ( r != 0 )
    printf( "BCryptEncrypt失敗\n" );
  else
    printf( "BCryptEncrypt成功 復号された平文=%s\n", vPlainText );
 
  // 鍵を破棄する
  r = BCryptDestroyKey( hKey );
  if ( r != 0 ) printf( "BCryptDestroyKey失敗\n" );
 
  // アルゴリズムプロバイダを破棄する
  r = BCryptCloseAlgorithmProvider( hAlgorithm, 0 );
  if ( r != 0 ) printf( "BCryptCloseAlgorithmProvider失敗\n" );
 
  printf( "### Bob側の復号 終了 ######################################################\n" );
}
 
///////////////////////////////////////////////////////////////////////////////
// main関数
 
int _tmain(int argc, _TCHAR* argv[])
{
  // Alice側の鍵生成処理
  CreateKeyPair_Alice();
  printf( "\n" );
 
  // Bob側の鍵生成処理
  CreateKeyPair_Bob();
  printf( "\n" );
 
  // 本来ここで、公開鍵を交換する処理を行う。
  // このプログラムではグローバル変数に格納しているだけなので、
  // 特に交換もクソもヘッタクレもない。
 
  // Alice側のシークレット生成処理
  GenSecert_Alice();
  printf( "\n" );
 
  // Bob側のシークレット生成処理
  GenSecert_Bob();
  printf( "\n" );
 
  // ここまでで、AliceとBobは同じシークレットを手に入れているはず
 
  // Alice側の暗号化処理
  Encrypt_Alice();
  printf( "\n" );
 
  // Bob側の復号処理
  Decrypt_Bob();
  printf( "\n" );
 
  return 0;
}

長い。いつになく長い。

一応、一番最初に示した図の通り、AliceとBobの間で鍵の交換を行い、その後Aliceで暗号文を生成してBobで復号する処理を行っている。

しかし実のところ、AliceとBobの処理の大部分はなったく同じことを行っているのであり、プログラム上は1つにすることができる。だが、AliceとBobを一緒にしてしまうとサンプルとしてわかりにくくなるかと思い、この例ではあえて分けて記載している。そのうえで、変数名とコメントだけはかき分けるようにしている。

だから、処理の内容については、プログラム中のコメントを上から順番に読んでいってもらえば大体わかるかと思われる。

実行例

あまり面白みはないが、実行例だけ示しておく。

### Alice側の鍵ペアの生成 開始 ############################################
BCryptExportKey成功 Alice側公開鍵のサイズ=140
BCryptExportKey成功 Alice側秘密鍵のサイズ=206
### Alice側の鍵ペアの生成 終了 ############################################

### Bob側の鍵ペアの生成 開始 ##############################################
BCryptExportKey成功 Bob側公開鍵のサイズ=140
BCryptExportKey成功 Bob側秘密鍵のサイズ=206
### Bob側の鍵ペアの生成 終了 ##############################################

### Alice側のシークレット生成 開始 ########################################
BCryptDeriveKey成功 Alice側シークレット=CD864A9A17271F736FCA2E8001C17A66950FC49F
Alice側シークレットのバイト長=20
### Alice側のシークレット生成 終了 ########################################

### Bob側のシークレット生成 開始 ##########################################
BCryptDeriveKey成功 Bob側シークレット=CD864A9A17271F736FCA2E8001C17A66950FC49F
Bob側シークレットのバイト長=20
### Bob側のシークレット生成 終了 ##########################################

### Alice側の暗号化 開始 ##################################################
### Alice側の暗号化 終了 ##################################################

### Bob側の復号 開始 ######################################################
BCryptEncrypt成功 復号された平文=abcdefghijklmnopqrstuvwxyz
### Bob側の復号 終了 ######################################################

開始・終了と言っているだけで、何をやっているのかわからない。

とりあえず、以下2つの記述でAliceとBobがめいめいに生成したシークレットが同一のものであることが注目すべき点である。

  1. 「BCryptDeriveKey成功 Alice側シークレット=CD864A9A17271F736FCA2E8001C17A66950FC49F」
  2. 「BCryptDeriveKey成功 Bob側シークレット=CD864A9A17271F736FCA2E8001C17A66950FC49F」

結果として、Alice側で暗号化された暗号文をBobが正しく復号することができている。

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


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