読者です 読者をやめる 読者になる 読者になる

文字列を暗号化し、文字列として保存する

・暗号化キーは端末内で作成、Preferenceで保存する
・暗号化した文字列をPreferenceで保存する
・暗号化はAESを利用する
ということを実現してみました。
PreferenceUtilは、SharedPreferencesに保存するヘルパークラスです。

package hm.orz.chaos114.android.tumekyouen.util;

import java.security.GeneralSecurityException;
import java.security.Key;
import java.security.SecureRandom;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.spec.SecretKeySpec;

import android.content.Context;
import android.util.Base64;

public class EncryptionUtil {
	/** 鍵のbit数 */
	private static final int ENCRYPT_KEY_LENGTH = 128;

	/** 暗号化キー */
	private final Key key;

	/** コンテキスト */
	private final Context context;

	/**
	 * コンストラクタ。
	 * 暗号化キーを生成、もしくは復元します。
	 * 
	 * @param context コンテキスト
	 */
	public EncryptionUtil(Context context) {
		this.context = context.getApplicationContext();
		
		// Preferenceから暗号化キーを取得
		PreferenceUtil preferenceUtil = new PreferenceUtil(this.context);
		String keyStr = preferenceUtil.getString(PreferenceUtil.KEY_SECRET_KEY);

		if (keyStr == null) {
			// Preferenceから取得できなかった場合

			// 暗号化キーを生成
			key = generateKey();
			// 生成したキーを保存
			String base64Key = Base64.encodeToString(key.getEncoded(),
					Base64.URL_SAFE | Base64.NO_WRAP);
			preferenceUtil.putString(PreferenceUtil.KEY_SECRET_KEY, base64Key);
		} else {
			// Preferenceから取得できた場合
			
			// キーを復元
			byte[] keyBytes = Base64.decode(keyStr, Base64.URL_SAFE
					| Base64.NO_WRAP);
			key = new SecretKeySpec(keyBytes, "AES");
		}
	}

	/**
	 * 暗号化した文字列を返却する。
	 * 
	 * @param input 入力文字列
	 * @return 暗号化した文字列
	 */
	public String encrypt(String input) {
		if (input == null) {
			return null;
		}

		try {
			Cipher cipher = Cipher.getInstance("AES");
			cipher.init(Cipher.ENCRYPT_MODE, key);
			byte[] result = cipher.doFinal(input.getBytes());
			return Base64.encodeToString(result, Base64.URL_SAFE
					| Base64.NO_WRAP);
		} catch (GeneralSecurityException e) {
			throw new RuntimeException(e);
		}
	}

	/**
	 * 復号化した文字列を返却する。
	 * 
	 * @param input 入力文字列
	 * @return 復号化した文字列
	 */
	public String decrypt(String input) {
		if (input == null) {
			return null;
		}

		try {
			Cipher cipher = Cipher.getInstance("AES");
			cipher.init(Cipher.DECRYPT_MODE, key);
			byte[] result = cipher.doFinal(Base64.decode(input, Base64.URL_SAFE
					| Base64.NO_WRAP));
			return new String(result);
		} catch (GeneralSecurityException e) {
			throw new RuntimeException(e);
		}
	}

	/**
	 * 暗号化キーを生成する。
	 * 
	 * @return 暗号化キー
	 */
	private static Key generateKey() {
		try {
			KeyGenerator generator = KeyGenerator.getInstance("AES");
			SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
			generator.init(ENCRYPT_KEY_LENGTH, random);
			return generator.generateKey();
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}
}

※2015/08/06追記
Android Studioの警告から知りましたが、`Cipher.getInstance`の引数は「動作モード」と「パディング」も指定しないといけないようです。
AES暗号アルゴリズムを使用してデータを暗号化する « Tech Booster
ここを読むと、`AES/CBC/PKCS5Padding`ってのが良さそうです。
ただし、この場合"IV(Initialization Vector)"なるものを保存しないといけないようです。