というわけで、今回はMacのコマンドラインツールを作り、CCCryptを使い平文を暗号文にしてみます。
使う暗号化アルゴリズムはアメリカ合衆国の新暗号規格AES。
なのでプロダクト名もAESにしましょう。
Macのコマンドラインツール用プロジェクトの作り方がわからん人は、ちと古いけど「ブートキャンプ(3)Xcodeを使ったC言語の学習」あたりを参考にしてください。
ちなみに、ブートキャンプ(1)で紹介している無料開発者登録は、もう必要ありません。
今は登録無しで、AppStoreからXcodeをインストールできるようになってる。
手順をビデオにしてみますた。
↓
人類の夜明けだ
今回のコマンドラインツールで暗号化する平文は
const char* text = "Hello cipher\n";
とします。
ここでは平文らしく英語の文字列にしたけど、暗号化自体はバイト列ならなんでもよくて、テキスト、画像、音楽データにも適用できます。
この平文を、キーを決めてAESアルゴリズムで暗号化するわけですな。
AESのキー長は128、192、256ビットの長さを選べるようになっていて、長い方がより強力な暗号となります。今回はMAXの256ビット=32バイトのキーを用意することにしました。
int8_t key[] = {0,1,2,3,4,5,6,7,0,1,2,3,4,5,6,7,0,1,2,3,4,5,6,7,0,1,2,3,4,5,6,7};
int8_tという型は1バイトを使った整数用の型で-128~127の値を記憶します。ぶっちゃけchar型と同じですが、キーは文字列にする必要はないので区別する意味でint8_tを指定しています。AESに指定するキーは必要なバイト長さえあれば画像データでもなんでもかまわない。
今回は0~7の値を4回繰り返して32バイト分にしたので、16進表記だと
0001020304050607000102030405060700010203040506070001020304050607
という256ビット長のキーを用意したことになる。
ここまで準備できたら、暗号文を受け取るためのバッファを用意してCCCryptの呼び出しです。
CCCryptを使うときは以下のヘッダーをインクルードするように。
#include <CommonCrypto/CommonCrypto.h>
でもって、作成された暗号文を受け取るために用意するバッファの大きさですが、AESの場合、暗号化は128ビット=16バイトのブロック単位でおこないます。

