Why is a SecretKeySpec needed when deriving a key from a password in Java?

Riley Willow picture Riley Willow · Nov 21, 2014 · Viewed 34k times · Source

What is difference between SecretKey vs SecretKeySpec classes in Java?

The documentation of SecretKeySpec says:

it can be used to construct a SecretKey from a byte array

In this code, if I print secretKey.getEncoded() or secret.getEncoded(), in hex then both give the same output. So why do we need the SecretKeySpec?

final String password = "test";
int pswdIterations = 65536  ;
int keySize = 256;
byte[] ivBytes;
byte[] saltBytes = {0,1,2,3,4,5,6};

SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");

PBEKeySpec spec = new PBEKeySpec(
                    password.toCharArray(), 
                    saltBytes, 
                    pswdIterations, 
                    keySize
                    );

SecretKey secretKey = factory.generateSecret(spec);

SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(),"AES");

Here is the output of both calls to getEncoded():

00367171843C185C043DDFB90AA97677F11D02B629DEAFC04F935419D832E697

Answer

Duncan Jones picture Duncan Jones · Nov 21, 2014

Every SecretKey has an associated algorithm name. You cannot use a SecretKey with algorithm "DES" in a context where an AES key is needed, for example.

In your code, the following line produces a SecretKey:

SecretKey secretKey = factory.generateSecret(spec);

However, at this point the key is not an AES key. If you were to call secretKey.getAlgorithm(), the result is "PBKDF2WithHmacSHA1". You need some way of telling Java that this is actually an AES key.

The easiest way to do this is to construct a new SecretKeySpec object, using the original key data and explicitly specifying the algorithm name:

SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(),"AES");

Note: I would personally declare secret as a SecretKey, since I don't think you'll need to care about the concrete implementation after this.