ホーム 主筆 その他ソフト その他情報 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:NULL暗号の実装

2016年2月27日公開

当然といえば当然だが、CNGが標準で提供しているMicrosoft Primitive ProviderにはNULL暗号は含まれていない。

ここではアルゴリズムプロバイダの実装例として、NULL暗号を行うものを作ってみる。

エントリポイント

アルゴリズムプロバイダとして機能するためには、下記の関数をエクスポートするDLLを作らなければならない。

// インタフェースとなる関数のアドレスを応答する
NTSTATUS WINAPI GetCipherInterface(
  LPWSTR pszProviderName,                         // プロバイダ名
  LPWSTR pszAlgId,                                // アルゴリズム名
  BCRYPT_CIPHER_FUNCTION_TABLE **ppFunctionTable, // 関数ポインタのテーブル
  DWORD dwFlags                                   // フラグ
)
{
  ……
}

関数のプロトタイプはbcrypt_provider.hで宣言されている。なお、上記では関数アノテーションなるものの記述は除外している。

やることととしては、クライアントから呼び出されるBCryptOpenAlgorithmProvider関数やBCryptEncrypt関数といったCNGのインタフェースが使用されたときに呼び出される、対応する関数のポインタを返すことである。

具体的には、BCRYPT_CIPHER_FUNCTION_TABLE構造体に関数ポインタを指定することである。

処理内容としては、以下のようになる。

// CNGに公開する関数のアドレスを保持するテーブル
BCRYPT_CIPHER_FUNCTION_TABLE SymmCipherFunctionTable;

// インタフェースとなる関数のアドレスを応答する
NTSTATUS WINAPI GetCipherInterface(
  LPWSTR pszProviderName,                         // プロバイダ名
  LPWSTR pszAlgId,                                // アルゴリズム名
  BCRYPT_CIPHER_FUNCTION_TABLE **ppFunctionTable, // 関数ポインタのテーブル
  DWORD dwFlags                                   // フラグ
)
{
  NTSTATUS Status = STATUS_NOT_SUPPORTED;

  // 関数ポインタを格納した構造体を準備して、そのアドレスを返す
  BCRYPT_CIPHER_FUNCTION_TABLE work = {
    BCRYPT_CIPHER_INTERFACE_VERSION_1,
    SymmOpenCipherProvider,
    SymmGetCipherProperty,
    SymmSetCipherProperty,
    SymmCloseCipherProvider,
    SymmGenerateKey,
    SymmEncrypt,
    SymmDecrypt,
    SymmImportKey,
    SymmExportKey,
    SymmDuplicateKey,
    SymmDestroyKey
  };
  SymmCipherFunctionTable = work;
 
  // "NULL"暗号をサポートする
  if( wcscmp( _T( "TestCAP-NULL" ), pszAlgId ) == 0 ) {
    (*ppFunctionTable) = &SymmCipherFunctionTable;
    Status = STATUS_SUCCESS;
  }
 
  return Status;
}

上記の関数名などは、サンプルにあったものをそのまま使っているから、ほぼ読んで字のごとくの名前になってはいるが、一応どういうものを指定するのかを示す。

BCRYPT_CIPHER_FUNCTION_TABLE構造体のメンバ名 指定する関数
OpenAlgorithmProvider BCryptOpenAlgorithmProvider関数が呼ばれたときに呼び出される関数を指定する。
GetProperty BCryptGetProperty関数が呼ばれたときに呼び出される関数を指定する。
SetProperty BCryptSetProperty関数が呼ばれたときに呼び出される関数を指定する。
CloseAlgorithmProvider BCryptCloseAlgorithmProvider関数が呼ばれたときに呼び出される関数を指定する。
GenerateKey BCryptGenerateSymmetricKey関数が呼ばれたときに呼び出される関数を指定する。
Encrypt BCryptEncrypt関数が呼ばれたときに呼び出される関数を指定する。
Decrypt BCryptDecrypt関数が呼ばれたときに呼び出される関数を指定する。
ImportKey BCryptImportKey関数が呼ばれたときに呼び出される関数を指定する。
ExportKey BCryptExportKey関数が呼ばれたときに呼び出される関数を指定する。
DuplicateKey BCryptDuplicateKey関数が呼ばれたときに呼び出される関数を指定する。
DestroyKey BCryptDestroyKey関数が呼ばれたときに呼び出される関数を指定する。

