2014-02-11 37 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「));」 –