This blog is mainly about Java...

Friday, February 13, 2009

Hash user password in Seam 2.1.1 manually without using IdentityManager

In the Seam Documentation, they give a minimal schema example of how you can create Users and Roles here: http://docs.jboss.com/seam/2.1.1.GA/reference/en-US/html_single/#d0e9178

As it is explained in the documentation, you can annotate your password with

@UserPassword(hash = "md5")
public String getPasswordHash() { return passwordHash; }
public void setPasswordHash(String passwordHash) { this.passwordHash = passwordHash; }
You could also use
hash= "none"
which doesnt hash the password which is what I did at first. The reason why I didnt start using md5 at once is because you needed a default username and password to be able to log into your seam application. And since I was using an import.sql script that created a default user for me, I had no way of knowing how to hash the password.

So what I needed to do is manually somehow hash a password which I could put in my import.sql script, so that I could enable hashing and correctly log in to my seam application. However this was easier said then done.
I tried changing the @UserPassword(hash="md5") and try to log in, but for obvious reason the login failed, because it was expecting a hashed password.

So then I got the idea to call a method that doesn't require login, which creates a user for us, that is called from a button in the login page. However when I persisted the user, the password was saved as clear text. This baffled me, because I thought that seam would handle that automatically. But if you actually look at the user entity and the setHashPassword method, it is not annotated, and it only sets a string. So it was obvious it only persisted the password as string.
In Seam you can also use IdentityManager to create a user for you. This will perform the hashing for you. So I tried to call
identityManager.createUser("username","password","firstname","lastname");

However, identityManager requires that you are correctly Authenticated, and it threw an exception
org.jboss.seam.security.AuthorizationException: Authorization check failed for permission seam.user,create

So apparently IdentityManager requires the correct permissions, which must be set in your drools setting in security.drl, but I didn't want to do that, since that is not how I wanted to configure my seam application.

So what I did, was look in the seam source and IdentityManager class and find the exact code that performs the hashing for us, and do that manually. And this worked great. Here is the source.

public String saveProcessUser() {
// Check if a new password has been entered
if (currentUser.getPasswordHash() != null && !"".equals(currentUser.getPasswordHash())) {
if (!currentUser.getPasswordHash().equals(passwordVerify)) {
StatusMessages.instance().addFromResourceBundle("admin.wrongPassword");
return "failure";
}
}

// If passwordVerify is not empty, it generally means its a new password
if (!passwordVerify.equals("")) {
String hashPass = generatePasswordHash(currentUser.getPasswordHash(), currentUser.getUsername());
log.debug("Setting new hash password: " + hashPass);
currentUser.setPasswordHash(hashPass);
}

if (getCurrentUser().getId() != null) {
entityManager.flush();
} else {
entityManager.persist(currentUser);
}

FacesMessages.instance().addToControlFromResourceBundle("successMessage", "admin.user.saved");
return "success";
}

/**
* This method will generate a hash password
*
* @param password - The password in cleartext
* @param salt - The username is used as salt
* @return - hash password based on password and username
*/
private String generatePasswordHash(String password, String salt) {
AnnotatedBeanProperty<UserPassword> userPasswordProperty = new AnnotatedBeanProperty<UserPassword>(ProcessUser.class, UserPassword.class);
// Will get the hash value from annotation UserPassword in ProcessUser.class
String algorithm = userPasswordProperty.getAnnotation().hash();
return PasswordHash.instance().generateSaltedHash(password, salt, algorithm);
}


Note that the method saveProcessUser() is called from the user administration form, and the currentUser object is my User entity. Seam will inject all the methods for us, so we dont have to do that. So we check if the verifyPassword is set and correct and then we hash the password. If the @UserPassword is set to something other than none, then it will correctly save a user and you can finally copy the hashed password in your import.sql script.

PS: You first should have the @UserPassword(hash="none") and then go to your user registration and then just print out what the password is, and then copy that to your import.sql script. When the authentication correctly works with either md5 or sha, then you can safely add the user to the database.

5 comments:

nicolai said...

Hi,

Just some security comments:

- Please use the salt properly: It should be random. If you use the username there might as well not be a salt. A random salt makes the password harder to brute-force crack.

- Please avoid using md5 it is known to be weak. sha is better and is currently preferred (even if it's only better by a cats whisker).

- If seam uses the password hashes in a standard manner you can create a hashed password with the help of htpasswd:

$ htpasswd -nbs janl floppa
janl:{SHA}kUgwB6n2vjvNLjg5EfvjBqlNlpY=

The generated password can then be inserted into the database using the SQL-interface I presume.

Regards,
Nicolai

Unknown said...

Thanks for the comment Nicolai.

I agree with your comment, you should definetly have a random salt and even iterator to make it much more complex. A very easy way you can integrate a strong and quite easy encryption by using annotations is using Jasyp, which I have blogged about: http://shervinasgari.blogspot.com/2008/08/using-encryption-jasypt-in-seam-20-and.html

Your option would need the modification of the way seam checks its login and the override of getHashPassword() and @UserPassword annotation to work. In case people want to have a go :-)

Unknown said...

I obviously mean Jasypt, not Jasyp :-)

Ebu_Elyas said...

How can i save a hashed password in Seam 3 ?

Unknown said...

I have not used Seam 3. Try to ask in the seam 3 forums

Labels