一応、上の例では引数に指定されるアルゴリズム名(登録時に指定する奴だ)を確認してはいるが、使い捨てのサンプルだと思えばどうでもよいことだと思われる。

SymmOpenCipherProvider

BCryptOpenAlgorithmProvider関数に対応する関数を定義する。

NTSTATUS WINAPI SymmOpenCipherProvider(
  BCRYPT_ALG_HANDLE *phAlgorithm,  // アルゴリズムプロバイダのハンドル
  LPCWSTR pszAlgId,                // アルゴリズム名
  DWORD dwFlags                    // フラグ
)
{
  (*phAlgorithm) = malloc( 4 );
  return STATUS_SUCCESS;
}

アルゴリズムプロバイダが動作する上で必要になるバッファ領域を確保してハンドルとして返す処理を行う。

ハンドルとして何を返すのかは実装上の勝手である。NULL暗号だと思うと強いて何かをする必要もないのだが、とりあえず無駄にメモリ領域を確保して返しておく。

SymmGetCipherProperty

BCryptGetProperty関数に対応する関数を定義する。

NTSTATUS WINAPI SymmGetCipherProperty(
  BCRYPT_HANDLE hObject, // アルゴリズムプロバイダや鍵のハンドル
  LPCWSTR pszProperty,   // プロパティ名
  PUCHAR pbOutput,       // 出力バッファ
  ULONG cbOutput,        // 出力バッファのサイズ
  ULONG *pcbResult,      // 出力バッファに書き込んだサイズ
  ULONG dwFlags          // フラグ
)
{
  // オブジェクト長の取得
  // 明示的にオブジェクト長の取得が行われなくても、
  // CNGから勝手に呼び出されるため何らかのサポートが必要になる。
  if ( !_tcscmp( pszProperty, BCRYPT_OBJECT_LENGTH ) ) {
    // 必要になるバッファ長を設定する
    (*pcbResult) = sizeof( DWORD );
 
    // NULLが指定されていたらデータそのものを返す必要がない
    if ( NULL == pbOutput )
      return STATUS_SUCCESS;

    // バッファが足りない場合は失敗
    if ( cbOutput < (*pcbResult) )
      return STATUS_BUFFER_TOO_SMALL;
 
    // オブジェクトなんかいらんが、とりあえず4を返しておく。
    *( (DWORD*)pbOutput ) = 4;
    return STATUS_SUCCESS;
  }
 
  // 別に指定できるようなプロパティなどない
  return STATUS_NOT_IMPLEMENTED;
}

実装するアルゴリズムに関連するプロパティ値を応答する処理を行う。

ここの例で実装するものがNULL暗号だと思えば、もとより設定すべきプロパティなどないようにも思われる。であれば、無条件にSTATUS_NOT_IMPLEMENTEDを返してもいいと思ったのだが実はそうではない。

クライアントが呼び出さなくても、CNGがpszPropertyにBCRYPT_OBJECT_LENGTHを指定してこの関数を呼び出すのである。そこで適当な値が取得できないといろいろと処理が失敗するらしい。

だから上記では、必要もないのにオブジェクト長を取得できるように処理を実装している。

SymmSetCipherProperty

BCryptSetProperty関数に対応する関数を定義する。

// プロパティ値の設定
NTSTATUS WINAPI SymmSetCipherProperty(
  BCRYPT_HANDLE hObject, // アルゴリズムプロバイダや鍵のハンドル
  LPCWSTR pszProperty,   // プロパティ名
  PUCHAR pbInput,        // バッファ
  ULONG cbInput,         // バッファ長
  ULONG dwFlags          // フラグ
)
{
  // 別に指定できるようなプロパティなどない
  return STATUS_NOT_IMPLEMENTED;
}

実装するアルゴリズムに関連するプロパティ値を更新する処理を行う。

