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

2016年2月6日公開

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

プログラム例

#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 *pIV;
 
  // イニシャルベクタのバイト長(ブロック長と等しい)
  unsigned long IVLength;
 
  // 対称鍵生成元となるシークレット
  unsigned char *pSecretData;
 
  // 平文のデータ
  char Hirabun1[64] = "abcdefghijklmnopqrstuvwxyz";
 
  // 暗号文
  char Angoubun[512];
 
  // 生成された暗号文の長さ
  unsigned long AngoubunSize;
 
  // 復号した平文
  char Hirabun2[512];
 
  // 作業用
  unsigned long Result = 0;
 
  // 暗号利用モード
  TCHAR ChainModeBuf[64];
 
  // 認証情報
  BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO acmi;
 
  // Nonceのバッファ
  // (詳しく知らんが、Nonceは12バイトと決まっているらしい)
  unsigned char vNonce1[12];
  unsigned char vNonce2[12];
 
  // 認証タグのバッファ
  unsigned char vAuthTag[256];
 
  // 認証タグのサイズ
  BCRYPT_AUTH_TAG_LENGTHS_STRUCT AuthTagLen;
 
  // アルゴリズムプロバイダのハンドルを取得する
  NTSTATUS r = BCryptOpenAlgorithmProvider(
    &hAlgorithm, BCRYPT_AES_ALGORITHM, MS_PRIMITIVE_PROVIDER, 0
  );
  if ( r != 0 )
    printf( "BCryptOpenAlgorithmProvider失敗\n" );
  else
    printf( "BCryptOpenAlgorithmProvider成功\n" );
 
  // 暗号利用モードを変える
  _tcscpy_s( ChainModeBuf, BCRYPT_CHAIN_MODE_CCM );
  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_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 );
  }
 
  // 認証タグのサイズを取得する
  r = BCryptGetProperty(
    hAlgorithm, BCRYPT_AUTH_TAG_LENGTH, (unsigned char*)( &AuthTagLen ), sizeof( AuthTagLen ), &Result, 0
  );
  if ( r != 0 )
    printf( "BCryptGetProperty失敗\n" );
  else
    printf( "BCryptGetProperty成功 認証タグのバイト長=%d\n", AuthTagLen.dwMaxLength );
 
  // 一応確認する
  assert( sizeof( vAuthTag ) > AuthTagLen.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" );
 
  // 認証情報を設定する
  for ( int i = 0; i < sizeof( vNonce1 ); i++ ) {
    vNonce1[i] = i;               // 乱数である必要はない
	vNonce2[i] = vNonce1[i];      // 後で使うため
  }
  BCRYPT_INIT_AUTH_MODE_INFO( acmi );  // 初期化
  acmi.pbNonce = vNonce1;              // Nonceの情報が必要
  acmi.cbNonce = sizeof( vNonce1 );    // Nonceのサイズ
 
  // 暗号化時に生成される認証タグを取得するバッファを指定する
  acmi.pbTag = vAuthTag;
  acmi.cbTag = AuthTagLen.dwMaxLength;
 
  // 事前に暗号文のバイト数を取得する
  r = BCryptEncrypt(
    hKey,                                         // 鍵
    (unsigned char*)Hirabun1, sizeof( Hirabun1 ), // 平文
    &acmi,                                        // 認証情報
    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 ), // 平文
    &acmi,                                        // 認証情報
    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" );
	printf( "認証タグ=" );
    for ( unsigned int j = 0; j < AuthTagLen.dwMaxLength; j++ )
      printf( "%02X", (unsigned char)vAuthTag[j] );
    printf( "\n" );
  }
  AngoubunSize = Result;
 
  printf( "--- 復号 ---------------------------------------------\n" );
 
  // 認証情報の初期化
  BCRYPT_INIT_AUTH_MODE_INFO( acmi );  // 初期化
  acmi.pbNonce = vNonce2;              // Nonceの情報が必要
  acmi.cbNonce = sizeof( vNonce2 );    // Nonceのサイズ
 
  // 暗号化時に取得された認証タグを指定する
  // (認証タグが一致しないとSTATUS_AUTH_TAG_MISMATCHで失敗する)
  acmi.pbTag = vAuthTag;
  acmi.cbTag = AuthTagLen.dwMaxLength;
 
  // 平文のサイズを取得する
  r = BCryptDecrypt(
    hKey,                                         // 鍵
    (unsigned char*)Angoubun,                     // 暗号文のバッファ
	AngoubunSize,                                 // 暗号文のサイズ(前半部分)
    &acmi,                                        // 認証情報
	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,                                 // 暗号文のサイズ
    &acmi,                                        // 認証情報
	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成功
BCryptSetProperty成功
BCryptGetProperty成功 キーオブジェクトのバイト長=618
BCryptGetProperty成功 イニシャルベクタのバイト長=16
BCryptGetProperty成功 キーのバイト長=32
BCryptGetProperty成功 認証タグのバイト長=16
BCryptGenerateSymmetricKey成功
--- 暗号化 -------------------------------------------
BCryptEncrypt成功 予測されるバイト数=64
BCryptEncrypt成功 バイト数=64, 暗号文=2E68843FEFAC5DE4
DC013F1EB613BB22183AB6A733CD7F4D4DFB941D3EA75B2
4F2F40ECF6812CFE4B11BF1C542B6CD8B64EA221287D8355
A6F235DE1CBA6950C
認証タグ=7A1E8C8883A092EC4BD7593F58967811
--- 復号 ---------------------------------------------
BCryptDecrypt成功 予測される平文のバイト数=64
BCryptDecrypt成功 バイト数=64, 平文=abcdefghijklmnopqrstuvwxyz
BCryptDestroyKey成功
BCryptCloseAlgorithmProvider成功

概説

CBCモードとの差異は、BCryptEncrypt関数とBCryptDecrypt関数に対して、BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO構造体のアドレスを渡していること、イニシャルベクタの指定がないことである。

BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO構造体で指定するNonceと認証タグについては、AES-GCMのプログラム例のページに記載しているものと同じらしい。

イニシャルベクタだが、これはどうも無視されるらしい。指定してもしなくても、イニシャルベクタの中身が何であっても暗号/復号に差し支えない上に、鍵とNonceが同じであれば毎回同じ結果が返ってくる。

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

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


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