Cocos2d-xの3.xでOpenSSLの暗号化とハッシュを利用する

Pocket

cocos_encrypt

掲題の通りです。
以前はCCCryptoというcocos2d-xのライブラリがあったんですが、随分と更新も止まっちゃってるんで他の方法を調べてたところ、OpenSSLのライブラリを入れることでCocos2d-xでもAESやSHA256などが利用できるようになるので、その導入方法を備忘としてまとめておきます。

※ただ、ぼくの環境だけかもしれませんがなぜかAES256はAndroidではうまくできませんでした(´・ω・`)「使えない」わけがないと思うんですが、うまく復号化されないんですよね…。知ってる人いたら教えてほしいです。

OpenSSLの導入(iOS)

Androidもなんですが、まずはOpenSSLをダウンロードしてきてそれぞれの環境用にビルドする必要があります。
とはいっても、簡単にビルドできるシェルスクリプトを用意してくれている方がいるんで、ビルドと導入までは比較的カンタンです。

iOSの場合は以下を利用します。

OpenSSL-for-iPhone

どこでもいいんでcloneまたはZipをダウンロードしましょう。

$ git clone https://github.com/x2on/OpenSSL-for-iPhone.git

あとは、中にある「build-libssl.sh」を叩けばおしまいです。
一応シェルスクリプト内の24行目あたりにOpenSSLのバージョンを指定する箇所があるんで、指定します。
※ぼくは1.0.2hそのままで行いました。

//この部分を任意のバージョンに変更
VERSION="1.0.2h"

ターミナルを呼び出してシェルスクリプトを実行すると、あとは自動で落としてビルドまでしてくれます。便利!

openssl.frameworkを作成

さらに、上記のツールの「create-openssl-framework.sh」を叩けばopenssl.frameworkまで作成できます。
それも作っちゃいましょう。

フレームワークをプロジェクトに導入

そこまでできたら、あとは簡単です。
適当な場所に「openssl.framework」を置いて、xcodeプロジェクトに導入します。

openssl1

あと、「build settings」の「Framework Search Paths」に、そのフレームワークを置いたフォルダを指定します。

openssl2

あとはビルドすれば、普段からXcodeと良いお付き合いをしているかXcodeが激おこじゃない限りはきっと使えるようになるようなならないようなです。

OpenSSLの導入(Android)

続いてはAndroidです。こちらも、素晴らしいビルドツールを作ってくれている方がいるのでそれを使います。

hydrogenium
(説明ページ:Cocos2d-xでOpenSSLを使う(Android編))

同様にgit cloneするかダウンロードし、「build-tools/OpenSSL」フォルダ内に移動して「build-libssl-for-android.sh」を叩けばビルドしてもらえます。

この中にも、OpenSSLのバージョンを指定するのがあるので、設定します。
iOSと同じのがいいですね。
あと、Androidのプラットフォームバージョンを指定する箇所もあるので、そこも指定します。
なんとなくノリで12にしました。

// 15行目
# OpenSSL Version
OPENSSL_REPOSITORY_TAG="OpenSSL_1_0_2h"

// 34行目
# Android Platform Version
ANDROID_PLATFORM="android-12"

あとは叩けば、同じフォルダに「dest」というフォルダが出来上がるので、そこの中のを任意の場所に置いておきます。
最後に、そのフォルダ内のAndroid.mkを参照するようにプロジェクト内のAndroid.mkファイル内を編集すればビルド時に導入されます。

というか、詳しくはこれを作った方のページを見るほうがいいと思います(`・ω・´)

md5とsha256でハッシュを作成する

というわけで、早速使ってみます。
導入が成功していれば、#includeすることでハッシュも暗号化も利用できるようになります。

たとえば、sha256のハッシュ作成はこんな感じでサクっとできちゃいます。

#include <openssl/sha.h>

 const std::string str = "これはハッシュテストです";

 unsigned char md[SHA256_DIGEST_LENGTH];
 SHA256((unsigned char*)(str.c_str()), str.length(), md);
 std::string result = "";
 for (int i = 0; i < SHA256_DIGEST_LENGTH; i++) {
     result += StringUtils::format("%02x", md[i]);
 }
 CCLOG("%s", result.c_str());

md5もさくっと…

#include <openssl/md5.h>

 const std::string str = "これはハッシュテストです";

 unsigned char md5[MD5_DIGEST_LENGTH];
 MD5((unsigned char*)(str.c_str()), str.length(), md5);
 std::string result = "";
 for (int i = 0; i < MD5_DIGEST_LENGTH; i++) {
     result += StringUtils::format("%02x", md5[i]); 
 }
 CCLOG("%s", result.c_str());

…なんか、めんどいね(´・ω・`)

きっとぼくがC言語に慣れてないからですかね。
正直、ハッシュを生成するだけならhashlib++を使う方がずっと楽だと思いますね。
ただOpenSSLってC言語なだけあってかなり高速らしいし、ユーティリティクラスやラッパークラスを作ればいいだけの話ですが。

暗号化と復号化を行う

まあ、ただ、今回のメインはこの暗号化と復号化なのです。
ハッシュなんてついでですよね。

(あんまりOpenSSLのリファレンス読んでないけど)とりあえずこんな風にすることで暗号化と復号化を行うことができます。

#include <openssl/evp.h>

unsigned char password[] = "PASSWORD";
std::string str = "暗号化復号化テスト";

// std::stringをunsigned char*に変換
unsigned char* message = (unsigned char*)malloc(str.length() + 1);
message[str.length()] = '\0';
memcpy(message, str.c_str(), str.length());

// 暗号化を行う
// encrypt内に暗号化したバイトデータが入る
int len = 0;
std::vector<unsigned char> encrypt(str.length() + EVP_MAX_BLOCK_LENGTH);
EVP_CIPHER_CTX context;
EVP_EncryptInit(&context, EVP_aes_128_ecb(), password, nullptr);
EVP_EncryptUpdate(&context, encrypt.data(), &len, message,
                  static_cast<int>(str.length() + EVP_MAX_BLOCK_LENGTH));
EVP_EncryptFinal(&context, encrypt.data() + len, &len);
EVP_CIPHER_CTX_cleanup(&context);

// 復号化を行う
// decrypt内に復号化した文字列が入る
int _length = 0;
std::vector<unsigned char> decrypt(encrypt.size() + EVP_MAX_BLOCK_LENGTH);
EVP_DecryptInit(&context, EVP_aes_128_ecb(), password, nullptr);
EVP_DecryptUpdate(&context, decrypt.data(), &_length, encrypt.data(),
                  static_cast<int>(encrypt.size() + EVP_MAX_BLOCK_LENGTH));
EVP_DecryptFinal(&context, decrypt.data() + len, &len);
_length += len;
EVP_CIPHER_CTX_cleanup(&context);

// 復号化したデータを文字列に変換
auto _str = __String::createWithData(decrypt.data(), _length);
CCLOG("%s", _str->getCString());

// メモリ解放
free(message);
EVP_cleanup();

…やっぱ、めんどいね(´・ω・`)

ただ、冒頭でも書いたけどなぜかaes128ではちゃんと暗号化と復号化がされるんだけど、aes256ではAndroidではちゃんと復号化できませんでした(´・ω・`)
ぼくの書き方がいけないんでしょうけど、なんでだろ。

まあ、ハッシュも暗号化もしばらく使う予定はないんで、深くは調べてないです(´・ω・`)
でも気になるんで、知ってる方がいたら教えて貰えるとうれしいです。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です