File Viewer, Part 5

Handling a particular form of encryption in the FileDumper program is not hard. Handling the general case is not. It’s not that decryption is difficult. In fact, it’s quite easy. However, most encryption schemes require more than simply providing a key. You also need to know an assortment of algorithm parameters, like initialization vector, salt, iteration count, and more. Higher-level protocols are usually used to pass this information between the encryption program and the decryption program. The most common type of protocol is to simply store the information unencrypted at the beginning of the encrypted file. You saw an example of this in the FileDecryptor and FileEncryptor programs. The FileEncryptor chose a random initialization vector and placed its length and the vector itself at the beginning of the encrypted file so the decryptor could easily find it.

For the next iteration of the FileDumper program, I am going to use the simplest available encryption scheme, DES in ECB mode with PKCS5Padding. Furthermore, the key will simply be the first eight bytes of the password. This is probably the least secure algorithm discussed in this chapter; however, it doesn’t require an initialization vector, salt, or other meta-information to be passed between the encryptor and the decryptor. Because of the nature of filter streams, it is relatively straightforward to add decryption services to the FileDumper program, assuming you know the format in which the encrypted data is stored. Generally, you’ll want to decrypt a file before dumping it. This does not require a new dump filter. Instead, I simply pass the file through a cipher input stream before passing it to one of the dump filters.

When a file is both compressed and encrypted, compression is usually performed first. Therefore, we’ll always decompress after decrypting. The reason is twofold. Since encryption schemes make data appear random, and compression works by taking advantage of redundancy in nonrandom data, it is difficult, if not impossible, to compress encrypted files. In fact, one quick test of how good an encryption scheme is checks whether encrypted files are compressible; if they are, it’s virtually certain the encryption scheme is flawed and can be broken. Conversely, compressing files before encrypting them removes redundancy from the data that a code breaker can exploit. Therefore, it may serve to shore up some weaker algorithms. On the other hand, some algorithms have been broken by taking advantage of magic numbers and other known plaintext sequences that some compression programs insert into the encrypted data. Thus, there’s no guarantee that compressing files before encrypting them will make them harder to penetrate. The best option is simply to use the strongest encryption that’s available to you.

We’ll let the user set the password with the -password command-line switch. The next argument after -password is assumed to be the password. Example 10.10, FileDumper5 , demonstrates.

Example 10-10. FileDumper5

import java.io.*;
import java.util.zip.*;
import java.security.*;
import javax.crypto.*;
import javax.crypto.spec.*;
import com.macfaq.io.*;

public class FileDumper5 {

  public static final int ASC = 0;
  public static final int DEC = 1;
  public static final int HEX = 2;
  public static final int SHORT = 3;
  public static final int INT = 4;
  public static final int LONG = 5;
  public static final int FLOAT = 6;
  public static final int DOUBLE = 7;
  
  public static void main(String[] args) {

    if (args.length < 1) {
      System.err.println(
       "Usage: java FileDumper5 [-ahdsilfx] [-little] [-gzip|-deflated] "
       + "[-password password] file1...");
    }

    boolean bigEndian = true; 
    int firstFile = 0;
    int mode = ASC;
    boolean deflated = false;
    boolean gzipped = false;
    String password = null;
    
    // Process command-line switches.
    for (firstFile = 0; firstFile < args.length; firstFile++) {
      if (!args[firstFile].startsWith("-")) break;
      if (args[firstFile].equals("-h")) mode = HEX;
      else if (args[firstFile].equals("-d")) mode = DEC;
      else if (args[firstFile].equals("-s")) mode = SHORT;
      else if (args[firstFile].equals("-i")) mode = INT;
      else if (args[firstFile].equals("-l")) mode = LONG;
      else if (args[firstFile].equals("-f")) mode = FLOAT;
      else if (args[firstFile].equals("-x")) mode = DOUBLE;
      else if (args[firstFile].equals("-little")) bigEndian = false;
      else if (args[firstFile].equals("-deflated") && !gzipped) deflated = true;
      else if (args[firstFile].equals("-gzip") && !deflated) gzipped = true;
      else if (args[firstFile].equals("-password")) {
        password = args[firstFile+1];
        firstFile++;
      }
    }
    
    for (int i = firstFile; i < args.length; i++) {
      try {
        InputStream in = new FileInputStream(args[i]);
        dump(in, System.out, mode, bigEndian, deflated, gzipped, password);
        
        if (i < args.length-1) {  // more files to dump
          System.out.println();
          System.out.println("--------------------------------------");
          System.out.println();
        }
      }
      catch (IOException e) {
        System.err.println(e);
        e.printStackTrace();
      }
    }
  }

  public static void dump(InputStream in, OutputStream out, int mode, 
   boolean bigEndian, boolean deflated, boolean gzipped, String password) 
   throws IOException {
    
    // The reference variable in may point to several different objects
    // within the space of the next few lines. 
    if (password != null) {
      // Create a key.
      try {
        byte[] desKeyData = password.getBytes();
        DESKeySpec desKeySpec = new DESKeySpec(desKeyData);
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
        SecretKey desKey = keyFactory.generateSecret(desKeySpec);

        // Use Data Encryption Standard.
        Cipher des = Cipher.getInstance("DES/ECB/PKCS5Padding");
        des.init(Cipher.DECRYPT_MODE, desKey);
        
        in = new CipherInputStream(in, des);
      }
      catch (GeneralSecurityException e) {
        throw new IOException(e.getMessage());
      }
    }
    
    if (deflated) {
      in = new InflaterInputStream(in);
    }
    else if (gzipped) {
      in = new GZIPInputStream(in);
    }

    // could really pass to FileDumper3 at this point
    if (bigEndian) {
      DataInputStream din = new DataInputStream(in);
      switch (mode) {
        case HEX: 
          in = new HexFilter(in);
          break;
        case DEC: 
          in = new DecimalFilter(in);
          break;
        case INT: 
          in = new IntFilter(din);
          break;
        case SHORT: 
          in = new ShortFilter(din);
          break;
        case LONG: 
          in = new LongFilter(din);
          break;
        case DOUBLE: 
          in = new DoubleFilter(din);
          break;
        case FLOAT: 
          in = new FloatFilter(din);
          break;
        default:
      }
    }
    else {
      LittleEndianInputStream lin = new LittleEndianInputStream(in);
      switch (mode) {
        case HEX: 
          in = new HexFilter(in);
          break;
        case DEC: 
          in = new DecimalFilter(in);
          break;
        case INT: 
          in = new LEIntFilter(lin);
          break;
        case SHORT: 
          in = new LEShortFilter(lin);
          break;
        case LONG: 
          in = new LELongFilter(lin);
          break;
        case DOUBLE: 
          in = new LEDoubleFilter(lin);
          break;
        case FLOAT: 
          in = new LEFloatFilter(lin);
          break;
        default:  
      }
    }   
    StreamCopier.copy(in, out);
    in.close();
  }
}

Note how little we had to change. I simply imported two more packages and added a command-line switch and about a dozen lines of code (which could easily have been half that) to build a Cipher object and add a cipher input stream to the chain. Other encryption schemes, like password-based encryption, would not be hard to support either. The main difficulty lies in deciding exactly how the key would be entered, since not all schemes have keys that map to passwords in a straightforward way. That’s left as an exercise for the reader.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.117.100.20