4

I am making an application in Java and I want to allow users to encrypt a file (or folder - I'd zip the directory) using a password of their choice. I currently have the following method(s):

static Cipher createCipher(int mode, String password) throws Exception {
            PBEKeySpec keySpec = new PBEKeySpec(password.toCharArray());
            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
            SecretKey key = keyFactory.generateSecret(keySpec);
            MessageDigest md = MessageDigest.getInstance("MD5");
            md.update("input".getBytes());
            byte[] digest = md.digest();
            byte[] salt = new byte[8];
            for (int i = 0; i < 8; ++i)
              salt[i] = digest[i];
            PBEParameterSpec paramSpec = new PBEParameterSpec(salt, 20);
            Cipher cipher = Cipher.getInstance("PBEWithMD5AndDES");
            cipher.init(mode, key, paramSpec);
            return cipher;
    }

     static void applyCipher(String inFile, String outFile, Cipher cipher) throws Exception {
            String decryption = "";
            CipherInputStream in = new CipherInputStream(new FileInputStream(inFile), cipher);
            BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(outFile));
            int BUFFER_SIZE = 8;
            byte[] buffer = new byte[BUFFER_SIZE];
            int numRead = 0;
            do {
              numRead = in.read(buffer);
              System.out.println(buffer + ", 0, " + numRead);
              if (numRead > 0){
                out.write(buffer, 0, numRead);
                System.out.println(toHexString(buffer, 0, numRead));
              }
             } while (numRead == 8);
            in.close();
            out.flush();
            out.close();
          }
     private static char[] hex_table = {
            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 
            'a', 'b', 'c', 'd', 'e', 'f'};

     public static String toHexString(byte[] data, int offset, int length)
     {
       StringBuffer s = new StringBuffer(length*2);
       int end = offset+length;

       for (int i = offset; i < end; i++)
       {
         int high_nibble = (data[i] & 0xf0) >>> 4;
         int low_nibble = (data[i] & 0x0f);
         s.append(hex_table[high_nibble]);
         s.append(hex_table[low_nibble]);
       }

       return s.toString();
     }

However, to make the program more user friendly I would like to be able to check that the user has entered the correct password before a file is produced. I don't want to "leave the key under the door mat" or completely undo the security etc. - I just want to prevent the wrong file from being produced if the user enters the wrong password...

Any ideas will be greatly appreciated. If you need anymore details please don't hesitate to ask.

Thanks in advance

Andy
  • 3,600
  • 12
  • 53
  • 84
  • If I were to implement something like this I would zip the files the user selected, but I would also add a very small file ( 1 byte ) used to verify the password, then verify the contents of the file. – Security Hound Apr 04 '12 at 14:22

3 Answers3

3

You could save the encrypted password with the file. When the user enters the password, you encrypt it and check, if the same encrypted password is in the file. If not, you dont load the file.

ricky ho
  • 73
  • 4
  • Thank you for your answer, but I don't quite understand what you mean. Are you saying that I should store the password as plain text next to the encrypted file, or store the password in an encrypted file with a fixed password/algorithm? Please elaborate/explain... – Andy Apr 04 '12 at 14:31
  • when the user gives a password to the file, you encrypt it and save it in the file (which is encrypted the same way). when the user loads the file, you prompt for the password, encrypt it and compare it with the encrypted password from the file. so the password is stored in the file, but encrypted. i think thats the same like passwords are stored in unix. – ricky ho Apr 04 '12 at 15:03
  • Thanks again, I've kind of ended up using this method but now I'm storing the password at the top of the file (as you will see by reading the accepted answer). – Andy Apr 06 '12 at 18:20
2

Use PBKDF2WithHmacSHA1 and not PBEWithMD5AndDES. The later users two different outdated primitives. The former is the current standard.

