Visual Studio 2013でCrypto++を使う

配布ゲームの画像や音楽データの暗号化をしたいと思うようになり、
暗号化ライブラリであるCrypto++を導入してみました。

■インストール手順

1.ダウンロード
以下サイトにアクセスします。

http://sourceforge.jp/projects/sfnet_cryptopp/

Windowsの cryptopp562.zipをクリックし、ライブラリ本体をダウンロードします。
ダウンロードしたZIPファイルは適当な場所に解凍します。

2.ビルド
解凍したフォルダ内の「cryptest.sln」をVisualStudio2013で開きます。
開いたらプロジェクトの変換画面が出てくるので2013用に変換します。
変換したらソリューション全体をビルドします。
ビルドに成功したらライブラリファイルがcryptopp562Win32Output以下に生成されます。

■プロジェクトの準備
ビルドしたCrypto++を別プロジェクトで使えるようにするための設定を行います。

1.プロジェクト作成
まず、新規プロジェクトを適当な場所に作成して下さい。

2.パス設定
プロジェクトを作成したら、ソリューションエクスプローラーの該当プロジェクトを右クリックし、「プロパティ」を選択します。

「構成プロパティ」→「VC++ ディレクトリ」を選択し、インクルードディレクトリ、ライブラリディレクトリに以下パスを追加します。

 インクルードディレクトリ:(適当なパス)cryptopp562
 ライブラリディレクトリ:(適当なパス)cryptopp562Win32OutputDebug

もしライブラリディレクトリをReleaseビルドした場合は最後のDebugをReleaseに置き換えて下さい。

3.ランライムライブラリの変更
このままではビルドしたときにリンクエラーが大量に出てきてしまいます。
「構成プロパティ」→「C/C++」→「コード生成」を選択し、「ランタイムライブラリ」を「マルチスレッド デバッグ (/MTd)」にします。

4.ライブラリのインポート
「構成プロパティ」→「リンカー」→「入力」を選択し、「追加の依存ファイル」に以下ライブラリを追加します。

 cryptlib.lib

これでCrypto++が使用できるようになりました。
早速暗号化プログラムを使用してみます。
今回はAESを使った暗号化プログラムを作成します。

■暗号化

1.共通鍵・IVの初期化
AESによる平文の暗号化・複合化を行うための共通鍵およびIVを初期化します。

// 鍵データ初期化
void InitKey(byte* key, size_t size) {
    for ( size_t i = 0; i < size; ++i ) {
        key[i] = rand();
    }
}
    // 共通鍵とIVを適当な値で初期化
    InitKey(key, sizeof(key));
    InitKey(iv, sizeof(iv));

ひとまずランダムな値で初期化します。

2.暗号化の前準備
AESで暗号化するためのオブジェクトを作成します。

    CryptoPP::CTR_Mode<cryptoPP::AES>::Encryption enc;
    enc.SetKeyWithIV(key, sizeof(key), iv);

Encryption::SetKeyWithIV()メソッドに1.で作成した共通鍵とIVを渡しています。
次に、暗号化を行うための変換フィルタを作成します。

    // 暗号化のための変換フィルタの作成
    std::string encText;
    CryptoPP::StreamTransformationFilter encFilter(enc, new CryptoPP::StringSink(encText));

StreamTransformationFilterオブジェクトが変換フィルタです。
このコンストラクタに先ほど作成した暗号化オブジェクトencと暗号化結果を格納するencTextをStringSinkオブジェクトにより渡しています。
なぜStringSinkでラッパーするのかは正直わかりません・・・
おまじないと思って差し支えないと思います。

3.暗号化
暗号化を行うには、まずStreamTransformationFilter::Put()メソッドを呼び出し、暗号化したい文字列を指定します。

    encFilter.Put(reinterpret_cast<const byte*>(plainText.c_str()), plainText.size());

暗号化したらおまじないでStreamTransformationFilter::MessageEnd()メソッドを呼び出します。

    encFilter.MessageEnd();

これで暗号化された文字列がencTextから取得できるようになります。

■復号化