ここの例ではさすがに実装すべきことがない。

SymmCloseCipherProvider

BCryptCloseAlgorithmProvider関数に対応する関数を定義する。

// アルゴリズムプロバイダのハンドルを開放する
NTSTATUS WINAPI SymmCloseCipherProvider(
  BCRYPT_ALG_HANDLE hAlgorithm,  // アルゴリズムプロバイダのハンドル
  DWORD dwFlags                  // フラグ
)
{
  // 確保したメモリ領域を解放する
  free( hAlgorithm );
 
  return STATUS_SUCCESS;
}

SymmOpenCipherProviderで返したハンドルが指定されるので、不要となったオブジェクトを開放する処理を行う。

ここではmallocで確保したメモリ領域の開放を行っている。

SymmGenerateKey

BCryptGenerateSymmetricKey関数に対応する関数を定義する。

// キーの生成
NTSTATUS WINAPI SymmGenerateKey(
  BCRYPT_ALG_HANDLE hAlgorithm, // アルゴリズムプロバイダのハンドル
  BCRYPT_KEY_HANDLE *phKey,     // 生成した鍵のハンドル
  PUCHAR pbKeyObject,           // キーオブジェクト
  ULONG cbKeyObject,            // キーオブジェクトのサイズ
  PUCHAR pbSecret,              // シークレット
  ULONG cbSecret,               // シークレットのサイズ
  ULONG dwFlags                 // フラグ
)
{
  // 意味はないが、何かを返しておく
  (*phKey) = malloc( 4 );
  return STATUS_SUCCESS;
}

シークレットを元に初期状態の鍵を生成する処理を実装する。

まともな暗号なら鍵を生成する必要がない、なとどいうことはないはずだが、ここで作るものはいかんせんNULL暗号である。やることがない。

とりあえず、何か適当にメモリ領域を確保して、ハンドルとして返すだけ返しておく。

SymmEncrypt

BCryptEncrypt関数に対応する関数を定義する。

// 暗号化する
NTSTATUS WINAPI SymmEncrypt(
  BCRYPT_KEY_HANDLE hKey, // 鍵のハンドル
  PUCHAR pbInput,         // 平文のバッファ
  ULONG cbInput,          // 平文のバイト長
  VOID *pPaddingInfo,     // パディングの情報
  PUCHAR pbIV,            // イニシャルベクタ
  ULONG cbIV,             // イニシャルベクタのバイト長
  PUCHAR pbOutput,        // 暗号文のバッファ
  ULONG cbOutput,         // 暗号文のバッファ長
  ULONG *pcbResult,       // 暗号文として出力したバイト数
  ULONG dwFlags           // フラグ
)
{
  // 出力バッファが指定されている場合は値をコピーする
  if ( NULL != pbOutput )
    memmove_s( pbOutput, cbOutput, pbInput, cbInput );
 
  // pcbResultが指定されている場合は、出力するバイト数を設定する
  if ( NULL != pcbResult )
    (*pcbResult) = cbInput;
 
  return STATUS_SUCCESS;
}

鍵と平文から暗号文を作り出す処理を実装する。

ここの例では、アルゴリズムの本体はmemmove_sで実装している。というか、どうせ何の入力チェックも行っていないのであれば、開き直ってmemcpy_sでも同じことだと思われる。

SymmDecrypt

BCryptDecrypt関数に対応する関数を定義する。

NTSTATUS WINAPI SymmDecrypt(
  BCRYPT_KEY_HANDLE hKey, // 鍵のハンドル
  PUCHAR pbInput,         // 暗号文のバッファ
  ULONG cbInput,          // 暗号文のバイト長
  VOID *pPaddingInfo,     // パディングの情報
  PUCHAR pbIV,            // イニシャルベクタ
  ULONG cbIV,             // イニシャルベクタのバイト長
  PUCHAR pbOutput,        // 平文のバッファ
  ULONG cbOutput,         // 平文のバッファ長
  ULONG *pcbResult,       // 平文として出力したバイト数
  ULONG dwFlags           // フラグ
)
{
  // 出力バッファが指定されている場合は値をコピーする
  if ( NULL != pbOutput )
    memmove_s( pbOutput, cbOutput, pbInput, cbInput );
 
  // pcbResultが指定されている場合は、出力するバイト数を設定する
  if ( NULL != pcbResult )
    (*pcbResult) = cbInput;
 
  return STATUS_SUCCESS;
}