なので、暗号文用のバッファのバイト長は、平文側のバイト長ではなく、それより大きく、かつ16バイト境界で区切られた大きさ、またはそれ以上が必要となります。
というわけで、次の計算で、平文に対して、AES暗号化で必要となる暗号文受け取り用バッファが必要とするバイト数は計算できる。
必要となる暗号文のバイト数 = (平文のバイト数 + 15) / 16 * 16
ぶっちゃけ、それ以上なら問題ないです。なので今回は平文が短いので暗号文用バッファは100バイトと適当に確保しました。
size_t cipher[100];
これでCCCryptを呼び出します。
暗号化
size_t numBytesEncrypted; 暗号文のバイト数が設定される
CCCryptorStatus cryptStatus = CCCrypt(
kCCEncrypt, 暗号化
kCCAlgorithmAES, AES使用
kCCOptionPKCS7Padding
AESは128ビットごとのブロックで暗号化する。
そのため平文が、128ビット境界にぴったり収まらないなら
後ろに詰め物をして128ビット境界にする必要がある。
その詰め方としてPKCS7を指定した
| kCCOptionECBMode,
ECBは圧縮方法のオプション。
暗号強度は少し落ちるが初期化ベクタを必要としないので指定
key, 32, キーとキーの大きさをバイト単位で指定(32=256ビット)
NULL, ECBを指定したので不要。NULLを指定
text, strlen(text), 平文と、その大きさをバイト単位で指定
cipher, 100, 暗号文を入れてもらうバッファと
その大きさをバイト単位で指定
&numBytesEncrypted); 暗号文の大きさをバイト単位で受け取る
第1引数が、平文を暗号文にする指定です。CCCryptは暗号文を平文に戻すときにも使い
kCCEncrypt 平文→暗号文
kCCDecrypt 暗号文→平文
となっています。
第2引数が暗号化アルゴリズム、今回はAESを使うので
kCCAlgorithmAES
を指定。
第3引数が暗号化アルゴリズム別のオプション指定。
AESの場合、128ビット単位での暗号化になるので、平文が128ビット境界にぴったり収まらないなら後ろに詰め物をして128ビット境界にする必要がある。で、その詰め物の設定方法としてPKCS7を使えって指示が
kCCOptionPKCS7Padding
です。で、もう一つの
kCCOptionECBMode
は、AESの動作モードの指定で、128ビットのブロックごとに、前のブロックの情報を合成するかどうかの指定。
合成すると暗号化されたバイト列のばらつきがより上がるんだけど、その場合、初期化ベクタという、最初の128ビットブロックに適用する値(128ビット=16バイト分)が必要になるので、ここでは、それを使わないように合成しないモードを指定しています。これを指定しないとCCCryptではCBCモードという前のブロックの情報を合成するモードが適用されます。
第4、第5引数はキーの指定。第4がキーの先頭番地、第5引数がキーのバイト長。
key, 32
第6引数が初期化ベクトル。ECBモードを指定したんで必要ありません。なので
NULL
第7、第8引数は平文→暗号文指定なんで平文のバッファ情報をわたします。第7に平文の先頭番地、第8引数にそのバイト長を渡します。
text, strlen(text)
第9、第10引数が平文→暗号文指定なんで暗号文のバッファ情報をわたします。第9に暗号文の先頭番地、第10引数にそのバイト長を渡します。
このさい第10引数には、平文のバイト長以上で128ビット境界となるバイト長以上の値を渡さないとエラーになるんで注意。
cipher, 100
最後は、実際に暗号文化されたバイト長を受け取るための変数の番地をわたす。
&numBytesEncrypted
これでcipherに暗号文が入る。
できあがった暗号文をCCCryptで平文に戻すのは簡単だけど、それじゃあ、ちゃんと他のアプリで平文に戻せるかの確認にはならない。
なので、暗号文をファイルにしてみます。
そのため暗号文のcipherをfwriteでstderrに書き出すようにしてます。
fwrite(cipher, sizeof(int8_t), numBytesEncrypted, stderr);
これで、このAESをビルドして作成されたAESのディレクトリをターミナルでカレントにして
./AES 2>hello.enc
とすれば暗号文をhello.encというファイルにできる。fopenでちゃんとファイルに書き出してもいいけど手抜き。
↓ 実際のサンプルはここ
https://github.com/Takahiro-Kunii/AES
あとは、書き出したファイルを、そのままターミナル上でopensslを使って平文に戻してみる。
opensslは指定した暗号化アルゴリズムで、平文を暗号化したり、その逆をおこなったりできるコマンドラインツールです。
まあ、もっといろいろな機能があるわけですが、とりあえず今回はCCCryptで作った暗号文を平文に戻すのに使います。
openssl渡す情報としては、暗号文がhello.encというファイルに入っていること、暗号化アルゴリズムにAESを使ったこと、キー長は256ビットで、ECBモードで暗号化していること、そしてキーの値となります。
openssl enc -d -aes-256-ecb -in hello.enc -nosalt -K 0001020304050607000102030405060700010203040506070001020304050607
これでhello.encの内容が読み込まれ平文がターミナル画面に出力される。
実際の手順をYouTubeにアップしたんで見てね。
↓
次回はopensslで暗号化したファイルを平文に戻してみる。
使う暗号化アルゴリズムはアメリカ合衆国の新暗号規格AES。
なのでプロダクト名もAESにしましょう。
Macのコマンドラインツール用プロジェクトの作り方がわからん人は、ちと古いけど「ブートキャンプ(3)Xcodeを使ったC言語の学習」あたりを参考にしてください。
ちなみに、ブートキャンプ(1)で紹介している無料開発者登録は、もう必要ありません。
今は登録無しで、AppStoreからXcodeをインストールできるようになってる。
手順をビデオにしてみますた。
↓
人類の夜明けだ
今回のコマンドラインツールで暗号化する平文は
const char* text = "Hello cipher\n";
とします。
ここでは平文らしく英語の文字列にしたけど、暗号化自体はバイト列ならなんでもよくて、テキスト、画像、音楽データにも適用できます。
この平文を、キーを決めてAESアルゴリズムで暗号化するわけですな。
AESのキー長は128、192、256ビットの長さを選べるようになっていて、長い方がより強力な暗号となります。今回はMAXの256ビット=32バイトのキーを用意することにしました。
int8_t key[] = {0,1,2,3,4,5,6,7,0,1,2,3,4,5,6,7,0,1,2,3,4,5,6,7,0,1,2,3,4,5,6,7};
int8_tという型は1バイトを使った整数用の型で-128~127の値を記憶します。ぶっちゃけchar型と同じですが、キーは文字列にする必要はないので区別する意味でint8_tを指定しています。AESに指定するキーは必要なバイト長さえあれば画像データでもなんでもかまわない。
今回は0~7の値を4回繰り返して32バイト分にしたので、16進表記だと
0001020304050607000102030405060700010203040506070001020304050607
という256ビット長のキーを用意したことになる。
ここまで準備できたら、暗号文を受け取るためのバッファを用意してCCCryptの呼び出しです。
CCCryptを使うときは以下のヘッダーをインクルードするように。
#include <CommonCrypto/CommonCrypto.h>
でもって、作成された暗号文を受け取るために用意するバッファの大きさですが、AESの場合、暗号化は128ビット=16バイトのブロック単位でおこないます。

