Encrypting passwords in Tomcat

Apache TomcatApache Tomcat is by far the most popular (open source) web server and Java servlet container. It has been around for a long time and – at the time of writing this post – has reached version 7.0.29. As Apache rightfully claims on its web site, Tomcat powers numerous large-scale, mission-critical web applications across a diverse range of industries and organizations. Therefore, one might imagine that such a widely used server would out-of-the-box already prove to be very secure .

However, alot of weaknesses in Apache Tomcat stem from incorrect or inappropriate configuration – as is the case not only for Tomcat but for the majority of software products, I would imagine. The OWASP organization has written up a nice document with a lot of best practices and recommendations on how to make Tomcat more secure than the default out-of-the-box installation.

The OWASP document rightfully states that best practices advice us never to store clear text passwords, but that in the case of the server.xml it is very difficult to avoid. In this post, I will try to look into ways to avoid storing clear text password in Tomcat’s files that hopefully will make it less difficult to avoid.

Encrypt the admin’s password

Tomcat comes with a nice little app called the Web Application Manager, which makes it easy to deploy a new war-file. To be able to use the application you have to add an account with the role of “manager-gui”. This is done by adding the following two lines to the conftomcat-users.xml file:

As you can clearly see, the password is stored in plain text, which is something we would always like to avoid, especially in a production environment. Also, in real life of course, you would never use admin as the username or password ;).

Fortunately, although it apparently isn’t widely known, Tomcat comes with a script that  allows us to encrypt passwords. This script is called digest.bat on Windows or digest.sh on Linux and can be found in the bin directory. With this we can specify the encryption algorithm that we want to use – here we’re using SHA-256 – and we enter the text we want to encrypt:

We can now replace the plain text password in the conftomcat-users.xml file by the AES digest.

But we’re still not quite there yet. We some how have to tell Tomcat that we’ve encrypted the password. So open the file confserver.xml in your editor and locate the following lines of code:

Add the attribute digest=”sha-256″ so you end up with the following:

Encrypting database passwords

It is well documented how we can configure a JDBC DataSource in Tomcat, and use it in a web application. Within our application we add a resource-ref to our web.xml, like so:

Next, we have to download and install the appropriate JDBC driver and copy it to Tomcats lib folder. Then we have to edit the confcontext.xml file and link our resource-ref to a data source by adding the following:

Finally, edit confserver.xml and add the following lines to the GlobalNamingResources (in this example we’re using Microsoft SQL Server):

That’s it. We’ve configured a DataSource for use in our applications. But, oh dear, as you can clearly see, the database credentials are stored in plain text. And this time we don’t have the aid of a digest.bat script.

Write your own DataSourceFactory

The only thing we can do now, is write our own DataSource that uses encrypted passwords. Of course, we’re not going to write a complete implementation ourselves. Instead, for this example, we are going to rely on the default connection pool that comes with Tomcat, called Tomcat JDBC Connection Pool, and we will create a class that will extend org.apache.tomcat.jdbc.pool.DataSourceFactory.

First of all, create a new Java project and add the tomcat-jdbc library to your classpath. If you’re using maven, simply add the following dependency:

Next, we have to write a class called Encryptor that can be used to encrypt and decrypt our passwords. For our example we are going to use the use the AES algorythm. I’m not going to explain the details about AES or encryption since there are already a lot of good articles on the web written by far more capable people than me.

So what we have here is nothing more than some code that can encrypt and decrypt text using the AES algorythm. What is important to note is that I’ve added a main method which will enable us later on to encrypt our password from the commandline in order to copy and paste it into our Tomcat configuration file.

Now what we’re actually most interested in, is the following class that we are going to write. So add a new class called EncryptedDataSourceFactory and have it extend org.apache.tomcat.jdbc.pool.DataSourceFactory:

Basically, the only thing that EncryptedDataSourceFactory does, is decrypt the database password. All the heavy lifting is left to org.apache.tomcat.jdbc.pool.DataSourceFactory.

The project is finished. So, export your project into an executable JAR file, e.g. encryptedDS.jar, and copy it to the Tomcat lib directory.

Encrypt your database password by executing the following:

Replace the password in server.xml with the encrypted one. And, finally we have to tell Tomcat to use our custom DataSource factory. To do so, add an attribute factory, specifying our fully qualified class, to the Resource element. So what we’ve should end up with in server.xml is the following:

So at last, with a little bit of effort, we’ve managed to encrypt our database password. And if you want to, you could even go one step further and also encrypt the other attributes
such as the username or url by adding a few more lines of code to the EncryptedDataSourceFactory.createDataSource() method.

49 Replies to “Encrypting passwords in Tomcat”

  1. I am trying to use the encryption key from an property in context.xml in the Tomcat conf folder.

    The DataSourceFactory I implemented does not seem to have access to look up the values in the comp/env JNDI tree. I am not quite certain if the JNDI has not been loaded by the time the DataSourceFactory is instantiated or it’s using the wrong ClassLoader or …

    It’s odd because I can write JAX-RS endpoints that can look up values in the comp/env JNDI tree with no problems.

  2. Does this works against java 8? our tomcat environment is on java 8.

    Thanks,
    Kanth

  3. Hi,
    If there is a connection break, validation query does not restore the connection without restarting.
    Any help, please ?
    Regards

  4. I was thinking its still not secure as any one who knows can use the following, And should able to get the password.

    Cipher cipher = Cipher.getInstance(ALGORITHM);
    cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
    byte[] original = cipher.doFinal(toByteArray(encryptedString));
    return new String(original);

    Any one please help me what I understood is right or wrong. If I am right then what could be other approach to secure fully.

  5. Hi i have used this implementation for Tomcat 7 but am facing the issue with my datasource connection: “Unexpected exception resolving reference
    java.sql.SQLException: oracle.jdbc.xa.client.OracleXADataSource cannot be cast to java.sql.Driver”

    Eg:

    Is this issue related to the creatDatasource method which takes XA as a parameter

  6. Nifty solution that works well – thank you! I have two suggestions for the implementation:

    1) Method Encryptor.generateKey() instantiates and inits a KeyGenerator (variable kgen), but doesn’t do anything with it. These two lines can be dropped.

    2) Method EncryptedDataSourceFactory.createDataSource() could be simplified as follows:

    @Override
    public DataSource createDataSource(Properties properties, Context context, boolean XA) throws Exception {
    String encryptedPassword=properties.getProperty(PROP_PASSWORD);
    properties.put(PROP_PASSWORD, encryptor.decrypt(encryptedPassword));
    return super.createDataSource(properties, context, XA);
    }

    This avoids copying code from the base class and should be more robust against changes in future versions of Tomcat.

  7. Please help with the issue .. Is there a possibility of connection leak in the code?

  8. I am getting a pool exhausted exception..
    Timeout: Pool empty. Unable to fetch a connection in 10 seconds, none available[size:8; busy:8; idle:0; lastwait:10000].

  9. org.apache.tomcat.jdbc.pool.DataSourceFactory parsePoolProperties
    WARNING: poolPreparedStatements is not a valid setting, it will have no effect.

    Anyone know why I would get this error while using the “poolPreparedStatements” and “maxOpenPreparedStatements” options?

  10. I’m using this in java 1.8 and tomcat 7. It works after I restart tomcat. However, I got exception everytime the next day. Any clue? Many thanks!

    org.springframework.security.authentication.AuthenticationServiceException: Could not open JPA EntityManager for transaction; nested exception is javax.persistence.PersistenceException: org.hibernate.TransactionException: JDBC begin transaction failed:
    at org.springframework.security.authentication.dao.DaoAuthenticationProvider.retrieveUser(DaoAuthenticationProvider.java:109)

  11. Shoubhik and Rober2D2 have asked the proper questions: now how to protect the password used to do the password-encryption? This article just adds code and moves the problem somewhere else. There is *no additional security for database connections* demonstrated in this post, only hiding the problem to pass a (naive) security audit.

  12. Many thanks. This is beautifully simple. It had bothered me for a while that the passwords were in clear text.

  13. Eclipse is complaining about &amp. “amp cannot be resolved to a variable”. Also “Syntax error on token “;”, * expected”. Lines 70 and 73 I’m on eclipse 4.4.2. Any ideas?

    1. In HTML, you cannot simply write &, because that is an escape character. So, in history, they invented the escape sequence for ampersands, which is: &. That is what the website is showing you unfortunately.

      Solution: Just replace “&” with “&” (without double quotes)
      Actual : if (((int) buf[i] & 0xff) < 0x10) {
      Solution : if (((int) buf[i] & 0xff) < 0x10) {

      1. Solution: Just replace “&amp” with “&” (without double quotes)

        Actual : if (((int) buf[i] & 0xff) < 0x10) {
        Solution : if (((int) buf[i] & 0xff) < 0x10) {

  14. I am using this code in java 1.8. The encrypted password is being returned with dashes. When I try to decrypt the parseInt call is blowing up. I know paresInt cannot hadle dashes. I suspect that the encryption should not be returning a string with dashes. I used sha-1 utility to encrypt the password and I get a different result.

  15. Hello Everyone,

    I am facing a issue while doing migration of Apache tomcat server from 5.5 to 7.0.61. Following error is coming while starting my application which refer tomcat 7 lib file at runtime.
    Logs :
    FATAL [localhost-startStop-1 ] (asourceConnectionProvider.java:55) – Could not find datasource: java:comp/env/jdbc/mobiliser/core
    javax.naming.NamingException: Could not load resource factory class [Root exception is java.lang.ClassNotFoundException: SecureDataSourceFactory]

    I am not able to find jar which includes SecureDataSourceFactory class. Please help me to resolve this error. All valuable inputs I required from everyone.

    Thanks in Advance 🙂

  16. Plz help me

    G:\tomcat7\bin>digest.bat -a sha-256 admin
    Exception in thread “main” java.lang.UnsupportedClassVersionError: Bad version n
    umber in .class file
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:620)
    at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:12
    4)
    at java.net.URLClassLoader.defineClass(URLClassLoader.java:260)
    at java.net.URLClassLoader.access$100(URLClassLoader.java:56)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:195)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:268)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:251)
    at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:319)

    1. Most likely, you’ve compiled your classes with a Java version that is newer than the version you’re using for execution.

  17. Hi Wim,
    Thanks for the article. Helps understand a key feature of tomcat. Just had one query if in a windows environment we have Tomcat 7.0 installed and the files digest.bat and digest.sh are missing, what can be done to implement above method to hash the passwords in tomcat-users.xml. i.e can we copy these files from another tomcat 7 bin foler, is any version match required. And in above scenario consider that these files are not present in Prod or Non-Prod environment. Thanks in advance.

    Regards,
    Vignesh

  18. Number format exception at given line

    Integer.parseInt(l_digit, 16);

    Please help me on this

  19. I’ve been looking for something like this for a while. Now I can encrypt DB password, but this only solves part of the problem.

    If an attacker gains access to context.xml, it will probably gain access to the code (or at lesat the .class files). I think that the only solution that will work is that the secret key is provided on startup by a user.

  20. Thanks for this great article. In the pool implementation, wouldn’t it be an option to just update the password from encrypted to decrypted, and pass on the heavy-lifting entirely to super class.
    String passwordEncrypted = properties.getProperty(“password”);
    String decrypted = encryptor.decrypt(passwordEncrypted);
    properties.setProperty(“password”, decrypted);
    DataSource d = super.createDataSource(properties, context, XA);
    log.info(“EncryptedDBConnectionFactory datasource created”);

    1. I think this of Rober2D2 is an important point: password is just obfuscated if the encryption key is accessible somehow and inside a class it is accessible. The option of a pin or a key inserted at startup is more secure and works good for desktop apps with a human user, however servers restart by themselves, is impossible to be there each time.

  21. Very useful article. Thaks Wim. I am able to encrypt my password but how to use this encrypted password for doing a database connection? Thanks in advance.

  22. one last question that what changes are required to compile this on JDK 1.6. Because in my environment tomcat is pointing to jdk 1.6, so I might get java version incompatible exception…
    thank you,

    1. Since you’ve already replaced the multi-catch, everything else should work fine with Java 1.6. Just make sure that the code is compiled with a JDK 1.6 compiler.

  23. Hi Wim,
    Thank you for the response. I have used 1.7, and able compile class, built jar. Generated encrypted key, and update data source config with encrypted key, and added factory name as you suggested in the article,
    factory=”nl.amc.adict.tomcat7.AESEncryptedDataSourceFactory”
    But I got below exception in my logs, seems I migght be giving a wrong fully qualified class name ? (I used same naming convention as it is in above)
    FINE: loadClass(java.io.PrintStream, false)
    DBConnectionManager::constructor [Mon Jan 13 03:38:25 EST 2014] Exception while retrieving a DataSource object
    javax.naming.NamingException: Could not load resource factory class [Root exception is java.lang.ClassNotFoundException: nl.amc.adict.tomcat7.AESEncryptedDataSourceFactory]
    at org.apache.naming.factory.ResourceFactory.getObjectInstance(ResourceFactory.java:84)
    at javax.naming.spi.NamingManager.getObjectInstance(NamingManager.java:321)
    at org.apache.naming.NamingContext.lookup(NamingContext.java:843)
    at org.apache.naming.NamingContext.lookup(NamingContext.java:154)

    Thanks,
    Kris

    1. Hi Kris,

      Seems you’ve found a typo in my blog (which I’ve now corrected). Seems I’ve used the wrong package and classname in the server.xml sampe. So, try replacing the line

      factory=”nl.amc.adict.tomcat7.AESEncryptedDataSourceFactory”

      inside the server.xml file by the following

      factory=”nl.wimvanhaaren.tomcat.secured.EncryptedDataSourceFactory”

      I hope this fixes your ClassNotFoundException.

      Regard,
      Wim

  24. hi Wim van Haaren,

    I created jar with these two classes, and able to create encrypted passwd. But in run time getting naming not found exception, basically due to class not found exception of new factory created (testing on Tomcat 7.0.42).

    Seems multi exception catch is not supported on jdk 1.6…, so I changed it in my code.
    can you pls help me here, am I missing anything here..appreciate it

    Thx,
    Kris

    1. Hi Kris,

      For multi-catch to work you indeed have to use Java 7.
      Reagarding the class not found exeception, if you’ve put your jar-file inside Tomcat’s lib folder, Tomcat should be able to resolve your classes.

      Regards,
      Wim

  25. What about key store password encryption. If you have any approach, please provide the same.
    e.g.

  26. Where are you saving the encryption key ?
    Are they also on the server?

    1. Everything happens inside the Encryptor class – using the AES algorithm. For this basic example I didn’t use external encryption keys nor a salt.
      If you want to you could extend the example so that it uses a salt and then you can chose yourself where to store it. This would be even more secure but you would have to ask yourself if it would be worth the extra effort (i.e., do you trust your ops guy? 😉 )

  27. This is what I have been looking for. Great. Thanks a lot.

  28. Super, goede blog! Hoe lang blog je al? je schrijft leuk.
    Het geheel van je site ziet er ook gelikt uit en ook je content in interessant!

  29. Hi,

    I tried your example with my web application in tomcat. After i add factory attribute to Resource in server.xml. It becomes not working and showed me Exception with could not find datasource. I did copy the jar file to tomcatlib.
    Do i miss out any step? Thank you

    1. i am facing the same issue , datasource missing and all , i used JNDI in my application .

      actually :
      i have a running spring application on tomcat, database configuration in tomcat > context.xml , now i created two classes -> encyptor and factory , now added factory attributr in context.xml but it is showing data source not found.

      when i start the tomcat :

      WARNING: Failed to register in JMX: javax.naming.NamingException: Could not load resource factory class [Root exception is java.lang.ClassNotFoundException: com.wellsfargo.omni.encryption.EncryptedDataSourceFactory]

      when i hit the database : i mean try to retrieve data from database :following error:
      org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘oracledataSource’: Invocation of init method failed; nested exception is javax.naming.NameNotFoundException: Name [jdbc/ABCdatabase] is not bound in this Context. Unable to find [jdbc].

Comments are closed.