Encrypting Passwords in Tomcat using Servlets

By: Sam Chen  

In Tomcat, it’s easy to encrypt passwords by adding the digest attribute to a realm definition. The value must be one of the digest algorithms supported by the java.security.MessageDigest class (SHA, MD2, or MD5). To expand on the earlier example, the XML snippet shown next adds SHA encrypting to a file-based realm:

<Realm className="org.apache.catalina.realm.UserDatabaseRealm" debug="0" resourceName="UserDatabase" digest="SHA" />

To log into the application, you now need to encrypt the password stored in tomcat-users.xml to its encrypted form. You can do this by executing the following command: 

java org.apache.catalina.realm.RealmBase -a SHA {cleartext-password}

where catalina.jar is in the CLASSPATH. Now, copy and paste this new password into the %TOMCAT_HOME%\conf\tomcat-users.xml file. The problem with this method of password encryption is that it might not be portable. Let’s take a look at programmatic encryption. You’ll need to add encrypt.password=true to the build.properties file, pass it in from the command line with ant –Dencrypt.password=true, or edit the default setting in app-settings.xml. If you’re using the binary version (.war file) of this application, simply edit the following <init-param> of the LoginServlet (in the web.xml file):

<init-param>
<param-name>encrypt-password</param-name>
<param-value>true</param-value>
</init-param>

Next, you need to actually encrypt the password within your servlet. To do this, create an encodePassword(String password, String algorithm) method in a StringUtil.java class. This method uses the MessageDigest class from JSSE to encrypt a string:

import java.security.MessageDigest;
public static String encodePassword(String password, String algorithm) {
byte[] unencodedPassword = password.getBytes();
MessageDigest md = null;
try {
// first create an instance, given the provider
md = MessageDigest.getInstance(algorithm);
} catch (Exception e) {
log.error("Exception: " + e);
return password;
}
md.reset();
// call the update method one or more times
// (useful when you don't know the size of your data, e.g. stream)
md.update(unencodedPassword);
// now calculate the hash
byte[] encodedPassword = md.digest();
StringBuffer buf = new StringBuffer();
for (int i = 0; i < encodedPassword.length; i++) {
if (((int) encodedPassword[i] & 0xff) < 0x10) {
buf.append("0");
}
buf.append(Long.toString((int) encodedPassword[i] & 0xff, 16));
}
return buf.toString();
}

This method encrypts a string based on the algorithm you pass in. This algorithm is defined in LoginServlet and configurable when building via the ${encrypt-algorithm} variable. The default setting is SHA.

If you’re using password encryption and also have a retrieve password feature, you’ll probably want to add a password_hint column in your user store. It’s hard enough to remember all the passwords you keep, and it’s annoying when you have to create a new password, so the “send me a hint” tactic is useful.




Archived Comments


Most Viewed Articles (in JSP )

Latest Articles (in JSP)

Comment on this tutorial