2012-12-13 163 views
1

我正在寫一個程序,在該程序中,我接受一個字符串,對其進行加密,然後將其寫入文件中。然後,我從文件中讀取字符串,解密並修改它。這裏是我的DES加密/解密代碼:DES從文件中加密/解密

/* class for crypting and decrypting a file */ 
class DESEncrypter 
{ 
private Cipher encryptionCipher; 
private Cipher decryptionCipher; 

public DESEncrypter (SecretKey key) throws Exception 
{ 
encryptionCipher = Cipher.getInstance("DES/ECB/PKCS5Padding"); 
encryptionCipher.init(Cipher.ENCRYPT_MODE, key); 
decryptionCipher = Cipher.getInstance("DES/ECB/PKCS5Padding"); 
decryptionCipher.init(Cipher.DECRYPT_MODE, key); 
} 

/* write to 'out' the encryption of the information read from 'in' */ 
public String encrypt(String unencryptedString) 
{ 
    String encryptedString = ""; 

    try { 
     byte[] unencryptedByteArray = unencryptedString.getBytes("UTF8"); 

     byte[] encryptedBytes = this.encryptionCipher.doFinal(unencryptedByteArray); 

     encryptedString = new sun.misc.BASE64Encoder().encode(encryptedBytes); 

    } catch (Exception ex) { 
     Logger.getLogger(DESEncrypter.class.getName()).log(Level.SEVERE, null, ex); 
    } 

    return encryptedString; 
} 

private static String bytes2String(byte[] bytes) 
{ 

    StringBuffer stringBuffer = new StringBuffer(); 
    for (int i = 0; i < bytes.length; i++) 
    { 
     stringBuffer.append((char) bytes[i]); 
    } 

    return stringBuffer.toString(); 
} 

/* write to 'out' the information obtained by decrypting the information read from 'in' */ 
public String decrypt (String encryptedString) throws UnsupportedEncodingException 
{ 
    byte[] unencryptedByteArray = new byte[4096]; 

    try { 
     // Encode bytes to base64 to get a string 
     byte[] decodedBytes = new sun.misc.BASE64Decoder().decodeBuffer(encryptedString); 

     // Decrypt 
     unencryptedByteArray = this.decryptionCipher.doFinal(decodedBytes);  
    } catch (Exception ex) { 
     Logger.getLogger(DESEncrypter.class.getName()).log(Level.SEVERE, null, ex); 
    } 

    return bytes2String(unencryptedByteArray); 
} 
} 

而這正是我在文件中寫一個加密的字符串函數:

public void writeToFileEncrypted(String filename, String owner, String departament) 
{ 
try 
    { 
     BufferedReader br = new BufferedReader(new FileReader(new File("files_encrypted"))); 
     String crypt = ""; 
     String aux; 
     while ((aux = br.readLine()) != null) 
     { 
      crypt += aux; 
     } 
     br.close(); 

     String info = this.server.crypt.decrypt(crypt); 
     info += filename + " " + owner + " " + departament + "\n"; 

     /* delete the old encryption */ 
     File temp = new File("files_encrypted"); 
     temp.delete(); 

     String infoCrypt = this.server.crypt.encrypt(info); 

     File newFiles = new File("files_encrypted"); 
     if (newFiles.createNewFile() == false) 
     { 
    log.severe("Failed to re-create the 'files_encrypted' file when trying to add a new file"); 
    return; 
     } 

     BufferedWriter bw = new BufferedWriter(new FileWriter(newFiles)); 
     bw.write(infoCrypt); 
     bw.close(); 
    } 
    catch (Exception e) 
    { 
     log.warning("An exception was caught while trying to remove '" + clientName + "' from the banned list"); 
     e.printStackTrace(); 
     return; 
} 
} 

當服務器運行時,我可以修改該字符串從文件(多次運行該功能)。問題是,當我關閉服務器,然後我再次打開它,因爲我得到的錯誤: javax.crypto.BadPaddingException:給定最後一個塊未正確填充

這是我從文件中讀取服務器開啓時:

BufferedReader br = new BufferedReader(new FileReader(new File("files_encrypted"))); 
String crypto = new String(); 
String aux; 
while ((aux = br.readLine()) != null) 
{ 
    crypto += aux; 
    readBytes++; 
} 
br.close(); 
System.out.println(readBytes); 
info = this.crypt.decrypt(crypto); 

爲什麼我得到那個錯誤?我做錯了什麼?我必須以其他方式將加密的字符串寫入文件中?

以後編輯:

我已經改變,從文件中讀取一個字符串函數,解密它,修改它,對它進行加密,然後在文件中寫道。

public void writeToFileEncrypted(String filename, String owner, String departament) 
{ 
try 
    { 
     File f = new File("files_encrypted"); 
     int nrRead = 0; 
     String info = null; 
     FileInputStream fis = new FileInputStream(f); 
     StringBuffer sb = new StringBuffer(); 
     int ch; 
     while ((ch = fis.read()) != -1) 
     { 
      sb.append((char)ch); 
      nrRead++; 
     } 
     fis.close(); 

     StringBuilder sba = null; 
     if (nrRead != 0) 
     { 
      info = this.server.crypt.decrypt(new String(sb.toString().getBytes("UTF-8"), "UTF-8")); 
      sba = new StringBuilder(info); 
      sba.append(filename + " " + owner + " " + departament + " "); 
     } 
     else 
     { 
      sba = new StringBuilder(filename + " " + owner + " " + departament + " "); 
     } 

     /* delete the old encryption */ 
     File temp = new File("files_encrypted"); 
     temp.delete(); 
     //System.out.println("before: " + sba.toString()); 
     String infoCrypt = this.server.crypt.encrypt(sba.toString()); 
     //System.out.println("after: " + infoCrypt); 
     File newFiles = new File("files_encrypted"); 
     if (newFiles.createNewFile() == false) 
     { 
    log.severe("Failed to re-create the 'files_encrypted' file when trying to add a new file"); 
    return; 
     } 

     FileOutputStream fos = new FileOutputStream(newFiles); 
     fos.write(infoCrypt.getBytes("UTF-8")); 
     fos.flush(); 
     fos.close(); 
    } 
    catch (Exception e) 
    { 
     log.warning("An exception was caught while trying to remove '" + clientName + "' from the banned list"); 
     e.printStackTrace(); 
     return; 
} 
} 

我也修改了,我從文件中讀取的信息時,服務器打開的第一次:

FileInputStream fis = new FileInputStream(f); 
StringBuffer sb = new StringBuffer(); 
int ch; 
while ((ch = fis.read()) != -1) 
{ 
    sb.append((char)ch); 
    readBytes++; 
} 

fis.close(); 
if (readBytes != 0) 
{ 
    System.out.println("on: " + sb.toString()); 
    info = this.crypt.decrypt(new String(sb.toString().getBytes("UTF-8"), "UTF-8"));     
    System.out.println("load: " + info); 
} 
} 

在用的System.out.println「上:」我從讀文件正是我寫的加密,沒有任何空格或新行。如果我用read(buffer)讀取,其中緩衝區是byte [],看起來增加了很多空間。

雖然我做了這一切的改變我仍然得到錯誤javax.crypto.BadPaddingException:鑑於最終塊未正確填充

有人偷任何想法,這是怎麼回事呢?

+0

我的第一個猜測是你的閱讀方法。首先,在這裏使用字符串連接是不好的做法。使用一個StringBuilder。其次,你使用readline作爲二進制數據。第三你編碼('getBytes')爲UTF8。請注意,Java標準是UTF 16.這可能會破壞您的閱讀。 – Fildor

+0

@Fildor - 加密的數據是Base64編碼的,所以它不是二進制數據。而且,只要正確的編碼用於編碼和解碼,getBytes()就可以了。 java在內部使用UTF-16,但這是無關緊要的。 – jtahlborn

+0

這是一個字符串,是的。但是String代表二進制數據。如果他將它讀到一個字節[]然後創建一個字符串,他至少在調試時會有更多的控制權。新行將被刪除。但是換行符在這裏可能不是新行。 – Fildor

回答

1

這裏有幾件事。

private static String bytes2String(byte[] bytes) 