鍵と暗号文から平文を作り出す処理を実装する。

NULL暗号だと、暗号と復号で行うべきことの間に差異がない。

SymmImportKey

BCryptImportKey関数に対応する関数を定義する。

// 鍵をインポートする
NTSTATUS WINAPI SymmImportKey(
  BCRYPT_ALG_HANDLE hAlgorithm,  // アルゴリズムプロバイダのハンドル
  BCRYPT_KEY_HANDLE hImportKey,  // 鍵のハンドル
  LPCWSTR pszBlobType,           // 入力バイトの種別
  BCRYPT_KEY_HANDLE *phKey,      // インポートした鍵のハンドル 
  PUCHAR pbKeyObject,            // キーオブジェクト
  ULONG cbKeyObject,             // キーオブジェクトのバッファ長
  PUCHAR pbInput,                // 入力データのバッファ
  ULONG cbInput,                 // 入力データのバッファ長
  ULONG dwFlags                  // フラグ
)
{
  return STATUS_NOT_IMPLEMENTED;
}

pbInputに指定されたバイト配列を元に、鍵を作り出す処理を実装する。

例として何らかの機能を実装してもよかったのだが、煩雑になるだけなのでここはSTATUS_NOT_IMPLEMENTEDを返して終わりにしてしまう。

SymmExportKey

BCryptExportKey関数に対応する関数を定義する。

// 鍵をエクスポートする
NTSTATUS WINAPI SymmExportKey(
  BCRYPT_KEY_HANDLE hKey,       // アルゴリズムプロバイダのハンドル
  BCRYPT_KEY_HANDLE hExportKey, // 鍵のハンドル
  LPCWSTR pszBlobType,          // 出力するバイト配列の種別
  PUCHAR pbOutput,              // エクスポートする対象となる出力バッファ
  ULONG cbOutput,               // 出力バッファのバイト長
  ULONG *pcbResult,             // 出力したバイト数
  ULONG dwFlags                 // フラグ
)
{
  return STATUS_NOT_IMPLEMENTED;
}

鍵の内容を指定されたバッファに吐き出す処理を実装する。当然、SymmImportKeyで読み込めるようなフォーマットを自分で考えて出力しなければならない。

SymmImportKeyと同様にSTATUS_NOT_IMPLEMENTEDを返して終わりにしてしまう。

SymmDuplicateKey

BCryptDuplicateKey関数に対応する関数を定義する。

// 鍵を複製する
NTSTATUS WINAPI SymmDuplicateKey(
  BCRYPT_KEY_HANDLE hKey,      // 鍵のハンドル
  BCRYPT_KEY_HANDLE *phNewKey, // 複製した鍵のハンドル
  PUCHAR pbKeyObject,          // キーオブジェクト
  ULONG cbKeyObject,           // キーオブジェクトのバイト長
  ULONG dwFlags                // フラグ
)
{
  return STATUS_NOT_IMPLEMENTED;
}

鍵の複製を行う処理を実装する。

SymmImportKeyと同様にSTATUS_NOT_IMPLEMENTEDを返して終わりにしてしまう。

SymmDestroyKey

BCryptDestroyKey関数に対応する関数を定義する。

// 鍵を破棄する
NTSTATUS WINAPI SymmDestroyKey(
	__inout BCRYPT_KEY_HANDLE hKey
)
{
  free( hKey );
  return STATUS_SUCCESS;
}

鍵のオブジェクトを破棄する処理を実装する。

SymmGenerateKey関数では単純にmallocだけしてそのアドレスをハンドルとして返しているので、ここではメモリ領域のfreeのみを行う。

プログラム全体

ここにプログラムの全体を張り付けるとページが長くなるので省略する。

 

 

 

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


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