Android のローカルファイルへの保存(/data/data/パッケージ/file/)への格納/読込み
ファイルの中は暗号化で、Mapのように、Key-Value で管理したいと考えた。。。
ポイントは、ContextWrapper を継承すること。
暗号化するデータを1文の文字列にすること(Key-Value)の文字列化で
JSON形式を採用したこと。
JSONを扱うので、Google GSON を使ったことである。
同期の問題はとりあえず無視して以下のように。。。
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.OutputStream;
import java.lang.reflect.Type;
import java.security.Key;
import java.security.spec.AlgorithmParameterSpec;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import android.content.Context;
import android.content.ContextWrapper;
/**
* ローカルファイル key-value 管理
*/
public final class PrivateStorage extends ContextWrapper{
private static PrivateStorage inst;
private Map<String,String> map;
private Criptor criptor; // inner class
private String filename = "my.dat";
GsonBuilder gonbuilder;
private PrivateStorage(Context base){
super(base);
gonbuilder = new GsonBuilder();
criptor = new Criptor();
map = new HashMap<String,String>();
this.reload();
}
/**
* インスタンス生成or取得
*/
public static synchronized PrivateStorage getInstance(Context context){
if (inst==null){
inst = new PrivateStorage(base);
}
return inst;
}
/**
* キーによる値取得
*/
public String get(String key){
return map.get(key);
}
/**
* key-value 格納
*/
public void put(String key,String value){
map.put(key,value);
}
/**
* ローカルファイルへの書き込み
*/
public void commit(){
try{
Gson gson = gonbuilder.create();
OutputStream out = openFileOutput(filename,MODE_PRIVATE);
out.write(criptor.encrypt(gson.toJson(map)));
out.close();
}catch(Exception e){
throw new RuntimeException(e);
}
}
/**
* ローカルファイル削除
*/
public void deleteAll(){
try{
deleteFile(filename);
map.clear();
}catch(Exception e){
throw new RuntimeException(e);
}
}
/**
* @return Key Set
*/
public Set<String> keySet(){
return map.keySet();
}
/**
* 再読み込み
*/
public void reload(){
map.clear();
try{
FileInputStream inputStream = openFileInput(filename);
int len = inputStream.available();
if (len > 0){
BufferedInputStream stream = new BufferedInputStream(inputStream);
byte b = new byte[len];
stream.read(b);
map.clear();
Gson gson = gonbuilder.create();
Type type = new TypeToken<Map<String,String>>(){}.getType();
map = gson.fromJson(criptor.decrypt(b),type);
}
inputStream.close();
}catch(Exception e){
}
}
class Criptor{
protected String key = "0123456789abcdef";
protected String initvecor = "abcdef0123456789";
private Key secretKey;
private AlgorithmParameterSpec ivParamSpec;
public Criptor(){
byte keybyte = key.getBytes();
this.secretKey = new SecretKeySpec(keybyte,"AES");
byte vector = initvecor.getBytes();
this.ivParamSpec = new IvParameterSpec(vector);
}
public final byte encrypt(String text){
try{
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE,this.secretKey,this.ivParamSpec);
byte iv = cipher.getIV();
byte enc = cipher.doFinal(text.getBytes());
byte bs = new byte[iv.length + enc.length];
System.arraycopy(iv,0,bs,0,iv.length);
System.arraycopy(enc,0,bs,iv.length,enc.length);
return bs;
}catch(Exception e){
throw new RuntimeException(e);
}
}
public final String decrypt(byte data){
try{
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE,this.secretKey,this.ivParamSpec);
int blocksize = cipher.getBlockSize();
return new String(cipher.doFinal(data,blocksize,data.length - blocksize));
}catch(Exception e){
throw new RuntimeException(e);
}
}
}
}
===============
改めて、Google GSON が凄いと思ったのは、
JSONの読込、fromJson のメソッドで
Type を指定できることで、総称型を含む Map<String,String> への変換を綺麗に書けることだ。
Type type = new TypeToken<Map<String,String>>(){}.getType();
map = gson.fromJson(criptor.decrypt(b),type);
の部分である。