views:

2603

answers:

5

I'm trying to learn how to do passphrase-based encryption with Java. I'm finding several examples online, but none (yet) on Stack Overflow. The examples are a little light on explanation for me, particularly regarding algorithm selection. There seems to be a lot of passing strings around to say what algorithms to use, but little documentation as to where the strings came from and what they mean. And it also seems like the different algorithms may require different implementations of the KeySpec class, so I'm not sure what algorithms can use the PBEKeySpec class I'm looking at. Furthermore, the examples all seem a little out of date, many requiring you to get an older cryptography package that used to not be part of the JDK, or even a third-party implementation.

Can someone provide a straightforward introduction to what I need to do to implement encrypt(String data, String passphrase) and decrypt(byte[] data, String passphrase)?

+2  A: 

You need an encryption library, which will tell you how to set it up.
I happen to like the stuff from bouncycastle.org. You can find their how to here The DES the refer to in the 5.1 example, is one of the encryptions they offer. What the actual string means, will depend on the provider. Essentially you load the library.

Security.addProvider(new BouncyCastleProvider());

And then only use the JCE interfaces to do whatever you want:

 keyGen = KeyGenerator.getInstance("DES", "BC");

Java handles the binding of the library and the interfaces for you, you don't have to do that. I'd be more then happy to explain more, if you have any questions. Unfortunately at the moment I'm suffering from "I can't remember how I learned it" disease, so please feel free to ask.

Jim Barrows
A: 

You could use a hash algorithm (multiple times if necessary) to get from the passphrase to some raw data you can use as a key (+ an initialisation vector if the algorithm calls for one).

Then you can use that key with any symmetric algorithm - such as 3DES-CBC or AES-CBC (DES is considered obsolete these days).

Depending on the JCE you have available you may have different algorithms at your disposal, but AES is probably what you want. Choice of algorithm and exactly how to use it is somewhat a religious issue, however, and you would be ill advised to try and roll your own, or even to try and build some encryption scheme of your own using standard algorithms. You will almost certainly get it wrong if you have not studied it, and maybe even if you have.

If the security is that important to you that you are considering encryption, then you should consider also looking at a security engineering book like Applied Cryptography by Bruce Schneier or Security Engineering by Ross Anderson - there are a lot of implementation pitfalls. For example, using a passphrase as a key is not that great an idea in the first place, as it essentially reduces the size of your key.

You could also look at designs that other people have done, there are lots at the IETF, e.g.: http://tools.ietf.org/html/draft-mcgrew-aead-aes-cbc-hmac-sha1-00

frankodwyer
+7  A: 

I'll be cautious about giving or taking security-related advice from a forum... the specifics are quite intricate, and often become outdated quickly.

Having said that, I think Sun's Java Cryptography Architecture (JCA) Reference Guide is a good starting point. Check out the accompanying code example illustrating Password-Based Encryption (PBE).

Btw, the standard JRE provides only a few options out-of-the-box for PBE ("PBEWithMD5AndDES" is one of them). For more choices, you'll need the "strong encryption pack" or some third-party provider like Bouncy Castle. Another alternative would be to implement your own PBE using the hash and cipher algorithms provided in the JRE. You can implement PBE with SHA-256 and AES-128 this way (sample encrypt/decrypt methods).

Briefly, the encrypt method for PBE may involve the following steps:

  1. Get password and cleartext from the user, and convert them to byte arrays.
  2. Generate a secure random salt.
  3. Append the salt to the password and compute its cryptographic hash. Repeat this many times.
  4. Encrypt the cleartext using the resulting hash as the initialization vector and/or secret key.
  5. Save the salt and the resulting ciphertext.
Zach Scrivena
+1  A: 

If you don't need to decrypt the passphrase, but just generate an encryption key based on a password/passphrase, you can implement the PKCS#5 standard, using the JCE Cipher and MessageDigest classes.

Chochos
+2  A: 