なので、暗号文用のバッファのバイト長は、平文側のバイト長ではなく、それより大きく、かつ16バイト境界で区切られた大きさ、またはそれ以上が必要となります。
というわけで、次の計算で、平文に対して、AES暗号化で必要となる暗号文受け取り用バッファが必要とするバイト数は計算できる。
必要となる暗号文のバイト数 = (平文のバイト数 + 15) / 16 * 16
ぶっちゃけ、それ以上なら問題ないです。なので今回は平文が短いので暗号文用バッファは100バイトと適当に確保しました。
size_t cipher[100];
これでCCCryptを呼び出します。
暗号化
size_t numBytesEncrypted; 暗号文のバイト数が設定される
CCCryptorStatus cryptStatus = CCCrypt(
kCCEncrypt, 暗号化
kCCAlgorithmAES, AES使用
kCCOptionPKCS7Padding
AESは128ビットごとのブロックで暗号化する。
そのため平文が、128ビット境界にぴったり収まらないなら
後ろに詰め物をして128ビット境界にする必要がある。
その詰め方としてPKCS7を指定した
| kCCOptionECBMode,
ECBは圧縮方法のオプション。
暗号強度は少し落ちるが初期化ベクタを必要としないので指定
key, 32, キーとキーの大きさをバイト単位で指定(32=256ビット)
NULL, ECBを指定したので不要。NULLを指定
text, strlen(text), 平文と、その大きさをバイト単位で指定
cipher, 100, 暗号文を入れてもらうバッファと
その大きさをバイト単位で指定
&numBytesEncrypted); 暗号文の大きさをバイト単位で受け取る
第1引数が、平文を暗号文にする指定です。CCCryptは暗号文を平文に戻すときにも使い
kCCEncrypt 平文→暗号文
kCCDecrypt 暗号文→平文
となっています。
第2引数が暗号化アルゴリズム、今回はAESを使うので
kCCAlgorithmAES
を指定。
第3引数が暗号化アルゴリズム別のオプション指定。
AESの場合、128ビット単位での暗号化になるので、平文が128ビット境界にぴったり収まらないなら後ろに詰め物をして128ビット境界にする必要がある。で、その詰め物の設定方法としてPKCS7を使えって指示が
kCCOptionPKCS7Padding
です。で、もう一つの
kCCOptionECBMode
は、AESの動作モードの指定で、128ビットのブロックごとに、前のブロックの情報を合成するかどうかの指定。
合成すると暗号化されたバイト列のばらつきがより上がるんだけど、その場合、初期化ベクタという、最初の128ビットブロックに適用する値(128ビット=16バイト分)が必要になるので、ここでは、それを使わないように合成しないモードを指定しています。これを指定しないとCCCryptではCBCモードという前のブロックの情報を合成するモードが適用されます。
第4、第5引数はキーの指定。第4がキーの先頭番地、第5引数がキーのバイト長。
key, 32
第6引数が初期化ベクトル。ECBモードを指定したんで必要ありません。なので
NULL
第7、第8引数は平文→暗号文指定なんで平文のバッファ情報をわたします。第7に平文の先頭番地、第8引数にそのバイト長を渡します。
text, strlen(text)
第9、第10引数が平文→暗号文指定なんで暗号文のバッファ情報をわたします。第9に暗号文の先頭番地、第10引数にそのバイト長を渡します。
このさい第10引数には、平文のバイト長以上で128ビット境界となるバイト長以上の値を渡さないとエラーになるんで注意。
cipher, 100
最後は、実際に暗号文化されたバイト長を受け取るための変数の番地をわたす。
&numBytesEncrypted
これでcipherに暗号文が入る。
できあがった暗号文をCCCryptで平文に戻すのは簡単だけど、それじゃあ、ちゃんと他のアプリで平文に戻せるかの確認にはならない。
なので、暗号文をファイルにしてみます。
そのため暗号文のcipherをfwriteでstderrに書き出すようにしてます。
fwrite(cipher, sizeof(int8_t), numBytesEncrypted, stderr);
これで、このAESをビルドして作成されたAESのディレクトリをターミナルでカレントにして
./AES 2>hello.enc
とすれば暗号文をhello.encというファイルにできる。fopenでちゃんとファイルに書き出してもいいけど手抜き。
↓ 実際のサンプルはここ
https://github.com/Takahiro-Kunii/AES
あとは、書き出したファイルを、そのままターミナル上でopensslを使って平文に戻してみる。
opensslは指定した暗号化アルゴリズムで、平文を暗号化したり、その逆をおこなったりできるコマンドラインツールです。
まあ、もっといろいろな機能があるわけですが、とりあえず今回はCCCryptで作った暗号文を平文に戻すのに使います。
openssl渡す情報としては、暗号文がhello.encというファイルに入っていること、暗号化アルゴリズムにAESを使ったこと、キー長は256ビットで、ECBモードで暗号化していること、そしてキーの値となります。
openssl enc -d -aes-256-ecb -in hello.enc -nosalt -K 0001020304050607000102030405060700010203040506070001020304050607
これでhello.encの内容が読み込まれ平文がターミナル画面に出力される。
実際の手順をYouTubeにアップしたんで見てね。
↓
次回はopensslで暗号化したファイルを平文に戻してみる。