加密保存和解密加载可序列化对象的ArrayList

我使用这两种方法在sd卡中保存并加载包含可序列化对象的ArrayList的文件

保存方法

 public static void saveUserList(ArrayList 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(); } } } 

加载方法

  public static ArrayList loadUserList() { if (storageAvailable()) { ArrayList userList = new ArrayList(); try { FileInputStream userList = new FileInputStream(baseDir + File.separator + baseAppDir + File.separator + fileName); ObjectInputStream oos = new ObjectInputStream( userList); userList = (ArrayList) oos.readObject(); oos.close(); } catch (Exception exc) { exc.printStackTrace(); } return userList; } else { return null; } } 

现在我希望saveUserList方法在保存期间根据特定的String keyword加密文件的内容,方法loadUserList使用相同的关键字解密文件以返回arrayList。

我怎么能这样做? 我已经看了CipherOutputStream但是我还没有理解我应该如何使用它。

该方法建议使用Conceal库

 public static void saveUserListCrypted(ArrayList 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(); } } } 

导致此错误

  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 

尝试(添加适当的检查并尝试我省略的块以使代码更具可读性)这样的事情来保存

 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; } 

从String创建一个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; } 

注意:

此代码加密和解密两次,以显示密封对象和密码流的使用方式

我建议看一下最近由facebook发布的Conceal: http : //facebook.github.io/conceal/

这应该是一个简单的修改,用于包含当前代码中使用的ObjectOutputStream的Conceal输出流:

 public static void saveUserList(ArrayList 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(); } } } 

我会将恢复作为练习留给读者。 ;)

您可以简单地使用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必须匹配This is a phrase to be encrypted!