ホーム 主筆 その他ソフト その他情報 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:トリプルDES ECBモードのプログラム例

2016年2月6日公開

Cryptography API: Next Generationを使い、トリプルDES ECBモードで暗号/復号を行うプログラム例を示す。

プログラム例

#include <stdio.h>
#include <stdlib.h>
#include <tchar.h>
#include <assert.h>
#include <Windows.h>
#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 char *pSecretData;

  // 平文のデータ
  char Hirabun1[64] = "abcdefghijklmnopqrstuvwxyz";
 
  // 暗号文
  char Angoubun[512];
 
  // 生成された暗号文の長さ
  unsigned long AngoubunSize;
 
  // 復号した平文
  char Hirabun2[512];
 
  // 作業用
  unsigned long Result = 0;
 
  // 暗号利用モード
  TCHAR ChainModeBuf[64];
 
  // アルゴリズムプロバイダのハンドルを取得する
  NTSTATUS r = BCryptOpenAlgorithmProvider(
    &hAlgorithm, BCRYPT_3DES_ALGORITHM, MS_PRIMITIVE_PROVIDER, 0
  );
  if ( r != 0 )
    printf( "BCryptOpenAlgorithmProvider失敗\n" );
  else
    printf( "BCryptOpenAlgorithmProvider成功\n" );
 
  // 暗号利用モードを変える
  _tcscpy_s( ChainModeBuf, BCRYPT_CHAIN_MODE_ECB );
  r = BCryptSetProperty(
    hAlgorithm, BCRYPT_CHAINING_MODE, (unsigned char*)ChainModeBuf, sizeof( ChainModeBuf ), 0
  );
  if ( r != 0 )
    printf( "BCryptSetProperty失敗\n" );
  else
    printf( "BCryptSetProperty成功\n", ChainModeBuf );
 
  // キーオブジェクトのサイズを取得する
  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_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を使ってはいけない

  // 対称鍵を生成する
  r = BCryptGenerateSymmetricKey(
    hAlgorithm, &hKey, pKeyObject, KeyObjectLength, pSecretData, KeyLength.dwMaxLength, 0
  );
  if ( r != 0 )
    printf( "BCryptGenerateSymmetricKey失敗\n" );
  else
    printf( "BCryptGenerateSymmetricKey成功\n" );
 
  printf( "--- 暗号化 -------------------------------------------\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 ( int j = 0; j < Result; j++ )
      printf( "%02X", (unsigned char)Angoubun[j] );
    printf( "\n" );
  }
  AngoubunSize = Result;
 
  printf( "--- 復号 ---------------------------------------------\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 );
 
  // 一応確認しておく
  assert( sizeof( Hirabun2 ) > 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;
}

実行結果

BBCryptOpenAlgorithmProvider成功
BCryptSetProperty成功
BCryptGetProperty成功 キーオブジェクトのバイト長=522
BCryptGetProperty成功 キーのバイト長=24
BCryptGenerateSymmetricKey成功
--- 暗号化 -------------------------------------------
BCryptEncrypt成功 予測されるバイト数=64
BCryptEncrypt成功 バイト数=64, 暗号文=2760E43197568FD282
9ACB80AE75FEB3620458ECA6598DDB1F4586ED6F8ABA7EBD
BDECD0395CF178BDBDECD0395CF178BDBDECD0395CF178B
DBDECD0395CF178
--- 復号 ---------------------------------------------
BCryptDecrypt成功 予測されるバイト数=64
BCryptDecrypt成功 バイト数=64, 平文=abcdefghijklmnopqrstuvwxyz
BCryptDestroyKey成功
BCryptCloseAlgorithmProvider成功

補足

ECBモードの場合、イニシャルベクタは使用しない。暗号化や復号を行うときにイニシャルベクタを指定していると、STATUS_INVALID_PARAMETERでBCryptEncrypt関数やBCryptDecrypt関数が失敗する。

ECBモードの場合、入力となる平文が同じだとまったく同じ暗号文が出力される。上記のプログラムでは、平文の前半26文字はabcdefghijklmnopqrstuvwxyzであるが、残りの38バイトは0x00で埋められている。

なので、生成された暗号文を8バイト(=ブロック長)で改行すると下記のようになる。

2760E43197568FD2
829ACB80AE75FEB3
620458ECA6598DDB
1F4586ED6F8ABA7E
BDBDECD0395CF178
BDBDECD0395CF178
BDBDECD0395CF178
BDBDECD0395CF178

後半部分が全部同じビットパターンになっている。これでは暗号の意味がない。

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

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


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