コロナウィルスで世間がなにかと騒がしいですが、相変わらず淡々とサイバーセキュリティな私です。
コロナウィルス対策?1月末頃の段階で、政治家は与党は観光客ガー、野党は花見の話しかしていなかったので、この時既に「対策する気ないみたいだから、コロナは罹っても耐える。」で腹くくって、筋肉体操で体を鍛えています。筋肉は裏切らないwww
とまあ、時事と政治の皮肉とマッチョの話で入るという・・・、本当に技術のブログかこれ?
WindowsのCyptAPIを使った暗号プログラムで、Hash計算に続いて第二弾です。今回はAES暗号のコードです(AESの基本については前回記事を参考)。
フォレンジック屋なのに、何故か暗号化のプログラムのネタ。まあ、Autopsyで色々試してて、上手くいってなくてネタがないだけなんですけどねw
あと、自作ツールを気分で使ってみようと思ったほか、CTRモードでちょっと検証コード書きたいとか・・・。まあ、ベースを作っておくと何かと便利なんです。
AES暗号化・復号化のCryptAPIのざっくりな流れ
大雑把なフローは以下の通り。
前提:
- "WinCrypt.h"をincludeすること。
概略フロー:
- Cryptコンテキストの取得(CryptAcquireContext)
- 鍵のインポート(CryptImportKey)
- 暗号化・復号化に必要なパラメータ設定(CryptSetKeyParam)
- 暗号化・復号化の実行(CryptEncrypt、CryptDecrypt)
- キーオブジェクトの開放(CryptDestroyKey)
- Cryptコンテキストの開放(CryptReleaseContext)
ポイント:
- 利用するCryptAPIの種類と順番、概ねのパラメータ。
- インポートされる鍵の管理、鍵交換方法は別途考慮しておく必要がある。
- 暗号化モード、初期化ベクトルをCryptSetKeyParamで設定する。
CryptAPIを使ったAES計算サンプルソース
これも、VisualStudio2017で動作することが確認されたソースです。
もし流用するのであれば、パラメータ等については、用途に合わせて変えてください。
MicrosoftのMSDNを参考にするとよいでしょう。
特に、今回は暗号化モードをCBCモードに固定したものにしています。利用したいモードが異なる場合は適宜修正してください。
また、例によってタブが上手く入らなくてスペースでインデントしているので、適宜直してください。
関数の仕様:
static int Encrypt(BYTE *binKey, int iKeySize, BYTE *binInData, int *pInDataSize, BYTE *binOutData, int *pOutSize);
第1引数で渡した鍵で第3引数のデータを暗号化し、第5引数の領域に格納し返す。
第5引数の領域にNULLを指定した場合、暗号化結果を受け取るサイズを返す。
引数:
binKey:暗号化に用いる鍵データのポインタ
iKeySize:ハッシュ化したいデータのサイズ
binInData:暗号化されるデータのポインタ
pInDataSize:暗号化されるデータのサイズ
binOutData:暗号化されたデータの受取りバッファ
pOutSize:暗号化されたデータの受取りサイズ
返り値:
0:正常終了
負値:異常終了(詳細はヘッダのdefine参照)
制限:
大きなサイズのデータには未対応。
大きなデータを暗号化する場合は、CryptEncrypt関数の第3引数(Final)をFalseにしたうえで、ループする処理にする必要がある。
static int Decrypt(BYTE *binKey, int iKeySize, BYTE *binInOutData, int *binInOutData);
第1引数で渡した鍵で第3引数のデータを暗号化し、第3引数の領域に格納し返す。
引数:
binKey:暗号化に用いる鍵データのポインタ
iKeySize:ハッシュ化したいデータのサイズ
binInOutData:復号化されるデータのポインタ。復号結果がそのまま格納される。
binInOutData:復号化されるデータのサイズ。復号されたサイズがそのまま格納される。
返り値:
0:正常終了
負値:異常終了(詳細はヘッダのdefine参照)
制限:
大きなサイズのデータには未対応。
大きなデータを暗号化する場合は、CryptDecrypt関数の第3引数(Final)をFalseにしたうえで、ループする処理にする必要がある。
CryptAESCBC.h
CryptAESCBC.cpp
単発で使えるようにということで、関数一つでコンテキスト取得、パラメータ設定、暗号化・復号化、開放までやっています。
鍵を変えずに暗号化・復号化を複数回する場合は hKey 、鍵を変えるものの暗号化・復号化を複数回する場合は hProv が使いまわせるので、実装の都合により適当に改造してください。インスタンス化するなら、コンストラクタで取得、デストラクタで開放っていう仕様にするのもありかなと。
今回はメンバ変数を持たないようにしたので、わざわざインスタンス化しなくてもいいように、 static 関数にしてそのまま呼べるようにしています。
CBCモードにしているため、パディングと初期化ベクトルが必要になります。
パディングは、指定しないと、暗号化・復号化時にエラーになります。
初期化ベクトルは、未指定の場合はオール0と同等になるようです。
概ねの処理の流れの参考になれば幸いです。
呼び出し側のメイン処理のサンプルは、そのうち追加するかもしれません。