opensslにAES暗号化ライブラリがあるのを知ったのでこんな形で利用できるようにラップしてみました。


キーとIVの設定


aes::aes_cbc cbc_key(key, iv);

エンコードするとき


// AES-CBC encode
// サイズが0以下の場合throw bad_aes_cast
aes::byte_string enc_buf =
aes::aes_encode_cast(cbc_key, data);

デコードする時


// AES-CBC decode
// デコードしたいデータのサイズが16の倍数じゃない場合throw bad_aes_cast
aes::byte_string dec_buf =
aes::aes_decode_cast(cbc_key, enc_buf);

以下ソース全体


#include
#include // std::runtime_error
#include // std::find
#include // ::memcpy
#include
#include

#ifdef _MSC_VER
#pragma comment(lib, "libeay32.lib")
#pragma comment(lib, "ssleay32.lib")
#endif

namespace aes {

typedef unsigned char byte;
typedef std::basic_string byte_string;

class bad_aes_cast : public std::runtime_error {
public:
bad_aes_cast(const std::string& message)
: std::runtime_error(message) {}
};

class aes_cbc
{
byte_string key_;
byte_string iv_;
byte padding_;

template
friend byte_string aes_encode_cast(const T& , const byte* , size_t );
template
friend byte_string aes_decode_cast(const T& , const byte* , size_t );
public:
aes_cbc(const byte_string& key, const byte_string& iv, byte pad = 0)
: key_(key)
, iv_(iv)
, padding_(pad) {} // パディングに使用する値デフォルトは0
};

// AESエンコードキャスト T は将来的にはcfbとかをサポートするのに使いたい拡張用
// 現状cbc固定だが T の型で別な関数を呼び分けるようにしたい
template
byte_string aes_encode_cast(const T& aes_type, const byte* data, size_t data_size)
{
if (data_size <= 0)
{
bad_aes_cast e("data size is too small");
throw e;
}
// IVはAES_cbc_encrypt関数で変化するのでコピーを取る
std::vector ivec(aes_type.iv_.begin(), aes_type.iv_.end());
// エンコード後のサイズを計算
size_t enc_size = ((data_size - 1) / AES_BLOCK_SIZE + 1) * AES_BLOCK_SIZE;
// エンコードするデータをパディングに使用する値で初期化しておく
std::vector tmp_v(enc_size, aes_type.padding_);
// エンコードするデータをコピー
::memcpy(&tmp_v[0], data, data_size);
// エンコード結果を受け取る領域を確保
std::vector out_v(enc_size);

AES_KEY enc_key;
AES_set_encrypt_key(aes_type.key_.c_str(), static_cast(aes_type.key_.size()) * 8, &enc_key);
AES_cbc_encrypt(&tmp_v[0], &out_v[0], static_cast(enc_size), &enc_key, &ivec[0],
AES_ENCRYPT);
return byte_string(out_v.begin(), out_v.end());
}

// AESエンコードキャスト T は将来的にはcfbとかをサポートするのに使いたい拡張用
template
inline byte_string aes_encode_cast(const T& aes_type, const byte_string& data)
{
return aes_encode_cast(aes_type, data.c_str(), data.size());
}

// AESデコードキャスト T は将来的にはcfbとかをサポートするのに使いたい拡張用
// 現状cbc固定だが T の型で別な関数を呼び分けるようにしたい
template
byte_string aes_decode_cast(const T& aes_type, const byte* data, size_t data_size)
{
if (data_size % AES_BLOCK_SIZE)
{
bad_aes_cast e("data size must be multiple of 16");
throw e;
}
// IVはAES_cbc_encrypt関数で変化するのでコピーを取る
std::vector ivec(aes_type.iv_.begin(), aes_type.iv_.end());
// デコードされた結果を受け取る領域を確保
std::vector tmp_v(data_size);
AES_KEY dec_key;
AES_set_decrypt_key(aes_type.key_.c_str(), static_cast(aes_type.key_.size()) * 8, &dec_key);
AES_cbc_encrypt(data, &tmp_v[0], static_cast(tmp_v.size()), &dec_key, &ivec[0],
AES_DECRYPT);
// パディングに使用した値をデコードされた領域から検索
std::vector::iterator first = tmp_v.begin();
std::vector::iterator last = tmp_v.end();
std::vector::iterator it = std::find(first, last, aes_type.padding_);
if (it != last)
{
// パディングに使用した値を見つけたのでそこまでの値を返す
return byte_string(first, it);
}
// パディングに使用した値が無いのでデコードした値すべてを返す
return byte_string(first, last);
}

// AESデコードキャスト T は将来的にはcfbとかをサポートするのに使いたい拡張用
template
inline byte_string aes_decode_cast(const T& aes_type, const byte_string& data)
{
return aes_decode_cast(aes_type, data.c_str(), data.size());
}

// だだの強引なキャスト(^^;byte_string <=> string
template
inline std::basic_string byte_cast(const std::basic_string& data)
{
return std::basic_string(reinterpret_cast(const_cast(data.c_str())));
}

} // namespace aes

