ホーム 主筆 その他ソフト その他情報 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年1月31日公開 

次はCNGを使ってハッシュ値の生成をやってみる。

全体の処理フロー

乱数の生成に比べると手順がだいぶ面倒なので、概要を図で示すことにする。

アルゴリズムプロバイダの取得(BCryptOpenAlgorithmProvider)と解放(BCryptCloseAlgorithmProvider)は乱数を取得するときと同じである。面倒なのは、ハッシュ値を算出するためにはハッシュオブジェクトなるものをもう1つ作らなければならない(BCryptCreateHash)ことである。

しかも、ハッシュオブジェクトを作るためには、そこで使うバッファを与えてやらなければならない上に、そのバッファ長はBCryptGetProperty関数で取得してやらなければならない。

BCryptCreateHash関数でハッシュオブジェクトを構築したら、それを指定してBCryptHashData関数を繰り返し呼び出し、ハッシュ値の算出対象となるデータを全て与える。一通りデータを与えたら、BCryptFinishHash関数で生成されたハッシュ値を取得する。

ところで、上記の図ではハッシュ値を1つ生成するごとにハッシュオブジェクトの構築と破棄を繰り返しているが、これはVistaと7ではハッシュオブジェクトの再利用を認めていないためである。Windows 8以降ではBCryptCreateHash関数のdwFlagsにBCRYPT_HASH_REUSABLE_FLAGを指定することで、オブジェクトを再利用できると書いてあるように見える。

しかし、バージョン間の互換性維持が面倒なこともあり試してみてはない。

 

使用例

上記に従い、プログラムにしてみる。

#include <stdio.h>
#include <stdlib.h>
#include <Windows.h>
 
// CNGのヘッダファイル
#include <Bcrypt.h>
 
