P.S. I want to also contribute the random-number generation logic
back to the project as well, I'm just not sure where to put it yet
(gotta figure this out on the dev list).
Here is how I do it at the moment: I have a UserService with the
following (simplified for clarity) create(User) method:
public long create(User user) {
if (user == null) {
throw new NullPointerException("User instance cannot be null.");
} else if (userDao.findByUsername(user.getUsername()) != null) {
throw new ExistingUserException("A user with username " +
user.getUsername() + " already exists");
}
DateTime now = new DateTime();
user.setCreated(now);
user.setUpdated(now);
//the password saved to the database is actually a password hash -
not the plain text sent from the UI
//so, convert the plain text one to a hashed one:
ByteSource generatedSalt = generatePasswordSalt();
String base64PasswordSalt = generatedSalt.toBase64();
user.setPassword(new Sha512Hash(user.getPassword(),
generatedSalt.getBytes(),
PASSWORD_NUM_ITERATIONS).toBase64());
//store the password salt along side the password
user.setPasswordSalt(base64PasswordSalt);
return (Long) userDao.create(user);
}
As you can see, I store the salt in its Base64-encoded string form.
You could also save it as the raw byte array (blob).
This method calls the generatePasswordSalt() method:
private ByteSource generatePasswordSalt() {
byte[] saltBytes = new byte[PASSWORD_SALT_NUM_BYTES];
try {
SecureRandom.getInstance(RANDOM_NUM_GENERATOR_ALGORITHM_NAME).nextBytes(saltBytes);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
return new SimpleByteSource(saltBytes);
}
Where in my case, PASSWORD_SALT_NUM_BYTES = 128 and
RANDOM_NUM_GENERATOR_ALGORITHM_NAME = "SHA1PRNG"
My Realm implementation returns the SaltedAuthenticationInfo instance
where the getSalt() method returns the value of
Base64.decode(user.getPasswordSalt()) - to convert the base-64 encoded
stored string back into the original bytes so they can be used
directly by the CredentialsMatcher. If you stored the byte array/blob
directly, you wouldn't need to perform this step.
This salting technique is the most secure that I know of since it
doesn't rely on any user attribute - i.e. it is totally random and not
guessable by an attacker. IMO, it should definitely find its way into
Shiro so our end-users don't need to repeat this logic themselves. To
be discussed on the dev list...
Cheers,
Les