title: My xor encryption not working on complex files
body: <p>Just for fun I have been trying to make a file encryptor which does a bitwise xor encryption against a 128-bit key. It seems to work for plain text files using some editors, but for others and more complex files I encounter a "corrupted file" error. If I do the encryption on a text file twice, in Gedit it displays the contents of the original file like it should, but in Notepad, I just get a whole bunch of question marks.</p>
  
  <p>I have written this code in Java, however the encryption part is pretty universal. The key is in the form of an array of 8 16-bit characters:</p>
  
  <pre><code>char[] key = {26372, 15219, 53931, 50406, 26072, 23469, 25002, 37812};
  </code></pre>
  
  <p>To encrypt the file, I have a loop which runs this code:</p>
  
  <pre><code>builder.append((char)(bR.read() ^ key[pos++]));
  if(pos == 8) pos = 0; //returns to first position in key
  </code></pre>
  
  <p>This reads the next character from the file using a BufferedReader, xors it against the next character in the key and appends it to a StringBuilder.</p>
  
  <p>Here is the entire code:</p>
  
  <pre><code>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&lt;Void, Void&gt; {
  
          @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;
          }
  
      }
  }
  </code></pre>
  
  <p>Is there something I'm missing in my code, or does xoring the entire file cause problems?</p>