The example is doing it wrong, as strings should not be used to store keys.
A secret key should consist of bytes that are unpredictable to an adversary. The most logical method to generate those is to use a random number generator, but you can also generate them from key establishment (Diffie-Hellman), using a key derivation function upon another key, ratchets and many other ways.
A somewhat dangerous method is to generate them from a password. For that you normally use a Password Based Key Derivation Function or PBKDF. Java has direct support for PBKDF2 which can be used for this.
So you could create a HMAC key in the following way:
Mac mac = Mac.getInstance("HMACSHA256");
SecureRandom rng = new SecureRandom();
// key size can be anything but should default to the hash / MAC output size for HMAC
byte[] hmacKeyData = new byte[mac.getMacLength()];
rng.nextBytes(hmacKeyData);
SecretKey hmacKey = new SecretKeySpec(hmacKeyData, "HMACSHA256");
Arrays.fill(hmacKeyData, (byte) 0x00);
However, the following code is shorter, probably more descriptive. It also allows hardware devices to be used later on to implement the Mac
, although that might be a bit out of your territory.
KeyGenerator kg = KeyGenerator.getInstance("HMACSHA256");
SecretKey hmacKey = kg.generateKey();
Finally, if you still want to use a password, then use PKBDF2 and don't forget to store the salt:
// you don't want to use a string, as you cannot delete strings in Java
char[] password = {'p', 'a', 's', 's' };
SecureRandom rng = new SecureRandom();
byte[] salt = new byte[128 / Byte.SIZE];
rng.nextBytes(salt);
int iterations = 1_000_000;
Mac mac = Mac.getInstance("HMACSHA256");
PBEKeySpec spec = new PBEKeySpec(password, salt, iterations, mac.getMacLength() * Byte.SIZE);
SecretKeyFactory pbkdf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
byte[] hmacKeyData = pbkdf.generateSecret(spec).getEncoded();
SecretKey hmacKey = new SecretKeySpec(hmacKeyData, "HMACSHA256");
// clean up secret material
Arrays.fill(password, (char) 0x0000);
spec.clearPassword();
Arrays.fill(hmacKeyData, (byte) 0x00);
As an attacker may have forever to try passwords if he has a MAC to compare the result with, it would be a very good idea to choose a very complex password though; this is why password based encryption generally is not a good idea.
Key
is a generic parent interface used for both SecretKey
, PublicKey
and PrivateKey
. It is used in many classes that represent crypto algorithms as they may be used with any kind of key. For instance Cipher
can be used for RSA but also for AES. So the implementation just checks at runtime if the correct key is given.
For Mac
it might as well have been SecretKey
as a Mac
is really always a symmetrical algorithm (an asymmetric form of a Mac
is called a Signature
after all). Just a HMAC key would not be enough though, as there are also Mac
algorithms based on block ciphers such as AES (thus requiring a SecretKey
with algorithm "AES"
).
For convenience, SecretKeySpec
also implements SecretKey
; that way you don't need the SecretKeyFactory
to create a SecretKey
. The Java designers kind of forgot about hardware support that does require such as factory, but here we are.