是狡猾的,你在這個方法鑄造一個字節到一個char所以此處沒有指定字符編碼。要將字節轉換爲字符,您應該只使用帶有字節數組和編碼的字符串構造函數。例如

byte[] tmp = new byte[10];  
    String a = new String(tmp, "UTF-8"); 

小心使用BufferedReaders + .readLine() - 這將您的文件剔除任何換行符當你讀它,除非你將它們重新添加到您的緩衝區。雖然我不認爲這是你的問題。

但我認爲簡化代碼的最好方法是通過OutputStream將編碼字節直接寫入文件。除非需要通過不喜歡二進制數據的傳輸方式發送文件的內容,否則不需要base64編碼。只需使用Input/OutputStreams將加密字節直接寫入磁盤即可。

迴應以後編輯:

你還是混合了您的二進制數據(字節)和字符數據(字符串/字符)的使用。你不能做這樣的事情:

int ch; 
    while ((ch = fis.read()) != -1) 
    { 
     sb.append((char)ch); 

輸入流重新調整字節,一個字節是不是一個字符,只是它鑄造人會造成很多問題。使用加密時,加密操作的輸出是二進制數據,解密操作的輸入也是二進制數據。事實上,你正在加密文本是你在加密之前和解密之後處理的東西。你的基本操作應該遵循以下幾行。

  • 取出要加密的文本並將其轉換爲字節,然後使用字符串上的.getBytes(String charsetName)指定編碼。
  • 通過這些字節到你的加密程序
  • 寫所得的字節直接到磁盤

要解密:

  • 從文件
  • 讀取的字節中傳遞的字節你解密例程(如字節!不包含字符串/文本)
  • 取出輸出字節並重新構造字符串使用新字符串(byte [] bytes,String char setName)指定與以前相同的編碼。有用

您可能會發現下面的(未經測試,但應工作)方法:

public byte[] readBinaryFile(File f) throws IOException 
{  
    byte[] contents = new byte[(int)f.length()]; 
    BufferedInputStream bis = null; 
    try 
    { 
     bis = new BufferedInputStream(new FileInputStream(f)); 
     DataInputStream dis = new DataInputStream(bis); 
     dis.readFully(contents); 
    } 
    finally 
    { 
     if(bis != null) 
     { 
      bis.close(); 
     } 
    }   
    return contents;    
} 

public void writeBinaryFile(byte[] contents, File f) throws IOException 
{ 
    BufferedOutputStream bos = null; 
    try 
    { 
     bos = new BufferedOutputStream(new FileOutputStream(f)); 
     bos.write(contents); 
    } 
    finally 
    { 
     if(bos != null) 
     { 
      bos.close(); 
     } 
    }   
} 

所以,你也需要改變接口,和你的加密的內部,所以他們需要解密的方法和返回字節數組,並丟棄base64編碼。

+0

我已經完成了你所說的修改(我添加了最後編輯部分),但我仍然得到相同的異常。 –

+0

我已經更新了我的答案... –

+0

非常感謝。我自己找到了解決方案,但就像你的例子。 –

0

我認爲這是在初始化

SecureRandom sr = new SecureRandom();
cipher.init(Cipher.DECRYPT_MODE,desKey,sr);

0

不知道這是主要的問題,但是當你從decrypt()返回解密的字符串,你應該使用:

return new String(unencryptedByteArray, "UTF-8"); 
1

你有幾個問題。讀取和解密過程應與加密和寫入過程對稱。但

  • 你改變你的字符串到使用getBytes("UTF8")一個byte [],這是很好的,但你不使用new String(byte[], "UTF8")做反向操作。
  • 您將整個字符串寫入文件(包括潛在換行符),但是您逐行閱讀並逐行連接,因此會丟失進程中的換行符。您必須閱讀已寫入的每個字符。

另外,不應該依賴諸如sun.misc.Base64Encoder/Decoder之類的未公開的,不受支持的類。使用Apache commons-codec來查找記錄的Base64編碼,保證在下一個JDK出來時仍然存在,並且可以在每個JVM上使用,包括非Sun JVM。

+0

丟失換行符不應影響加密過程。 – jtahlborn

+0

是的,它絕對會!在那裏,做到了(錯誤)。 – Fildor