// お試し

#include
#include
#include

int main(int, char**)
{
using namespace std;

static const aes::byte KEY[] = { 0x06, 0xa9, 0x21, 0x40, 0x36, 0xb8, 0xa1, 0x5b, 0x51, 0x2e, 0x03, 0xd5, 0x34, 0x12, 0x00, 0x06 };
static const aes::byte IV[] = { 0x3d, 0xaf, 0xba, 0x42, 0x9d, 0x9e, 0xb4, 0x30, 0xb4, 0x22, 0xda, 0x80, 0x2c, 0x9f, 0xac, 0x41 };
static const aes::byte ENC[] = { 0xe3, 0x53, 0x77, 0x9c, 0x10, 0x79, 0xae, 0xb8, 0x27, 0x08, 0x94, 0x2d, 0xbe, 0x77, 0x18, 0x1a };

// 128bit(16byte)aesキー作成
aes::byte_string key(KEY, KEY+16);
// 128bit(16byte)Initialization Vector作成16オクテット固定
aes::byte_string iv(IV, IV+16);
// AES-CBCキーとIV
aes::aes_cbc cbc_key(key, iv);

string input("Single block msg");
// string => aes::byte_string
aes::byte_string data = aes::byte_cast(input);

try {
// AES-CBC encode
// サイズが0以下の場合throw bad_aes_cast
aes::byte_string enc_buf =
aes::aes_encode_cast(cbc_key, data); // throw bad_aes_cast

// 内容を確認
for (size_t i = 0 ; i < enc_buf.size() ; ++i) {
assert(ENC[i] == enc_buf[i]);
}

// AES-CBC decode
// デコードしたいデータのサイズが16の倍数じゃない場合throw bad_aes_cast
aes::byte_string dec_buf =
aes::aes_decode_cast(cbc_key, enc_buf);

// aes::byte_string => string
string buf = aes::byte_cast(dec_buf);
// 内容を確認
assert(input == buf);
} catch(aes::bad_aes_cast& e) {
cout << e.what() << "\npress any key for exit" << endl;;
cin.get();
return 0;
}
cout << "succese\n"
<< "press any key for exit" << endl;
cin.get();
return 0;
}

うん、とりあえず動いた。
KEYとIVはRFC3602の4. テストベクトルを試したら一致してたのでOK
鍵 :0x06 0xa9 0x21 0x40 0x36 0xb8 0xa1 0x5b 0x51 0x2e 0x03 0xd5 0x34 0x12 0x00 0x06
IV :0x3d 0xaf 0xba 0x42 0x9d 0x9e 0xb4 0x30 0xb4 0x22 0xda 0x80 0x2c 0x9f 0xac 0x41
平文 :"Single block msg"
暗号文 :0xe3 0x53 0x77 0x9c 0x10 0x79 0xae 0xb8 0x27 0x08 0x94 0x2d 0xbe 0x77 0x18 0x1a