2016-09-25 141 views
0

我正在使用Cipher/CipherInputStream來加密和解密Android中的數據庫文件(sqlite)以進行備份。
當我使用沒有密碼的FileInputStream時,它工作得很好。但是,當我使用密碼時,文件會成功加密,但是當我解密(恢復)它時,數據庫不會解密爲原始「源代碼」。
原始caracteres(源代碼)似乎是一個表意文字/象形文字/漢字(我不知道),當我加密和解密時,「源代碼」是sql(英語)恢復OO
這使得'數據庫損壞'使用FileInputStream/Cipher在Android中加密/解密數據庫文件

才澄清

備份

File dbFile = new File(PATH_DB); 

FileInputStream fileInputStream = new FileInputStream(PATH_DB); 

FileOutputStream outputStream = new FileOutputStream(PATH_BKP); 

byte[] s = Arrays.copyOf(KEY_DATABASE.getBytes(),16); 
SecretKeySpec sks = new SecretKeySpec(s, "AES"); 

Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING"); 
cipher.init(Cipher.ENCRYPT_MODE, sks); 

CipherOutputStream cos = new CipherOutputStream(outputStream, cipher); 

//Transferencia dos dados do inputfile para o output 
byte[] buffer = new byte[1024]; 
int length; 
while ((length = fileInputStream.read(buffer))!= -1) { 
    cos.write(buffer,0,length); 
} 

//Fecha as streams 
cos.flush(); 
cos.close(); 
fileInputStream.close(); 

還原:

FileInputStream fis = new FileInputStream(PATH_BKP); 

FileOutputStream fos = new FileOutputStream(PATH_DB); 

byte[] s = Arrays.copyOf(KEY_DATABASE.getBytes(),16); 
SecretKeySpec sks = new SecretKeySpec(s, "AES"); 

Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING"); 
cipher.init(Cipher.DECRYPT_MODE, sks); 

CipherInputStream cis = new CipherInputStream (fis, cipher); 

byte[] buffer = new byte[1024]; 
int length; 
while ((length = cis.read(buffer)) != -1) { 
    fos.write(buffer, 0, length); 
} 

fos.flush(); 
fos.close(); 
cis.close(); 

回答

2

CBC模式需要一個初始化向量(IV)來操作。這個IV不是一個祕密價值,但它必須是不可預測的(閱讀:隨機選擇)。爲了解密工作,您必須使用相同的IV。否則,第一個塊將被損壞。解決這個問題的常用方法是在密文前寫入IV。

如果您在沒有IvParameterSpec的情況下調用Cipher#init作爲第三個參數,IV將自動爲您生成。如果你不存儲它,它將會丟失。

在加密過程中

Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING"); 
cipher.init(Cipher.ENCRYPT_MODE, sks); 

outputStream.write(cipher.getIV()); // store the generated IV 

CipherOutputStream cos = new CipherOutputStream(outputStream, cipher); 

在解密

byte[] iv = new byte[16]; // 16 is the block size of AES 
if (fis.read(iv) != 16) { 
    throw new Exception("Incomplete IV"); // TODO: rename to a different exception 
} 

Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING"); 
cipher.init(Cipher.DECRYPT_MODE, sks, new IvParameterSpec(iv)); 

CipherInputStream cis = new CipherInputStream (fis, cipher); 
+0

太好了!謝謝!它像一個魅力^^ – Jhon