int _tmain(int argc, _TCHAR* argv[])
{
  BCRYPT_ALG_HANDLE hAlgorithm;  // CNGのアルゴリズムプロバイダのハンドル
  BCRYPT_HASH_HANDLE hHash;      // ハッシュ値算出用オブジェクトのハンドル
  unsigned long ObjectSize = 0;  // ハッシュオブジェクトのサイズ
  unsigned long Result = 0;      // 作業用
  unsigned int HashLength = 0;   // ハッシュ値のサイズ
  unsigned char vSourceData[27] = "abcdefghijklmnopqrstuvwxyz";
 
  // アルゴリズムプロバイダを取得する
  NTSTATUS r = BCryptOpenAlgorithmProvider( &hAlgorithm, BCRYPT_MD5_ALGORITHM, MS_PRIMITIVE_PROVIDER, 0 );
  if ( r != 0 )
    printf( "BCryptOpenAlgorithmProvider失敗\n" );
  else
    printf( "BCryptOpenAlgorithmProvider成功\n" );
 
  // ハッシュ値算出用のオブジェクトのサイズを取得する
  r = BCryptGetProperty(
    hAlgorithm, BCRYPT_OBJECT_LENGTH, (PUCHAR)( &ObjectSize ), sizeof( ObjectSize ), &Result, 0 
  );
  if ( r != 0 )
    printf( "BCryptGetProperty失敗\n" );
  else
    printf( "BCryptGetProperty成功 ハッシュ値算出用のオブジェクトのサイズ=%d\n", ObjectSize );
 
  // ハッシュ値のサイズを取得する
  r = BCryptGetProperty(
    hAlgorithm, BCRYPT_HASH_LENGTH, (PUCHAR)( &HashLength ), sizeof( HashLength ), &Result, 0 
  );
  if ( r != 0 )
    printf( "BCryptGetProperty失敗\n" );
  else
    printf( "BCryptGetProperty成功 ハッシュ値のサイズ=%d\n", HashLength );
 
  // ハッシュ値算出用のオブジェクトのメモリ領域を確保する
  unsigned char *pHashObject = (PUCHAR)calloc( ObjectSize, sizeofunsigned char ) );
  if ( NULL == pHashObject )
    printf( "ハッシュ値算出用のオブジェクトのメモリ領域の確保に失敗\n" );
 
  // ハッシュ値を取得するためのバッファを確保する
  unsigned char *pHashValue = (PUCHAR)calloc( HashLength, sizeofunsigned char ) );
  if ( NULL == pHashValue )
    printf( "ハッシュ値を取得するためのバッファの確保に失敗\n" );
 
  // 例として、ハッシュ値の生成を3回繰り返してみる
  for ( int i = 0; i < 3; i++ ) {
    printf( "---------------------------------\n", i );
    printf( "ハッシュ値算出の%d回目\n", i );
 
    // ハッシュオブジェクトを構築する
    r = BCryptCreateHash( hAlgorithm, &hHash, pHashObject, ObjectSize, nullptr, 0, 0 );
    if ( r != 0 )
      printf( "BCryptCreateHash失敗 %d回目\n", i );
    else
      printf( "BCryptCreateHash成功 %d回目\n", i );
 
    // ハッシュ値算出対象となるデータを与える
    for ( int j = 0; j < 2; j++ ) {
      // ここは任意の回数だけ繰り返して、必要なデータを分割して与えることができる
      r = BCryptHashData( hHash, vSourceData, sizeof( vSourceData ) - 1, 0 );
      if ( r != 0 )
        printf( "BCryptHashData失敗 %d回目 データ%d個目\n", i, j );
      else
        printf( "BCryptHashData成功 %d回目 データ%d個目\n", i, j );
    }
 
    // ハッシュ値を取得する
    r = BCryptFinishHash( hHash, pHashValue, HashLength, 0 );
    if ( r != 0 )
      printf( "BCryptFinishHash 失敗 %d回目\n", i );
    else {
      printf( "BCryptFinishHash 成功 %d回目\nハッシュ値=", i );
      for ( unsigned int j = 0; j < HashLength; j++ )
        printf( "%02X", pHashValue[j] );
      printf( "\n" );
    }
 
    // ハッシュオブジェクトを破棄する
    r = BCryptDestroyHash( hHash );
    if ( r != 0 )
      printf( "BCryptDestroyHash失敗 %d回目\n", i );
    else
      printf( "BCryptDestroyHash成功 %d回目\n", i );
  }
 
  // アルゴリズムプロバイダを破棄する
  r = BCryptCloseAlgorithmProvider( hAlgorithm, 0 );
  if ( r != 0 )
    printf( "BCryptCloseAlgorithmProvider失敗\n" );
  else
    printf( "BCryptCloseAlgorithmProvider成功\n" );
 
  // メモリ領域を解放する
  free( pHashObject );
  free( pHashValue );
 
  return 0;
}

実行例

デバッガを当ててステップ実行するのはちょっと面倒だし、結果をいちいちprintfで表示するようにもしたのだから、このまま実行した結果だけを示す。

BCryptOpenAlgorithmProvider成功
BCryptGetProperty成功 ハッシュ値算出用のオブジェクトのサイズ=270
BCryptGetProperty成功 ハッシュ値のサイズ=16
---------------------------------
ハッシュ値算出の0回目
BCryptCreateHash成功 0回目
BCryptHashData成功 0回目 データ0個目
BCryptHashData成功 0回目 データ1個目
BCryptFinishHash 成功 0回目
ハッシュ値=55032B1BA8BC84B3755818C8A48EA031
BCryptDestroyHash成功 0回目
---------------------------------
ハッシュ値算出の1回目
BCryptCreateHash成功 1回目
BCryptHashData成功 1回目 データ0個目
BCryptHashData成功 1回目 データ1個目
BCryptFinishHash 成功 1回目
ハッシュ値=55032B1BA8BC84B3755818C8A48EA031
BCryptDestroyHash成功 1回目
---------------------------------
ハッシュ値算出の2回目
BCryptCreateHash成功 2回目
BCryptHashData成功 2回目 データ0個目
BCryptHashData成功 2回目 データ1個目
BCryptFinishHash 成功 2回目
ハッシュ値=55032B1BA8BC84B3755818C8A48EA031
BCryptDestroyHash成功 2回目
BCryptCloseAlgorithmProvider成功

元データとして「abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz」という文字列を与えて「55032B1BA8BC84B3755818C8A48EA031」というMD5のハッシュ値を得ている。これは、ほかのツールで算出しても同じ結果が得られるので、たぶんあっているのだろう。

 

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


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