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

Reply via email to