2014-02-11 34 views
7

我保存和加载在SD卡,它包含序列化的对象的这两种方法加密保存和解密的序列化的对象的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(); 
      } 
     } 

    } 

负载方法的ArrayList文件

public static ArrayList<User> loadUserList() { 
     if (storageAvailable()) { 
      ArrayList<User> userList = new ArrayList<User>(); 
      try { 
       FileInputStream userList = new FileInputStream(baseDir 
         + File.separator + baseAppDir + File.separator 
         + fileName); 

       ObjectInputStream oos = new ObjectInputStream(
         userList); 

       userList = (ArrayList<User>) oos.readObject(); 
       oos.close(); 

      } catch (Exception exc) { 
       exc.printStackTrace(); 
      } 

      return userList; 
     } else { 
      return null; 
     } 

    } 

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

我该怎么做? 我看了CipherOutputStream,但我还没有理解我该如何使用它。 http://facebook.github.io/conceal/

这应该是:

的方法建议采用隐藏式库

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

} 

原因这个错误

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 

回答

7

尝试(加入适当的检查和尝试,我已经省略,以使代码更可读的)类似这样的块保存

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

注:

这段代码进行加密和解密两次来显示使用在密闭的对象的方式和密码流

+0

谢谢,但不幸的是我得到'java.lang.IllegalStateException:密码未初始化' – AndreaF

+0

mmm ...创建一个关于错误的更多细节的新问题 – Silverstorm

+0

好吧,我已经解决了,谢谢 – AndreaF

3

我建议采取看看隐匿,最近被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(); 
     } 
    } 

} 

我会将还原作为读者的练习。 )

+0

谢谢你的答案,你能给我更多关于这个实现的细节吗?关键是上下文吗?如果是,如果我尝试从另一个活动调用类似的恢复方法无法解密? – AndreaF

+0

密钥存储在可从所有活动访问的SharedPreference后端。阅读图书馆的文档以获取更多信息。 –

+0

我试过了,但是得到这个错误'java.lang.UnsupportedOperationException 02-12 21:29:05.026 2051-2051/com.myapp W/System.err:at com.facebook.crypto.streams.NativeGCMCipherOutputStream.write' – AndreaF

2

你可以简单地使用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!

+0

对于getBytes,我得到“调用需要API级别9”。我能为Android KitKat API 19做什么?而且我也得到了同样的东西,这一行“new String(decryptedmess,Charset.forName(”US-ASCII“));” –