最近在做一個項目,需要將資源文件(包括圖片、動畫等類型)進行簡單的加密後再上傳至雲上的服務器,而在應用程序中對該資源使用前先將讀取到的文件數據進行解密以得到真正的文件信息。此策略的原因與好處是將准備好的資源存儲在雲上,使用時通過網絡進行讀取即可,減少了應用程序本身的大小。這一點對於移動應用尤其重要,特別是在資源量較大且需要對其進行保護的時候。畢竟在目前所處的大環境下,要想復制一款軟件不難,那真正寶貴的就是不容易找到的資源了。
先對文件與加密的相關知識做一個極為簡單的科普(知道的可以跳過)。
文件與字串
A、文件的操作流程一般為:打開-->讀取-->對內容進行變換-->寫入-->關閉。
B、常規性文件操作會用到的類有五個:File,InputStream,OutputStream,FileInoutStream,FileOutputStream,均包含在java.io下面。注意,在使用前必須對類文件進行導入,方法為import java.io.File(實現時需要分號結尾)。
C、創建InputStream類和OutputStream類的對象時,new關鍵字後邊的類分別是FileInputStream和FileOutputStream(而不是其自身),如InputStream fin = new FileInputStream(File objectFile)。可以看出構造參數是File類型對象,其創建方式為File file = new File(String fileName)。
D、當String類對象作為函數參數時,可以直接傳入常量字符串,如“D:\\source.jpg”。String類對象構造方法中比較簡單也是最常用的一種是String string = “string content”,當然,最終執行的是String string = new String(“string content”)。其實String是非常重要的類(可以說無處不在),提供了一套完善、高效操作字串的方法,使得開發者受益匪淺。
E、常規性文件操作涉及到的方法有五個:exist(),read(),write(),flush(),close()。exist()判斷文件是否存在,調用者為File類對象;read()讀取輸入流中的內容,調用者為InputStream類對象;write()、flush()、close()的作用分別為向輸出流中寫內容、強制發送緩沖區中數據、保存並關閉文件,調用者為OutputStream類對象,不過InputStream類對象在操作完成後也需要close()。
加密算法
MD5:以512位分組來處理輸入的信息,且每一分組又被劃分為16個32位子分組,經過了一系列的處理後,輸出由四個32位分組組成,將這四個32位分組級聯後將生成一個128位散列值。
SHA:接收一段明文,然後以一種不可逆的方式將它轉換成一段(通常更小)密文,也可以簡單的理解為取一串輸入碼(稱為預映射或信息),並把它們轉化為長度較短、位數固定的輸出序列即散列值(也稱為信息摘要或信息認證代碼)的過程。
DES:把64位的明文輸入塊變為64位的密文輸出塊,它所使用的密鑰也是64位,主要分為兩步:
(1)初始置換,把輸入的64位數據塊按位重新組合,並把輸出分為L0、R0兩部分,每部分各長32位,其置換規則為將輸入的第58位換到第一位,第50位換到第2位......依此類推,最後一位是原來的第7位。L0、R0則是換位輸出後的兩部分,L0是輸出的左32位,R0是右32位。
(2)逆置換,經過16次迭代運算後,得到L16、R16,將此作為輸入,進行逆置換,逆置換正好是初始置換的逆運算,由此即得到密文輸出。
3-DES:使用3條56位的密鑰對數據進行三次加密,是DES向AES過渡的加密算法(1999年,NIST將3-DES指定為過渡的加密標准)。
AES:使用128、192、和256位密鑰,並且用128位分組加密和解密數據。
異或:與其說這是一種加密算法,倒不如稱其為文件信息的簡單變換,將每一個數據與某給定數據進行異或操作即可完成加密或解密,如dataEncrypt = dataSource^dataSecret。
OK,是時候回到文件加密與解密的具體實現這個主題上來了。後續的舉例均采用圖片(包括GIF動畫)類型,而其他類型資源的實現原理相同,就不一一給出了。首先來看對一幅JPG類型圖片進行異或加密的Java實現,由於是第一次給出代碼,所以貼上了Java文件“FileEncAndDec.java”的所有內容。
import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
public class FileEncAndDec {
private static final int numOfEncAndDec = 0x99; //加密解密秘鑰
private static int dataOfFile = 0; //文件字節內容
public static void main(String[] args) {
File srcFile = new File("桌面.jpg"); //初始文件
File encFile = new File("encFile.tif"); //加密文件
File decFile = new File("decFile.bmp"); //解密文件
try {
EncFile(srcFile, encFile); //加密操作
} catch (Exception e) {
e.printStackTrace();
}
}
private static void EncFile(File srcFile, File encFile) throws Exception {
if(!srcFile.exists()){
System.out.println("source file not exixt");
return;
}
if(!encFile.exists()){
System.out.println("encrypt file created");
encFile.createNewFile();
}
InputStream fis = new FileInputStream(srcFile);
OutputStream fos = new FileOutputStream(encFile);
while ((dataOfFile = fis.read()) > -1) {
fos.write(dataOfFile^numOfEncAndDec);
}
fis.close();
fos.flush();
fos.close();
}
}
從代碼可以看出,給定的加密秘鑰(異或數據,可以在合法范圍內隨便定義)為十六進制數0x99。圖片資源為以中文命名的“桌面.jpg”,加密文件為“encFile.png”,還有值為“decFile.bmp”的String類對象作為解密文件名稱。
相對應地,解密的實現幾乎和加密相同,只是輸入與輸出文件不同,看下面代碼。
private static void DecFile(File encFile, File decFile) throws Exception {
if(!encFile.exists()){
System.out.println("encrypt file not exixt");
return;
}
if(!decFile.exists()){
System.out.println("decrypt file created");
decFile.createNewFile();
}
InputStream fis = new FileInputStream(encFile);
OutputStream fos = new FileOutputStream(decFile);
while ((dataOfFile = fis.read()) > -1) {
fos.write(dataOfFile^numOfEncAndDec);
}
fis.close();
fos.flush();
fos.close();
}
由於加密後的圖片文件(保存為PNG類型)是不能直接在圖片查看器中打開的,因為其內容已經改變,所以其縮略圖標會顯示為兩朵不同顏色的花。對於其他類型的加密或損壞文件的縮略圖標:JPG為山水畫,BMP和TIF為畫刷塗鴉,GIF為三個不同顏色的幾何圖形。當然,這些默認的圖標應該會因系統而異。
下面給出初始、加密及解密後的圖標截圖:
和預想的一致,經測試發現以上方法對GIF動畫(不是GIF圖片,而是可以播放的動畫資源)的加密與解密同樣適用,代碼和截圖也就沒有區別了,不過還是貼上來:
File srcFile = new File("srcFile.gif"); //初始文件
File encFile = new File("encFile.gif"); //加密文件
File decFile = new File("decFile.gif"); //解密文件
有兩點需要注意:
1、在調用加密與解密方法時,必須加上異常處理塊(try{...}catch{...},否則編譯不通過)。
2、對用來加密或解密的源文件進行打開(讀取)操作之前,最好判斷其是否存在,免得造成意想不到的錯誤和時間的浪費。因為若文件不存在,後續的操作都是沒有意義的。
今天就先寫到這,總結一下吧。文件加密簡單地說就是對數據進行變換,雖然一千種方法可能會有一千種一種結果,但是思想是通用的。關鍵是加密所采用的算法的難易,有時間會對文中提到的算法用Java進行實現。