2012-06-13 21 views
3

只是爲了好玩,我一直在試圖製作一個文件加密器,它對128位密鑰進行按位異或加密。它似乎適用於使用某些編輯器的純文本文件,但對於其他人和更復雜的文件,我會遇到「損壞的文件」錯誤。如果我在文本文件上進行兩次加密,在Gedit中它會像原來那樣顯示原始文件的內容,但在記事本中,我只是得到了一大堆問號。我的xor加密無法在複雜文件上工作

我已經用Java寫了這段代碼,但是加密部分非常普遍。最關鍵的是在8個16位字符的陣列的形式:

char[] key = {26372, 15219, 53931, 50406, 26072, 23469, 25002, 37812}; 

要加密文件,我有運行此代碼的循環:

builder.append((char)(bR.read()^key[pos++])); 
if(pos == 8) pos = 0; //returns to first position in key 

這從讀取下一個字符該文件使用BufferedReader,將其與鍵中的下一個字符對齊並將其附加到StringBuilder。

這裏是整個代碼:

import java.awt.BorderLayout; 
import java.awt.Toolkit; 
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 
import java.io.BufferedReader; 
import java.io.BufferedWriter; 
import java.io.File; 
import java.io.FileReader; 
import java.io.FileWriter; 
import java.io.IOException; 

import javax.swing.JButton; 
import javax.swing.JFileChooser; 
import javax.swing.JFrame; 
import javax.swing.JOptionPane; 
import javax.swing.JPanel; 
import javax.swing.JProgressBar; 
import javax.swing.JScrollPane; 
import javax.swing.JTextArea; 
import javax.swing.SwingUtilities; 
import javax.swing.SwingWorker; 
import javax.swing.UIManager; 


public class Encryptor implements Runnable { 

    /** 
    * @param args 
    */ 
    public static void main(String[] args) { 
     try { 
      SwingUtilities.invokeAndWait(new Encryptor()); 
     } catch(Exception e) { 
      e.printStackTrace(); 
     } 
    } 

    JFrame frame; 
    JButton bCancel, bEncrypt; 
    JTextArea statusArea; 
    JScrollPane statusScroll; 
    JProgressBar progressBar; 
    String fileContents, fileType, fileName; 
    EncryptSwingWorker encryptWorker; 
    char[] key = { 
     26372, 15219, 53931, 50406, 26072, 23469, 25002, 37812}; 

    public void run() { 

     try { 
      UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); 
     } catch(Exception e) {} 

     frame = new JFrame(); 

     statusArea = new JTextArea(); 
     statusArea.setEditable(false); 
     statusScroll = new JScrollPane(statusArea); 
     progressBar = new JProgressBar(); 
     progressBar.setValue(0); 

     bCancel = new JButton("Cancel"); 
     bCancel.setEnabled(false); 
     bCancel.addActionListener(new ActionListener() { 

      @Override 
      public void actionPerformed(ActionEvent arg0) { 
       int option = JOptionPane.showOptionDialog(frame, 
         "Are you sure you want to cancel?", 
         "Warning", JOptionPane.YES_NO_OPTION, 
         JOptionPane.QUESTION_MESSAGE, null, 
         null, null 
       ); 
       if(option == 0) { 
        encryptWorker.cancel(true); 
        statusArea.append("Encryption cancelled\n"); 
        progressBar.setValue(0); 
        progressBar.setIndeterminate(false); 
        bCancel.setEnabled(false); 
        bEncrypt.setEnabled(true); 
       } 
      } 

     }); 
     bEncrypt = new JButton("Encrypt file..."); 
     bEncrypt.addActionListener(new ActionListener() { 

      @Override 
      public void actionPerformed(ActionEvent e) { 
       encryptWorker = Encryptor.this.new EncryptSwingWorker(); 
       encryptWorker.execute(); 
      } 

     }); 

     JPanel top = new JPanel(new BorderLayout()); 
     top.add(bEncrypt, BorderLayout.CENTER); 
     top.add(bCancel, BorderLayout.EAST); 
     frame.getContentPane().add(top, BorderLayout.NORTH); 
     frame.getContentPane().add(statusScroll, BorderLayout.CENTER); 
     frame.getContentPane().add(progressBar, BorderLayout.SOUTH); 

