ホーム 主筆 その他ソフト その他情報 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:RC4のプログラム例

2016年2月6日公開

Cryptography API: Next Generationを使い、RC4で暗号/復号を行うプログラム例を示す。

プログラム例

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

int _tmain(int argc, _TCHAR* argv[])
{
  // アルゴリズムプロバイダのハンドル
  BCRYPT_ALG_HANDLE hAlgorithm;
 
  // 対称鍵のハンドル
  BCRYPT_KEY_HANDLE hKey;
 
  // キーオブジェクト
  unsigned char *pKeyObject;
 
  // キーオブジェクトのバイト長
  unsigned long KeyObjectLength;
 
  // 暗号鍵のバイト長(シークレットのバイト長に等しい)
  BCRYPT_KEY_LENGTHS_STRUCT KeyLength;
 
  // イニシャルベクタのバイト長(ブロック長と等しい)
  unsigned long IVLength;
 
  // 対称鍵生成元となるシークレット
  unsigned char *pSecretData;
 
  // 平文のデータ
  char Hirabun1[64] = "abcdefghijklmnopqrstuvwxyz";
 
  // 暗号文
  char Angoubun[512];
 
  // 暗号文のサイズ
  unsigned long AngoubunSize = 0;
 
  // 復号した平文
  char Hirabun2[512];
 
  // 作業用
  unsigned long Result = 0;
 
  // アルゴリズムプロバイダのハンドルを取得する
  NTSTATUS r = BCryptOpenAlgorithmProvider(
    &hAlgorithm, BCRYPT_RC4_ALGORITHM, MS_PRIMITIVE_PROVIDER, 0
  );
  if ( r != 0 )
    printf( "BCryptOpenAlgorithmProvider失敗\n" );
  else
    printf( "BCryptOpenAlgorithmProvider成功\n" );
 
  // キーオブジェクトのサイズを取得する
  r = BCryptGetProperty(
    hAlgorithm, BCRYPT_OBJECT_LENGTH, (unsigned char*)( &KeyObjectLength ), sizeof( KeyObjectLength ), &Result, 0
  );
  if ( r != 0 )
    printf( "BCryptGetProperty失敗\n" );
  else
    printf( "BCryptGetProperty成功 キーオブジェクトのバイト長=%d\n", KeyObjectLength );
 
  // ブロックサイズのサイズを取得する
  r = BCryptGetProperty(
    hAlgorithm, BCRYPT_BLOCK_LENGTH, (unsigned char*)( &IVLength ), sizeof( IVLength ), &Result, 0
  );
  if ( r != 0 )
    printf( "BCryptGetProperty失敗\n" );
  else
    printf( "BCryptGetProperty成功 ブロックサイズのバイト長=%d\n", IVLength );
 
  // キーのサイズを取得する
  r = BCryptGetProperty(
    hAlgorithm, BCRYPT_KEY_LENGTHS, (unsigned char*)( &KeyLength ), sizeof( KeyLength ), &Result, 0
  );
  if ( r != 0 )
    printf( "BCryptGetProperty失敗\n" );
  else {
    KeyLength.dwMaxLength /= 8;
    printf( "BCryptGetProperty成功 キーのバイト長=%d\n", KeyLength.dwMaxLength );
  }
 
  // 必要なメモリ領域を確保する
  pKeyObject = (unsigned char*)( malloc( KeyObjectLength ) );
  pSecretData = (unsigned char*)( malloc( KeyLength.dwMaxLength ) );
 
  // 対称鍵生成元となるシークレットを生成する
  for ( unsigned long i = 0; i < KeyLength.dwMaxLength; i++ )
    pSecretData[i] = rand();    // 本当は、乱数はrandを使ってはいけない
 
  printf( "--- 暗号化 -------------------------------------------\n" );
 
  // 対称鍵を生成する
  r = BCryptGenerateSymmetricKey(
    hAlgorithm, &hKey, pKeyObject, KeyObjectLength, pSecretData, KeyLength.dwMaxLength, 0
  );
  if ( r != 0 )
    printf( "BCryptGenerateSymmetricKey失敗\n" );
  else
    printf( "BCryptGenerateSymmetricKey成功\n" );
 
  // 事前に暗号文のバイト数を取得する
  r = BCryptEncrypt(
    hKey,                                         // 鍵
    (unsigned char*)Hirabun1, sizeof( Hirabun1 ), // 平文
    nullptr,                                      // 認証情報
    nullptr, 0,                                   // イニシャルベクタ(指定しない)
    nullptr, 0,                                   // 暗号文
    &Result,                                      // 出力された暗号文のバイト長
    0                                             // フラグ
  );
  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,                                      // 出力された暗号文のバイト長
    0                                             // フラグ
  );
  if ( r != 0 )
    printf( "BCryptEncrypt失敗\n" );
  else {
    printf( "BCryptEncrypt成功 バイト数=%d, 暗号文=", Result );
    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" );
 
  printf( "--- 復号 ---------------------------------------------\n" );
 
  // 対称鍵を生成する
  r = BCryptGenerateSymmetricKey(
    hAlgorithm, &hKey, pKeyObject, KeyObjectLength, pSecretData, KeyLength.dwMaxLength, 0
  );
  if ( r != 0 )
    printf( "BCryptGenerateSymmetricKey失敗\n" );
  else
    printf( "BCryptGenerateSymmetricKey成功\n" );
 
  // 平文のサイズを取得する
  r = BCryptDecrypt(
    hKey,                                         // 鍵
    (unsigned char*)Angoubun, AngoubunSize,       // 暗号文のバッファ
    nullptr,                                      // 認証情報
    nullptr, 0,                                   // イニシャルベクタ(指定しない)
    nullptr, 0,                                   // 平文
    &Result,                                      // 出力された平文のバイト長
    0                                             // フラグ
  );
  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,                                      // 出力された平文のバイト長
    0                                             // フラグ
  );
  if ( r != 0 )
    printf( "BCryptDecrypt失敗\n" );
  else
    printf( "BCryptDecrypt成功 バイト数=%d, 平文=%s\n", Result, 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" );
 
  // メモリ領域を解放する
  free( pKeyObject );
  free( pSecretData );
 
  return 0;
}