you have two options

  1. Fast but less secure: Put a short known value at the start of your encrypted file or encrypt an entirely different short file under the same password. When you decrypt this file, check for the known value.

    Clearly this works quickly. Its slightly less secure because it means an attacker attempting to brute force the password can discard a guessed password faster: instead of having to look at the whole file, they just have to check that value. This is not really a big issue since your key derivation function should be hard enough and they still have to run that

  2. Store the hash of the file encrypted as well and verify the hash on decryption. More secure in that the attacker has to decrypt the whole file and read through it, but by the same token it is slow.

imichaelmiers
  • 3,449
  • 2
  • 19
  • 25
  • I know you're not supposed to, but I assume by 'less insecure' you mean 'less secure'!? Also, expanding on those idea's, do you think I'd be able to use the method on the following link to store a hashed version of the password at the top of the file to verify, and then continue decrypting the rest of the file if the password is correct? If so, how secure would that be? http://www.rgagnon.com/javadetails/java-0400.html Thank you for your answer by the way, especially with the primitives/standards! – Andy Apr 05 '12 at 10:40
  • Also, I've just tried to implement that PBKDF2WithHmacSHA1 and create a cipher the same as I did with PBEWithMD5AndDES but every time I do so I receive a `InvalidKeySpecException: Salt not found`. Anybody know why? I'm using the latest version of Java/JDK. – Andy Apr 05 '12 at 18:16
  • Yep i mean less secure. Provided the file that has the password hash in it is encrypted, sure. In fact, you could just store the password at that point since the file is secure. Really though, @erickson's post below is probably the best idea if you can get an authenticated block cipher mode of operation (its not supported in all java platforms) I don't think PBKDF2 is a drop in replacement for PBE. It requires different paramaters I think. I believe this is a working example http://stackoverflow.com/a/992413/980922 – imichaelmiers Apr 05 '12 at 20:20
  • Thanks for that link and clarification. I've tried to implement the PBKDF2 using a few online examples, like the one on the following link. However, as far as i can see now, it gives a different result everytime you create a new cipher even with the same password and salt - which is not what I want. Any ideas? – Andy Apr 06 '12 at 14:09
  • With the same salt, password, and iteration count, PBKDF2 will produce the same key. You mention cipher text. By design(in fact its critical to their security) ciphers like AES given the same key and message will produce different cipher texts each time. This is a feature. – imichaelmiers Apr 06 '12 at 15:03
  • I'm using the same... everything - nothing is different - in fact, I'm testing using the class found on the following link (sorry, I forgot to add it in my last comment). https://gist.github.com/874426 But if you're saying the ciphers like AES produce a different result every time how am I ever supposed to decrypt a file that has previously been encrypted? – Andy Apr 06 '12 at 15:36
  • You need to know the IV when decrypting. ( don't hard code it it needs to be random) with the cipher text. I'd just prepend it to the byte array that has the ciphertext in it. Then on decrypt, take the first 16 bytes and that is the IV. – imichaelmiers Apr 06 '12 at 15:49
  • Just to clarify, I am working with whole files and not just Strings. Also, doesn't putting the unencrypted IV at the top file drastically reduce the level of security, or doesn't it work like that? – Andy Apr 06 '12 at 16:02
  • Your need to store it in the file then. And no, IV's can be public( they basically have to be actually), but they must be random. – imichaelmiers Apr 06 '12 at 17:03
  • Oh, that's very good - I've got the encryption and decryption working how I would like it to now! Thank you so much for your help... – Andy Apr 06 '12 at 18:17
1

I would use an AEAD mode, like CCM or EAX. This will check the integrity of every block of the file as it is decrypted, failing if the key is incorrect or the file has been tampered. The Bouncy Castle provider supports both of these modes.

erickson
  • 265,237
  • 58
  • 395
  • 493
  • Thank you for your answer and sincere apologies for the lack of my response. I would have implemented an AEAD mode but to be honest it seems a bit complicated for what I am doing (at the minute) and the answer I've accepted is the most suitable for my situation... +1 – Andy Apr 06 '12 at 18:27