先日書いたAndroid ローカルファイルへMapデータ保存は、Google GSONで JSON 形式データだった。
暗号化&バイナリでの保存の方がより解読しにくいであろうから、
JSON ではなく Mapのシリアライズから、暗号化してバイナリで保存しようと考えた。
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
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 android.content.Context;
import android.content.ContextWrapper;
/**
* PrivateStorage. Map データ→暗号化ローカルファイル管理
* ファイルの更新 commit() を実行すること
*/
public final class PrivateStorage extends ContextWrapper{
private static PrivateStorage inst;
private Map<String,String> map;
private Criptor criptor;
private String filename = "app.dat";
public PrivateStorage(Context base){
super(base);
criptor = new Criptor();
map = new HashMap<String,String>();
load();
}
/**
* インスタンス取得
* @param base Context
* @return PrivateStorage
*/
public static synchronized PrivateStorage getInstance(Context base){
if (inst==null){
inst = new PrivateStorage(base);
}
return inst;
}
/**
* キーによる値取得
* @param key String
* @return value
*/
public String get(String key){
return map.get(key);
}
/**
* key-value 格納
* @param key String
* @param value String
*/
public void put(String key,String value){
map.put(key,value);
}
/**
* ローカルファイル削除
*/
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 commit(){
try{
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream os = new ObjectOutputStream(bos);
os.writeObject(map);
os.flush();
os.close();
bos.close();
OutputStream out = openFileOutput(filename,MODE_PRIVATE);
out.write(criptor.encrypt(bos.toByteArray()));
out.close();
}catch(Exception e){
throw new RuntimeException(e);
}
}
/**
* ローカルファイル読込
*/
@SuppressWarnings("unchecked")
public void load(){
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();
ObjectInputStream is = new ObjectInputStream(new ByteArrayInputStream(criptor.decrypt(b)));
map = (HashMap<String,String>)is.readObject();
is.available();
stream.close();
}
inputStream.close();
}catch(Exception e){
}
}
//------------------------------------------------------
class Criptor{
/** 128-bit;16byte の鍵 */
protected String key = "abcdefg123456789";
/** 初期化ベクトル 16byte */
protected String initvecor = "0123456789ABCDEF";
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(byte data){
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(data);
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 byte 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 cipher.doFinal(data,blocksize,data.length - blocksize);
}catch(Exception e){
throw new RuntimeException(e);
}
}
}
}