Use RFC2898 to generate keys from passwords. This isn't included in the JRE or JCE, as far as I know, but it is included in J2EE Servers like JBoss, Oracle, and WebSphere. It is also included in the .NET Base Class Library (Rfc2898DeriveBytes).

There are some LGPL implementations in Java out there, but on a quick look this one looks a little over complicated. There is also a good javascript version. (I produced a modified version of that one and packaged it as a Windows Script Component)

Lacking a good implementation with an appropriate license, I packaged some code up from Mattias Gartner. This is the code in its entirety. Short, simple, easy to understand. It's licensed under the MS Public License.

// PBKDF2.java
// ------------------------------------------------------------------
//
// RFC2898 PBKDF2 in Java.  The RFC2898 defines a standard algorithm for
// deriving key bytes from a text password.  This is sometimes
// abbreviated "PBKDF2", for Password-based key derivation function #2.
//
// There's no RFC2898-compliant PBKDF2 function in the JRE, as far as I
// know, but it is available in many J2EE runtimes, including those from
// JBoss, IBM, and Oracle.
//
// It's fairly simple to implement, so here it is. 
// 
// Created Sun Aug 09 01:06:57 2009
//
// last saved: 
// Time-stamp: <2009-August-09 02:19:50>
// ------------------------------------------------------------------
//
// code thanks to Matthias Gartner
//
// ------------------------------------------------------------------

package cheeso.examples;


import java.security.NoSuchAlgorithmException;
import java.security.InvalidKeyException;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;


public class PBKDF2
{
    public static byte[] deriveKey( byte[] password, byte[] salt, int iterationCount, int dkLen )
        throws java.security.NoSuchAlgorithmException, java.security.InvalidKeyException
    {
        SecretKeySpec keyspec = new SecretKeySpec( password, "HmacSHA1" );
        Mac prf = Mac.getInstance( "HmacSHA1" );
        prf.init( keyspec );

        // Note: hLen, dkLen, l, r, T, F, etc. are horrible names for
        //       variables and functions in this day and age, but they
        //       reflect the terse symbols used in RFC 2898 to describe
        //       the PBKDF2 algorithm, which improves validation of the
        //       code vs. the RFC.
        //
        // dklen is expressed in bytes. (16 for a 128-bit key)

        int hLen = prf.getMacLength();   // 20 for SHA1
        int l = Math.max( dkLen, hLen); //  1 for 128bit (16-byte) keys
        int r = dkLen - (l-1)*hLen;      // 16 for 128bit (16-byte) keys
        byte T[] = new byte[l * hLen];
        int ti_offset = 0;
        for (int i = 1; i <= l; i++) {
            F( T, ti_offset, prf, salt, iterationCount, i );
            ti_offset += hLen;
        }

        if (r < hLen) {
            // Incomplete last block
            byte DK[] = new byte[dkLen];
            System.arraycopy(T, 0, DK, 0, dkLen);
            return DK;
        }
        return T;
    } 


    private static void F( byte[] dest, int offset, Mac prf, byte[] S, int c, int blockIndex ) {
        final int hLen = prf.getMacLength();
        byte U_r[] = new byte[ hLen ];
        // U0 = S || INT (i);
        byte U_i[] = new byte[S.length + 4];
        System.arraycopy( S, 0, U_i, 0, S.length );
        INT( U_i, S.length, blockIndex );
        for( int i = 0; i < c; i++ ) {
            U_i = prf.doFinal( U_i );
            xor( U_r, U_i );
        }

        System.arraycopy( U_r, 0, dest, offset, hLen );
    }

    private static void xor( byte[] dest, byte[] src ) {
        for( int i = 0; i < dest.length; i++ ) {
            dest[i] ^= src[i];
        }
    }

    private static void INT( byte[] dest, int offset, int i ) {
        dest[offset + 0] = (byte) (i / (256 * 256 * 256));
        dest[offset + 1] = (byte) (i / (256 * 256));
        dest[offset + 2] = (byte) (i / (256));
        dest[offset + 3] = (byte) (i);
    } 

    // ctor
    private PBKDF2 () {}

}
Cheeso