crypto++でのお手軽暗号
お手軽暗号
@yutakikuchi_です。
今日はcppで暗号/復号するためのエントリーを書きます。PHPやPythonにはcrypt標準モジュールがありドキュメントも充実しているので簡単に暗号化できますが、cppではそれらがあまり整備されていない事もあってプログラム書く時にちょいと苦労します。尚、ここでお手軽暗号と言っているのは暗号化のブロック方式をECBで対応するためです。CBC方式でも良いのですがIVの管理も面倒だし、そもそもそこまで暗号化強度に拘らないケースを想定しています。cppでの暗号化を行う為のLibraryとしてcrypto++という様々な暗号化方式をサポートする物を利用します。
Crypto++ Library 5.6.2 - a Free C++ Class Library of Cryptographic Schemes
crypto++
install
以下のOneLineです。
$ sudo yum install cryptopp cryptopp-devel -y
DES × ECB
TripleDES - Crypto++ Wiki
以下のコードでDES × ECBの暗号化/復号化ができます。crypto++のwikiにTripleDES × CBCの内容が載っていたのでそれを真似て作ってみました。暗号化の対象テキストが「魔法少女まどか☆マギカ」、暗号化keyに「Soul Gem」を指定します。ECBブロック暗号なのでIVを必要としません。処理としては単純にマルチバイト文字列を暗号化 => バイナリを16進数変換 => 16進数をバイナリに変換 => マルチバイト文字列を復元です。下に実行結果も載せておきます。#include <iostream> #include "modes.h" #include "filters.h" #include "hex.h" #include "des.h" using namespace std; using namespace CryptoPP; int main(int argc, char* argv[]) { string plain = "魔法少女まどか☆マギカ"; string cipher, encoded, decoded, recovered; ECB_Mode<DES>::Encryption encctx; byte key[DES::DEFAULT_KEYLENGTH]; memcpy( key, "Soul Gem", DES::DEFAULT_KEYLENGTH ); encctx.SetKey( key, DES::DEFAULT_KEYLENGTH ); // ciper StringSource( plain, true, new StreamTransformationFilter( encctx, new StringSink( cipher ) //StreamTransformationFilter::PKCS_PADDING //StreamTransformationFilter::ZEROS_PADDING ) ); //cout << "cipher text: " << cipher << endl; // hex encode StringSource( cipher, true, new HexEncoder( new StringSink( encoded ) ) ); cout << "encode text: " << encoded << endl; // hex decode StringSource( encoded, true, new HexDecoder( new StringSink( decoded ) ) ); // cout << "decode text: " << decoded << endl; // ciper decode ECB_Mode<DES>::Decryption decctx; decctx.SetKey( key, DES::DEFAULT_KEYLENGTH ); StringSource( decoded, true, new StreamTransformationFilter( decctx, new StringSink( recovered ) //StreamTransformationFilter::PKCS_PADDING //StreamTransformationFilter::ZEROS_PADDING ) ); cout << "recovered text: " << recovered << endl; return 0; }$ g++ -I/usr/include/cryptopp -lcryptopp crypto.cpp $ ./a.out encode text: B374EB12891FFF5C538281C88A612D522F2CE61360BA5B6776EB2A32E77E5C66B42978BA11B1C5E3 recovered text: 魔法少女まどか☆マギカSupport Padding
Crypto++: StreamTransformationFilter Class Reference
enum BlockPaddingScheme { NO_PADDING, ZEROS_PADDING, PKCS_PADDING, ONE_AND_ZEROS_PADDING, DEFAULT_PADDING }
crypto++では暗号化ブロックに分割した時のPaddingとして上の内容をサポートしています。Paddingは特に異なる言語間で暗号化/復号化する時に問題になる事が多いです。例としてはPHPですが、PHPのmcrypt関数では自動的にZEROS_PADDING(NULL_PADDING)となってしまいます。もし異なる言語間で連携をする時に平文と暗号化keyは一致しているんだけど、暗号文が異なってしまったという場合はPaddingの指定ミスの可能性があると覚えておけば嵌ることは無いと思います。次はPHPでencryptした結果が上のcrypto++とは異なっている事を表す例です。encode textの部分ですね。
<?php function pkcs5_pad ($text, $blocksize) { $pad = $blocksize - (strlen($text) % $blocksize); return $text . str_repeat(chr($pad), $pad); } function pkcs5_unpad($text) { $pad = ord($text{strlen($text)-1}); if ($pad > strlen($text)) return false; if (strspn($text, chr($pad), strlen($text) - $pad) != $pad) return false; return substr($text, 0, -1 * $pad); } //$size = mcrypt_get_block_size(MCRYPT_DES, MCRYPT_MODE_ECB); $message = '魔法少女まどか☆マギカ'; //$message = pkcs5_pad( $message, $size ); $key = 'Soul Gem'; $encryptedMessage = mcrypt_encrypt(MCRYPT_DES, $key, $message, MCRYPT_MODE_ECB); $decryptedMessage = mcrypt_decrypt(MCRYPT_DES, $key, $encryptedMessage, MCRYPT_MODE_ECB); echo "encode text:" . strtoupper(bin2hex($encryptedMessage)) . "\n"; echo "recovered text:$decryptedMessage";// phpでmcryptを使う為の設定 $ wget http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm $ wget http://ftp.riken.jp/Linux/fedora/epel/RPM-GPG-KEY-EPEL-6 $ sudo rpm --import RPM-GPG-KEY-EPEL-6 $ sudo rpm -i epel-release-6-8.noarch.rpm $ sudo yum install php-mcrypt -y // 上で書いたプログラムを実行 $ php crypto.php encode text:B374EB12891FFF5C538281C88A612D522F2CE61360BA5B6776EB2A32E77E5C6656FAC9AFC18DA65F recovered text:魔法少女まどか☆マギカではcrypto++とPHPを連携する時にはどうすれば良いか?一般的には「crypto++でZERO_PADDINGを指定する」、「PHPでPKCS5Paddingを自前で実装する」のどちらかで対応すると思います。暗号化ブロックの観点から言うとPKCS5でやるのが一般的かもしれません。PKCS5Paddingは上のPHPのmcrypt_get_block_size、pkcs5_padをコメントインするだけです。pkcs5_pad関数は以下のURLから持ってきました。PHP: Mcrypt Functions - Manual
当然crypto++のコードも修正が必要で、StreamTransformationFilter::PKCS_PADDING,StreamTransformationFilter::ZEROS_PADDINGを内容に合せてコメントインする必要があります。下はZEROS_PADDINGを指定した実行例です。今度はencode textの部分がcrypto++とPHPで一致していますね!$ cp crypto.cpp crypto2.cpp $ vi crypto.cpp // StreamTransformationFilter::ZEROS_PADDINGをコメントイン $ diff -u crypto.cpp crypto2.cpp --- crypto.cpp 2013-11-15 03:00:46.778536984 +0900 +++ crypto2.cpp 2013-11-15 03:00:02.604531919 +0900 @@ -19,8 +19,9 @@ // ciper StringSource( plain, true, new StreamTransformationFilter( encctx, - new StringSink( cipher ), - StreamTransformationFilter::ZEROS_PADDING + new StringSink( cipher ) + //StreamTransformationFilter::PKCS_PADDING + //StreamTransformationFilter::ZEROS_PADDING ) ); cout << "cipher text: " << cipher << endl; @@ -46,8 +47,9 @@ decctx.SetKey( key, DES::DEFAULT_KEYLENGTH ); StringSource( decoded, true, new StreamTransformationFilter( decctx, - new StringSink( recovered ), - StreamTransformationFilter::ZEROS_PADDING + new StringSink( recovered ) + //StreamTransformationFilter::PKCS_PADDING + //StreamTransformationFilter::ZEROS_PADDING ) ); cout << "recovered text: " << recovered << endl; $ g++ -I/usr/include/cryptopp -lcryptopp crypto.cpp $ ./a.out encode text: B374EB12891FFF5C538281C88A612D522F2CE61360BA5B6776EB2A32E77E5C6656FAC9AFC18DA65F recovered text: 魔法少女まどか☆マギカ