     frame.setSize(200, 150); 
     frame.setResizable(false); 
     frame.setTitle("sEncryptor"); 
     frame.setVisible(true); 
     frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); 
     frame.setIconImage(Toolkit.getDefaultToolkit().getImage("encryptor.png")); 
    } 

    void encrypt() throws IOException { 
     JFileChooser fc = new JFileChooser(); 
     int returnVal = fc.showOpenDialog(frame); 

     if(returnVal == JFileChooser.APPROVE_OPTION) { 
      if(fc.getSelectedFile() == null) { 
       JOptionPane.showOptionDialog(frame, 
        "You did not select a file", 
        "File Open Error", 
        JOptionPane.DEFAULT_OPTION, 
        JOptionPane.WARNING_MESSAGE, null, 
        null, null 
       ); 
       encrypt(); 
       return; 
      } 

      bCancel.setEnabled(true); 
      bEncrypt.setEnabled(false); 

      fileName = fc.getSelectedFile().getPath(); 

      statusArea.append("Calculating size...\n"); 
      progressBar.setIndeterminate(true); 
      int size = (int)(new File(fileName).length()/32); 
      progressBar.setIndeterminate(false); 
      progressBar.setMaximum(size); 

      BufferedReader bR = new BufferedReader(new FileReader(fc.getSelectedFile())); 
      StringBuilder builder = new StringBuilder(); 

      statusArea.append("Encrypting...\n"); 
      int pos = 0; 
      for(double d = 0; bR.ready(); d += 0.03125) { 
       if(encryptWorker.isCancelled()) return; 
       progressBar.setValue((int)d); 
       builder.append((char)(bR.read()^key[pos++])); 
       if(pos == 8) pos = 0; 
      } 

      progressBar.setValue(0); 
      progressBar.setIndeterminate(true); 

      fileContents = builder.toString(); 
      bR.close(); 
      statusArea.append("Encryption finished\n"); 
      progressBar.setIndeterminate(false); 
     } 
    } 

    void save(String file) throws IOException { 
     statusArea.append("Saving...\n"); 
     progressBar.setIndeterminate(true); 
     BufferedWriter bW = new BufferedWriter(new FileWriter(file)); 
     bW.write(fileContents); 
     bW.close(); 
     progressBar.setIndeterminate(false); 
     statusArea.append("Done!\n"); 
    } 

    void saveAs() throws IOException { 
     JFileChooser fc = new JFileChooser(); 
     int returnVal = fc.showSaveDialog(frame); 

     if(returnVal == JFileChooser.APPROVE_OPTION) { 
      if(fc.getSelectedFile() == null) { 
       JOptionPane.showOptionDialog(frame, 
        "You did not select a file", 
        "File Open Error", 
        JOptionPane.DEFAULT_OPTION, 
        JOptionPane.WARNING_MESSAGE, null, 
        null, null 
       ); 
       saveAs(); 
       return; 
      } 

      save(fc.getSelectedFile().getPath()); 
     } 
    } 

    class EncryptSwingWorker extends SwingWorker<Void, Void> { 

     @Override 
     protected Void doInBackground() throws Exception { 
      encrypt(); 
      if(EncryptSwingWorker.this.isCancelled()) return null; 
      bCancel.setEnabled(false); 
      while(true) { 
       Object[] options = {"Override", "Save As...", "Cancel"}; 
       int option = JOptionPane.showOptionDialog(frame, 
         "Override existing file?", 
         "Save", JOptionPane.YES_NO_CANCEL_OPTION, 
         JOptionPane.QUESTION_MESSAGE, null, 
         options, options[0] 
       ); 
       if(option == 0) 
        try { 
         save(fileName); 
        } catch(IOException e1) { 
         e1.printStackTrace(); 
        } 
       else if(option == 1) 
        try { 
         saveAs(); 
        } catch(IOException e1) { 
         e1.printStackTrace(); 
        } 
       else { 
        int option1 = JOptionPane.showOptionDialog(frame, 
          "The encryption will be lost if you continue\n" + 
          "Are you sure you want to cancel?", 
          "Warning", JOptionPane.YES_NO_OPTION, 
          JOptionPane.QUESTION_MESSAGE, null, 
          null, null 
        ); 
        if(option1 == 1) continue; 
        break; 
       } 
       break; 
      } 
      fileContents = ""; 
      bEncrypt.setEnabled(true); 
      return null; 
     } 

    } 
} 

有我丟失的東西在我的代碼,或不異或整個文件造成的問題?

+3

你能*請*只包括能夠證明最小的,其實相關的代碼失敗? –

+1

聖牛,這是很多未註釋的代碼。請考慮將其發佈在[代碼評論](http://codereview.stackexchange.com/)上。也就是說,我將它運行在一個文件上,它創建了一個新的'3F'文件_full_(十進制63)字符。你可能會嘗試調試它,一步一步來,並檢查你的邏輯。 –

+1

從嘗試閱讀源代碼,這裏有一些東西作爲未來自我改進的建議 - [enums](https://www.google.com/search?q=java%20enum)!更不容易出錯,更易讀。 –

回答

3

明白了。問題出在文件編碼中,一如既往:)。從來沒有,有史以來,取決於默認編碼 - 它最終會適得其反。

由於ReaderWriter類用於明文輸入/輸出,所以會變得混亂。在這種情況下,Writer不知道如何處理它獲得的各種整數,並最終失敗。當您修復Writer,你必須修復Reader,也因爲加密的數據可能不被有效字符...

最好的解決方案是忽略字符,請使用字節和FileInputStreamFileOutputStream(由包裹他們的Buffered兄弟,如果需要的話),因爲這些是爲了處理二進制數據。這樣,你最終會讀取/寫入你想要讀取/寫入的數據。試着自己一起破解它,這是一個很好的教訓。如果你堅持不懈,看看here

最簡單的解決方案是隻改變兩行。請注意,它將再次僅適用於文本文件!嚴肅地說,不要將隨機數字和可打印的字符混淆在一起。

BufferedReader bR = new BufferedReader(new FileReader(fc.getSelectedFile())); 

BufferedReader bR = new BufferedReader(new InputStreamReader(new FileInputStream(fc.getSelectedFile()), "UTF-8")); 

BufferedWriter bW = new BufferedWriter(new FileWriter(file)); 

BufferedWriter bW = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), "UTF-8")); 
+2

只需添加:簡而言之,在Java中使用裸流來處理二進制數據,對文本使用讀寫器(使用適當的編碼)。 – nhahtdh

+0

絕對如此,我將它嵌入到答案中。 –