実行結果

BCryptOpenAlgorithmProvider成功
BCryptGetProperty成功 キーオブジェクトのバイト長=630
BCryptGetProperty成功 ブロックサイズのバイト長=1
BCryptGetProperty成功 キーのバイト長=64
--- 暗号化 -------------------------------------------
BCryptGenerateSymmetricKey成功
BCryptEncrypt成功 予測されるバイト数=64
BCryptEncrypt成功 バイト数=64, 暗号文=BEAFF39E9ED2ED40
C6984F038FFF4D3E86A3328CBF5C7554CFFDFCC8390C4089
F047585CD83BB6ADDE779974395AD49A956C42F06F7CA175
26DA82730E2A6164
BCryptDestroyKey成功
--- 復号 ---------------------------------------------
BCryptGenerateSymmetricKey成功
BCryptDecrypt成功 予測される平文のバイト数=64
BCryptDecrypt成功 バイト数=64, 平文=abcdefghijklmnopqrstuvwxyz
BCryptDestroyKey成功
BCryptCloseAlgorithmProvider成功

補足

RC4の場合、BCryptEncrypt関数やBCryptDecrypt関数が呼び出される都度、キーオブジェクトの状態が更新されるらしい。

他のアルゴリズム、例えばAESのCBCモードCFBモードでは、暗号文は鍵とイニシャルベクタと平文によって決定される。復号する場合も同様です。BCryptEncrypt関数やBCryptDecrypt関数が複数回呼び出される場合には、イニシャルベクタに指定したバッファの中身が更新され、それにより同じ平文を暗号化したとしても、異なる暗号文が生成されるようになっている。

AESのCCMモードGCMモードの場合は、イニシャルベクタの代わりにNonceが使用される。

ECBモードの場合はイニシャルベクタもNonceも使用されない。そのため、同じ平文からは毎回同じ暗号文が得られる。

翻ってRC4の場合はイニシャルベクタもNonceもないが、暗号文は異なったものが生成される。BCryptEncrypt関数やBCryptDecrypt関数が複数回呼び出される場合には、何らかの方法で状態を引き継がない限り実現できないはずだが、一見すると更新される値がないように見受けられる。

結局、指定しているものがキーオブジェクトしかないため、キーオブジェクト内の値が更新されているとみるべきのようだ。

だから、上記のプログラムでは暗号化時と復号時とで、同じシークレットを元にキーを作り直している。まぁ、その方が実装例として本来あるべき姿なのかもしれないが。

<< 「CNG:共通鍵暗号アルゴリズムの比較」に戻る

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


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