暗号化zipを利用したストレージ管理

Androidアプリはapkをapktoolなどで展開できるのでasset, res内のデータは比較的簡単に覗き見ることができます。今回は配布データの隠蔽化の目的でパスワード付きzipを試してみました。
Android用のパスワード付きzipの解凍モジュールは下記のサイトのモジュールを使わさせてもらいました。

Androidでパスワード付きzipを生成する
Android用Zipユーティリティクラス


Assetからファイル読込はInputStreamでしか受け取れないので、一旦assetsから内部ストレージに暗号化zipをそのままコピーして、必要な時に都度Entryパス指定で解凍してメモリ展開するようにします。

Assetからzipファイルを読込み内部ストレージにコピー

File outDir = context.getDir("data", Context.MODE_PRIVATE);
File outFile = new File(dir, "data.zip");

AssetManager assetManager = context.getResources().getAssets();
BufferedInputStream bis = new BufferedInputStream(assetManager.open("data.zip"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(outFile));

byte[] buffer = new byte[1024];
int len = 0;
while ( (len = is.read(buffer, 0, buffer.length)) > 0) {
     bos.write(buffer, 0, len);
}
bos.flush();
bos.close();
bis.close();


Context#getDir("data",Context.MODE_PRIVATE) で /data/data/{package_name}/app_data が作成されます。
AssetManager#open(ファイル名)でInputStreamが取得できるので、FileOutputStreamでbyte書込を行いapp_data/data.zipにバイナリコピーします。

指定のEntryパスで読込

Bitmap bitmap = null;

ZipFile zipFile = new ZipFile(dataFile, "UTF-8");
zipFile.setPassword(password.getBytes("UTF-8"));
zipFile.setCheckCrc(true);
ZipEntry entry = zipFile.getEntry("data/binary/photos/frog.jpg");

BufferedInputStream bis = new BufferedInputStream(zipFile.getInputStream(entry));
bitmap = BitmapFactory.decodeStream(bis);
bis.close();
zipFile.close();

内部ストレージのdata.zipをZipFileでインスタンスを作成しパスワードとCRCチェックの有無を設定します。
ZipFile#getEntry() で 対象のZipEntryを取得し、ZipFile#getInputStream(entry) で InputStreamを取得します。

尚、ZipFile#setCheckCrc(true) は ファイルが破損している場合はZipFileのInputStreamのread処理でZipCrcExceptionが発生します。


サンプルソース

サンプルソースgithubにアップしました。

https://github.com/hmori/CryptZipTest

  1. 起動時にasset/data.zipを /data/data/jp.hmori.cryptziptest/app_data/data.zip にコピー
  2. data.zip内に含まれるEntryPathをListViewに表示
  3. ListViewのEntryPathの行クリックで対象のEntryをメモリ展開し表示

所感

軽微なデータ量であれば問題ないのですが、大きめの画像ファイル等になると展開にパフォーマンスが劣化します。サンプルソースでは1Entryの展開にかかる時間を計測していますので、この方法を利用する場合はこちらで確認してから利用した方がいいと思います。
大きめのデータを扱う場合は、素直にSQLCipherを利用するか自前でAES暗号化を行う方がよいかもしれません。