Java 可序列化对象的ArrayList的加密保存和解密加载
我用这两种方法在sd卡中保存并加载一个包含可序列化对象的Java 可序列化对象的ArrayList的加密保存和解密加载,java,android,encryption,file-io,Java,Android,Encryption,File Io,我用这两种方法在sd卡中保存并加载一个包含可序列化对象的ArrayList的文件 保存方法 public static void saveUserList(ArrayList<User> userList) { if (storageAvailable()) { try { createFolder(); FileOutputStream userList = new FileO
ArrayList
的文件
保存方法
public static void saveUserList(ArrayList<User> userList) {
if (storageAvailable()) {
try {
createFolder();
FileOutputStream userList = new FileOutputStream(
baseDir + File.separator + baseAppDir + File.separator
+ fileName);
ObjectOutputStream oos = new ObjectOutputStream(
userList);
oos.writeObject(userList);
oos.close();
} catch (Exception exc) {
exc.printStackTrace();
}
}
}
导致此错误的原因
this error java.lang.UnsupportedOperationException 02-12 21:29:05.026 2051-2051/com.myapp W/System.err﹕ at com.facebook.crypto.streams.NativeGCMCipherOutputStream.write
我建议大家看看facebook最近发布的《隐藏》: 对于使用当前代码中使用的ObjectOutputStream包装隐藏输出流,这应该是一个微不足道的修改:
public static void saveUserList(ArrayList<User> userList) {
if (storageAvailable()) {
try {
createFolder();
Crypto crypto = new Crypto(
new SharedPrefsBackedKeyChain(context),
new SystemNativeCryptoLibrary());
FileOutputStream userList = new FileOutputStream(
baseDir + File.separator + baseAppDir + File.separator
+ fileName);
OutputStream cryptedStream = crypto.getCipherOutputStream(
userList, new Entity("UserList");
ObjectOutputStream oos = new ObjectOutputStream(
cryptedStream);
oos.writeObject(userList);
oos.close();
} catch (Exception exc) {
exc.printStackTrace();
}
}
}
publicstaticvoidsaveuserlist(arraylistuserlist){
if(storageAvailable()){
试一试{
createFolder();
加密=新加密(
新SharedPrefsBackedKeyChain(上下文),
新SystemNativeCryptoLibrary());
FileOutputStream用户列表=新FileOutputStream(
baseDir+File.separator+baseAppDir+File.separator
+文件名);
OutputStream cryptedStream=crypto.getCipherOutputStream(
用户列表,新实体(“用户列表”);
ObjectOutputStream oos=新的ObjectOutputStream(
加密流);
oos.writeObject(用户列表);
oos.close();
}捕获(异常exc){
exc.printStackTrace();
}
}
}
我将把恢复留给读者作为练习。;尝试(添加我省略的适当检查和尝试块以使代码更可读)这样的内容来保存
public static void AESObjectEncoder(Serializable object, String password, String path) {
try {
Cipher cipher = null;
cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
cipher.init(Cipher.ENCRYPT_MODE, fromStringToAESkey(password));
SealedObject sealedObject = null;
sealedObject = new SealedObject(object, cipher);
CipherOutputStream cipherOutputStream = null;
cipherOutputStream = new CipherOutputStream(new BufferedOutputStream(new FileOutputStream(path)), cipher);
ObjectOutputStream outputStream = null;
outputStream = new ObjectOutputStream(cipherOutputStream);
outputStream.writeObject(sealedObject);
outputStream.close();
}
这个要装吗
public static Serializable AESObjectDedcoder(String password, String path) {
Cipher cipher = null;
Serializable userList = null;
cipher = Cipher.getInstance("AES/CBC/PKCS7Pdding");
//Code to write your object to file
cipher.init(Cipher.DECRYPT_MODE, fromStringToAESkey(password));
CipherInputStream cipherInputStream = null;
cipherInputStream = new CipherInputStream(new BufferedInputStream(new FileInputStream(path)), cipher);
ObjectInputStream inputStream = null;
inputStream = new ObjectInputStream(cipherInputStream);
SealedObject sealedObject = null;
sealedObject = (SealedObject) inputStream.readObject();
userList = (Serializable) sealedObject.getObject(ciper);
return userList;
}
要从字符串创建一个SecretKey
,可以使用以下命令
public static SecretKey fromStringToAESkey(String s) {
//256bit key need 32 byte
byte[] rawKey = new byte[32];
// if you don't specify the encoding you might get weird results
byte[] keyBytes = new byte[0];
try {
keyBytes = s.getBytes("ASCII");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
System.arraycopy(keyBytes, 0, rawKey, 0, keyBytes.length);
SecretKey key = new SecretKeySpec(rawKey, "AES");
return key;
}
注:
此代码加密和解密两次以显示密封对象和密码流的使用方式您可以简单地使用AES编码:
private static byte[] getEncrypt(final String key, final String message) throws GeneralSecurityException {
final byte[] rawData = key.getBytes(Charset.forName("US-ASCII"));
if (rawData.length != 16) {
// If this is not 16 in length, there's a problem with the key size, nothing to do here
throw new IllegalArgumentException("You've provided an invalid key size");
}
final SecretKeySpec seckeySpec = new SecretKeySpec(rawData, "AES");
final Cipher ciph = Cipher.getInstance("AES/CBC/PKCS5Padding");
ciph.init(Cipher.ENCRYPT_MODE, seckeySpec, new IvParameterSpec(new byte[16]));
return ciph.doFinal(message.getBytes(Charset.forName("US-ASCII")));
}
private static String getDecrypt(String key, byte[] encrypted) throws GeneralSecurityException {
final byte[] rawData = key.getBytes(Charset.forName("US-ASCII"));
if (rawData.length != 16) {
// If this is not 16 in length, there's a problem with the key size, nothing to do here
throw new IllegalArgumentException("Invalid key size.");
}
final SecretKeySpec seckeySpec = new SecretKeySpec(rawData, "AES");
final Cipher ciph = Cipher.getInstance("AES/CBC/PKCS5Padding");
ciph.init(Cipher.DECRYPT_MODE, seckeySpec, new IvParameterSpec(new byte[16]));
final byte[] decryptedmess = ciph.doFinal(encrypted);
return new String(decryptedmess, Charset.forName("US-ASCII"));
}
那么,为了进行尝试,此示例可能对您有效:
final String encrypt = "My16inLengthKey5";
final byte[] docrypt = getEncrypt(encrypt, "This is a phrase to be encrypted!");
final String douncrypt = getDecrypt(encrypt.toString(), docrypt);
Log.d("Decryption", "Decrypted phrase: " + douncrypt);
当然,
douncrypt
必须匹配这是一个要加密的短语代码>谢谢你的回答,你能给我更多关于这个实现的细节吗?关键是上下文?如果是,如果我尝试从其他活动调用类似的还原方法,则无法解密?否。密钥存储在可从所有活动访问的SharedReference后端中。阅读库中的文档了解更多信息。我尝试过,但得到了此错误java.lang.UnsupportedOperationException 02-12 21:29:05.026 2051-2051/com.myapp W/System.err﹕ 在com.facebook.crypto.streams.NativeGCMCipherOutputStream.write上
是否链接了本机库?无论如何,我建议打开一个新问题,提供有关此特定错误的更多信息。我已在libs
文件夹中添加了本机库,以及它们各自的“armeabi,
armeabi=v7`和x86
子文件夹。有什么我遗漏的吗?谢谢,但不幸的是我得到了java.lang.IllegalStateException:Cipher not initialized
mmm…创建一个新问题,提供有关错误的更多详细信息。我将用标准的密钥派生函数(如PBKDF)替换从StringToaeskey。这通常会提供更好的分布式密钥。为什么要加密和解密两次?这真的有效吗?首先,密码对象的状态不会正确结合。加密和解密两次以显示密封对象和密码流的使用方式,但其工作原理相同。你可以试着在投票前看看自己。编译这段代码只需要基本的调整(如适当的try-catch块),我得到的getBytes是“调用需要API级别9”。我能为Android KitKat API 19做些什么?这行代码“newstring(decryptedmems,Charset.forName(“US-ASCII”);”也得到了同样的结果
private static byte[] getEncrypt(final String key, final String message) throws GeneralSecurityException {
final byte[] rawData = key.getBytes(Charset.forName("US-ASCII"));
if (rawData.length != 16) {
// If this is not 16 in length, there's a problem with the key size, nothing to do here
throw new IllegalArgumentException("You've provided an invalid key size");
}
final SecretKeySpec seckeySpec = new SecretKeySpec(rawData, "AES");
final Cipher ciph = Cipher.getInstance("AES/CBC/PKCS5Padding");
ciph.init(Cipher.ENCRYPT_MODE, seckeySpec, new IvParameterSpec(new byte[16]));
return ciph.doFinal(message.getBytes(Charset.forName("US-ASCII")));
}
private static String getDecrypt(String key, byte[] encrypted) throws GeneralSecurityException {
final byte[] rawData = key.getBytes(Charset.forName("US-ASCII"));
if (rawData.length != 16) {
// If this is not 16 in length, there's a problem with the key size, nothing to do here
throw new IllegalArgumentException("Invalid key size.");
}
final SecretKeySpec seckeySpec = new SecretKeySpec(rawData, "AES");
final Cipher ciph = Cipher.getInstance("AES/CBC/PKCS5Padding");
ciph.init(Cipher.DECRYPT_MODE, seckeySpec, new IvParameterSpec(new byte[16]));
final byte[] decryptedmess = ciph.doFinal(encrypted);
return new String(decryptedmess, Charset.forName("US-ASCII"));
}
final String encrypt = "My16inLengthKey5";
final byte[] docrypt = getEncrypt(encrypt, "This is a phrase to be encrypted!");
final String douncrypt = getDecrypt(encrypt.toString(), docrypt);
Log.d("Decryption", "Decrypted phrase: " + douncrypt);