OpenSSLを使ってC言語でAES暗号
前回(OpenSSLのBIGNUM関連の関数群に関するメモ)から引き続きOpenSSLです。暗号です。 C言語でOpenSSLを使ってAES暗号を扱ってみたので、テストコードを公開します。
EVPとかいうもので抽象化されていて、ちょっと煩雑な手続きが必要。 とはいえおかげで別の暗号化方式に切り替えるのは楽だから、まあ良し悪しだね。
暗号化
unsigned char* Encrypt(const char* key, const char* data, const size_t datalen, const unsigned char* iv, unsigned char* dest, const size_t destlen) { EVP_CIPHER_CTX en; int i, f_len=0; int c_len = destlen; memset(dest, 0x00, destlen); EVP_CIPHER_CTX_init(&en); EVP_EncryptInit_ex(&en, EVP_aes_128_cbc(), NULL, (unsigned char*)key, iv); EVP_EncryptUpdate(&en, dest, &c_len, (unsigned char *)data, datalen); //EVP_EncryptFinal_ex(&en, (unsigned char *)(dest + c_len), &f_len); printf("c_len: %d\n", c_len); printf("f_len: %d\n", f_len); PrintBytes(dest, destlen); EVP_CIPHER_CTX_cleanup(&en); return dest; }
鍵と暗号化したいデータ、初期ベクトルを渡すと、destに代入してくれる。
コメントアウトしているEVP_EncryptFinal_ex
はデータ長が16Byteの倍数でないときにだけ必要になるようで、今回は要らないので省いてあります。
中途半端な長さのデータを渡した場合、Finalを呼んだ時にパディングしてうまいことやってくれるらしい。
初期ベクトルを使用しないとき(=暗号利用モードをEBCにするとき)は
EVP_EncryptInit_ex(&en, EVP_aes_128_ecb(), NULL, (unsigned char*)key, NULL);
のようにすればおっけー。
復号
unsigned char* Decrypt(const char* key, const unsigned char* data, const size_t datalen, const unsigned char* iv, char* dest, const size_t destlen) { EVP_CIPHER_CTX de; int f_len = 0; int p_len = datalen; memset(dest, 0x00, destlen); EVP_CIPHER_CTX_init(&de); EVP_DecryptInit_ex(&de, EVP_aes_128_cbc(), NULL, (unsigned char*)key, iv); EVP_DecryptUpdate(&de, (unsigned char *)dest, &p_len, data, datalen); //EVP_DecryptFinal_ex(&de, (unsigned char *)(dest + p_len), &f_len); EVP_CIPHER_CTX_cleanup(&de); printf("p_len: %d\n", p_len); printf("f_len: %d\n", f_len); printf("%s\n", dest); PrintBytes(dest, destlen); return dest; }
鍵とデータと初期ベクトルを渡して、destに代入。そのまんま。
EVP_DecryptFinal_ex
については暗号化の時と同じ。パディングが必要なときに呼んでください。
初期ベクトルを使用しないのもほぼ暗号化の時と同じで、
EVP_DecryptInit_ex(&de, EVP_aes_128_ecb(), NULL, (unsigned char*)key, NULL);
という感じ。
動かしてみる
#include <string.h> #include <stdio.h> #include <openssl/evp.h> #include <openssl/aes.h>
あたりをインクルード。
void PrintBytes(const unsigned char* bytes, const size_t length) { int i; for(i=0; i<length; i++) { printf("%02x", bytes[i]); } printf("\n"); }
これはデバッグ用の関数。Encrypt、Decryptの中で使っています。
int main() { const char key[] = "abcdefghijklmnop"; // 暗号化に使う鍵。16バイト。 const char data[] = "hello, OpenSSL! 123456789012345\0"; // 暗号化するデータ。ここでは32バイト。 const unsigned char iv = "abcdefghijklmnop"; // 初期ベクトル。16バイト。 unsigned char encode[32] = {'\0'}; // 暗号化したデータを入れる場所。 char decode[32] = {'\0'}; // 複合したデータを入れる場所。 printf("%s\n", data); PrintBytes(data, sizeof(data)-1); Encrypt(key, data, sizeof(data), iv encode, sizeof(encode)); Decrypt(key, encode, sizeof(encode), iv, decode, sizeof(decode)); return 0; }
これがメイン関数。エンコードしてデコードするだけ。
楽ちんといえば楽ちんだし、そうでないといえばそうでない、かなぁ。 さくっとラッパ書いて使うほうが良いのかなぁ、という気がしないでもないです。どうだろう。
参考: C言語でAESの暗号化・復号化を、opensslとmcryptで作ってみた:プログラマー社長のブログ:ITmedia オルタナティブ・ブログ