重要なデータを保存するときは暗号化しよう
Androidで重要なデータをSharedPreferenceや内部ストレージに保存する際、セキュリティを考慮して暗号化した値を保存することが好ましいです。
「Android AES」とかでググれば色々な例は出てきますが、秘密鍵をソース内にベタに書いてたりする微妙なコードも多いので注意です。
で、調べるうちに、Facebookが公開しているConcealという暗号化ライブラリに行き着きました。
ソースを全部読んでるわけではないですが、
- APIがシンプル
- Android 4.3以前の端末で標準の暗号化ライブラリより高速
- 暗号化アルゴリズムはAES-GCM
- 実装はOpenSSLのもので、JNI経由でネイティブコードで実行する
- 暗号化に使う秘密鍵は初回に生成し、SharedPreferencesに保存している
- Androidの既知の乱数生成関連の不具合にも対応済み
な感じでした。
Concealの使い方
build.gradleに一行足す
compile 'com.facebook.conceal:conceal:1.0.3@aar'
- 2016/02/17 ライブラリのバージョンを1.0.3に更新
- 古いバージョンは脆弱性のあるOpenSSLを使っているため、必ず最新版を使ってください
- 古いバージョンは64bit対応していないため、必ず最新版を使ってください
サンプルコード
大きいデータやファイルに対しては、getCipherOutputStreamというストリームベースのAPIもありますが、ここでは小さい文字列なのでencrypt/decryptを使用。
private void testConceal() {
String plainText = "この文字列を暗号化したいのだ";
// encrypt/decryptのときに同じ文字列を渡す必要がある
String name = "hoge";
KeyChain keyChain = new SharedPrefsBackedKeyChain(context, CryptoConfig.KEY_256);
Crypto crypto = AndroidConceal.get().createDefaultCrypto(keyChain);
if (!crypto.isAvailable()) {
return;
}
try {
// UTF8でbyte[]に変換して、暗号化する
byte[] cipherText = crypto.encrypt(plainText.getBytes("utf-8"), new Entity(name));
// 暗号化されたbyte[]を復号化して、UTF8で文字列に戻す
byte[] decrypted = crypto.decrypt(cipherText, new Entity(name));
String data = new String(decrypted, "utf-8");
Log.v("test", "decrypted data is " + data);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (CryptoInitializationException e) {
e.printStackTrace();
} catch (KeyChainException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}