1.暗号化の前準備
AESで暗号化した文字列を復号化するためのオブジェクトを作成します。
手順は暗号化のときとほぼ一緒です。
暗号化で使った共通鍵とIVを用意しておいてください。

    std::string encText;
    CryptoPP::StreamTransformationFilter encFilter(enc, new CryptoPP::StringSink(encText));

Decryption::SetKeyWithIV()メソッドに復号化で使用した共通鍵とIVを渡します。

復号化も暗号化同様、変換フィルタを作成します。

    // 復号化のための変換フィルタの作成
    std::string decText;
    CryptoPP::StreamTransformationFilter decFilter(dec, new CryptoPP::StringSink(decText));

StreamTransformationFilterは暗号化に用いた変換フィルタと同じ型です。
インスタンス化時にコンストラクタに復号化オブジェクトdecを渡します。

2.復号化
復号化のメソッド呼び出しは暗号化とまったく一緒です。

    decFilter.Put(reinterpret_cast<const byte*>(encText.c_str()), encText.size());
    decFilter.MessageEnd();

復号化用に作成した変換フィルタのPut()メソッド呼び出しで復号化実施、MessageEnd()メソッド呼び出しで復号化後の文字列が取得できます。

上記の説明をまとめたサンプルソースを最後に貼り付けて起きます。

#include <iostream>
#include <aes.h>
#include <dh.h>
#include <modes.h>
#include <osrng.h>


// 鍵データ初期化
void InitKey(byte* key, size_t size) {
    for ( size_t i = 0; i < size; ++i ) {
        key[i] = rand();
    }
}

// メイン関数
int main() {
    // 共通鍵・IV
    byte key[CryptoPP::AES::DEFAULT_KEYLENGTH];
    byte iv[CryptoPP::AES::BLOCKSIZE];

    // 共通鍵とIVを適当な値で初期化
    InitKey(key, sizeof(key));
    InitKey(iv, sizeof(iv));

    std::string plainText = "暗号化される前の平文";
    std::cout << "Plain Text : " << plainText << std::endl;

    // 暗号化オブジェクトの作成
    CryptoPP::CTR_Mode<cryptoPP::AES>::Encryption enc;
    enc.SetKeyWithIV(key, sizeof(key), iv);

    // 暗号化のための変換フィルタの作成
    std::string encText;
    CryptoPP::StreamTransformationFilter encFilter(enc, new CryptoPP::StringSink(encText));

    // 暗号化
    encFilter.Put(reinterpret_cast<const byte*>(plainText.c_str()), plainText.size());
    encFilter.MessageEnd();

    std::cout << "Encrypted Text : " << encText << std::endl;


    // 復号化オブジェクトの作成
    CryptoPP::CTR_Mode<cryptoPP::AES>::Decryption dec;
    dec.SetKeyWithIV(key, sizeof(key), iv);

    // 復号化のための変換フィルタの作成
    std::string decText;
    CryptoPP::StreamTransformationFilter decFilter(dec, new CryptoPP::StringSink(decText));
    decFilter.Put(reinterpret_cast<const byte*>(encText.c_str()), encText.size());
    decFilter.MessageEnd();

    std::cout << "Decrypted Text : " << decText << std::endl;


    return 0;
}

暗号化→復号化を行い、暗号化前と暗号化後、復号化後の文字列を表示させています。
上記サンプルを実行すると以下の結果が出力されます。

Plain Text : 暗号化される前の平文
Encrypted Text : Z燁・。・ォオ挟+メ嵜
Decrypted Text : 暗号化される前の平文

暗号化前と復号化後の文字列が一致していることが確認できます。

Crypto++はVS2013では比較的すんなりと導入できました。
今回例に挙げたAES以外にも、DESやRSAなど色々な暗号化方式をサポートしています。

手軽に使える暗号化ライブラリとして色々な場面で重宝すると思います。

■参考サイト
Crypto++ ― C++用の暗号化ライブラリ ― を使ってみた
crypto++でのお手軽暗号
Example of AES using Crypto++
TripleDES