<

Want to win a PS4? Go Premium and enter to win our High-Tech Treats giveaway. Enter to Win

x

Encrypt username and password for JNDI in Tomcat Server.xml

Published on
26,190 Points
19,990 Views
2 Endorsements
Last Modified:
Most of the developers using Tomcat find it easy to configure the datasource in Server.xml and use the JNDI name in the code to get the connection.  So the default connection pool using DBCP (or any other framework) is made available and the life goes easy using the connection pool.

But problems pop up when you use Tomcat as your production server. The most vulnerable part is the exposure of database username and password in clear text format. There are lots of chances for the data being corrupted using the login credentials. So the need of the hour is to safeguard the exposed username and password through some means.

And here goes the way to do it....

Credit: Full credit to Michael Remijan for this wonderful article on how to secure the password in Tomcat.  Please read it at:
     http://websphere.sys-con.com/node/393364

What I am discussing here is the other way of implementing it using the Cipher encryption based on a encryption key, just to make the life of some intruders a little bit harder.  Ok, let's get to the code straight away,

Here is the normal Datasource configuration in Server.xml,.  A you can see, username and password are exposed, which is vulnerable and totally unacceptable.

<Context path="/myProject" docBase="myProject" debug="1" reloadable="true">
 <Resource name="jdbc/test" auth="Container" type="javax.sql.DataSource"  driverClassName="oracle.jdbc.driver.OracleDriver" url="jdbc:oracle:thin:@server:1523:TEST"
 username="muralim" password="helloworld"  maxActive="4" maxIdle="2" maxWait="-1"
 logAbandoned="true"  removeAbandoned="true"  removeAbandonedTimeout="60"/>
 </Context>

Open in new window

So I use the  Cipher encryption to encrypt my username and password and replace the clear text of username and password as below:

<Context path="/myProject" docBase="myProject" debug="1" reloadable="true">
 <Resource name="jdbc/test" auth="Container" type="javax.sql.DataSource"  driverClassName="oracle.jdbc.driver.OracleDriver" url="jdbc:oracle:thin:@server:1523:TEST"
username="Ql6u3CAl988=" password="CgrtRRVz6A3aaGAfDpRRuQ=="  maxActive="4" maxIdle="2" maxWait="-1"
 logAbandoned="true"  removeAbandoned="true"  removeAbandonedTimeout="60"/>
 </Context>

Open in new window

I used a common key to encrypt my username and password and it's all encrypted now.  But how does Tomcat recognise that these credentials are encrypted and know to decrypted then when making the actual connection???...

So here we have the server.xml entry changed again:
<Context path="/myProject" docBase="myProject" debug="1" reloadable="true">

<Resource name="jdbc/test" auth="Container" type="javax.sql.DataSource"

factory="com.mycomp.util.CompEncryptedDataSourceFactory"

driverClassName="oracle.jdbc.driver.OracleDriver" url="jdbc:oracle:thin:@server:1523:TEST"
 username="Ql6u3CAl988=" password="CgrtRRVz6A3aaGAfDpRRuQ=="  maxActive="4" maxIdle="2" maxWait="-1"
 logAbandoned="true"  removeAbandoned="true"  removeAbandonedTimeout="60"/>
 </Context>

Open in new window

In the above code snippet, you might notice the attribute factory with some value of a class name.  By default DBCP factory is used to handle the connection resources and hence the attribute value is implicit. To read more on custom factories read on...

So what exactly is there in the CompEncryptedDataSourceFactory ???

Here goes the code, i have tweaked the sample from the REMIJAN article to achieve this,

import java.util.Enumeration;
import java.util.Hashtable;
import javax.naming.*;
import org.apache.commons.dbcp.BasicDataSourceFactory;

public class  CompEncryptedDataSourceFactory extends BasicDataSourceFactory{

// common key for encryption and decryption
private static final String ENC_KEY ="CompEncryptedDataSourceFactory";

public CompEncryptedDataSourceFactory()
{}

public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable environment)
throws Exception
{
if(obj instanceof Reference)
{
setUsername((Reference)obj);
setPassword((Reference)obj);
}
return super.getObjectInstance(obj, name, nameCtx, environment);
}

private void setUsername(Reference ref)
throws Exception
{
findDecryptAndReplace("username", ref);
}

private void setPassword(Reference ref)
throws Exception
{
findDecryptAndReplace("password", ref);
}

private void findDecryptAndReplace(String refType, Reference ref)
throws Exception
{
int idx = find(refType, ref);
String decrypted = decrypt(idx, ref);
replace(idx, refType, decrypted, ref);
}

private void replace(int idx, String refType, String newValue, Reference ref)
throws Exception
{
ref.remove(idx);
ref.add(idx, new StringRefAddr(refType, newValue));
}

private String decrypt(int idx, Reference ref)throws Exception
{
return new CipherEncrypter(ENC_KEY).decrypt(ref.get(idx).getContent().toString());
}

private int find(String addrType, Reference ref)
throws Exception
{
Enumeration enu = ref.getAll();
for(int i = 0; enu.hasMoreElements(); i++)
{
RefAddr addr = (RefAddr)enu.nextElement();
if(addr.getType().compareTo(addrType) == 0)
return i;
}

throw new Exception("The \"" + addrType + "\" name/value pair was not found" + " in the Reference object.  The reference Object is" + " " + ref.toString());
}
}

Open in new window

For now don't worry about the working of this class as it's not so important unless you are too keen :-) . As you might see, the class extends BasicDataSourceFactory from Commons/DBCP.

And here is the CipherEncrypter, -- I have no idea on the working of this class and i got this by googling.

[Editor's Note:  This, and similar source code is found, usually without attribution, at several Java coding sites, including: http://www.exampledepot.com/egs/javax.crypto/desstring.html ]
import java.io.UnsupportedEncodingException;
import java.security.spec.AlgorithmParameterSpec;

import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;

public class CipherEncrypter {
Cipher ecipher;
Cipher dcipher;

byte[] salt = {
(byte)0xA9, (byte)0x9B, (byte)0xC8, (byte)0x32,
(byte)0x56, (byte)0x35, (byte)0xE3, (byte)0x03
};

int iterationCount = 19;

CipherEncrypter(String passPhrase) {
try {
// Create the key
PBEKeySpec keySpec = new PBEKeySpec(passPhrase.toCharArray());
SecretKey key = SecretKeyFactory.getInstance("PBEWithMD5AndDES").generateSecret(keySpec);
ecipher = Cipher.getInstance(key.getAlgorithm());
dcipher = Cipher.getInstance(key.getAlgorithm());

// Prepare the parameter to the ciphers
AlgorithmParameterSpec paramSpec = new PBEParameterSpec(salt, iterationCount);

// Create the ciphers
ecipher.init(Cipher.ENCRYPT_MODE, key, paramSpec);
dcipher.init(Cipher.DECRYPT_MODE, key, paramSpec);
} catch (java.security.InvalidAlgorithmParameterException e) {
} catch (java.security.spec.InvalidKeySpecException e) {
} catch (javax.crypto.NoSuchPaddingException e) {
} catch (java.security.NoSuchAlgorithmException e) {
} catch (java.security.InvalidKeyException e) {
}
}

public String encrypt(String str) {
try {
byte[] utf8 = str.getBytes("UTF8");
byte[] enc = ecipher.doFinal(utf8);
return new sun.misc.BASE64Encoder().encode(enc);
} catch (javax.crypto.BadPaddingException e) {
} catch (IllegalBlockSizeException e) {
} catch (UnsupportedEncodingException e) {
} catch (Exception e) {
}
return null;
}

public String decrypt(String str) {
try {
// Decode base64 to get bytes
byte[] dec = new sun.misc.BASE64Decoder().decodeBuffer(str);

// Decrypt
byte[] utf8 = dcipher.doFinal(dec);

// Decode using utf-8
return new String(utf8, "UTF8");
} catch (javax.crypto.BadPaddingException e) {
} catch (IllegalBlockSizeException e) {
} catch (UnsupportedEncodingException e) {
} catch (java.io.IOException e) {
}
return null;
}

public static void main(String[] args)
{

try {
DesEncrypter encrypter = new DesEncrypter("CompEncryptedDataSourceFactory"); // key for encryption
String encrypted = encrypter.encrypt("helloworld");
System.out.println("encrypted :"+encrypted);

String decrypted = encrypter.decrypt(encrypted);
System.out.println("decrypted :"+decrypted);
} catch (Exception e) {

e.printStackTrace();
}

}
}

Open in new window

The above code includes a main method to get the encrypted text of the user name and password.

To set up configuration of jars to enable this in Tomcat 5.5 (it might vary with Tomcat versions):

1. Place commons-dbcp-1.2.1.jar,commons-pool-1.3.jar (use latest versions of these jars)  in
    common/lib.
2. Make a jar file of both classes CipherEncrypter.class and  CompEncryptedDataSourceFactory.class
    and place it in the common/lib.   Something like  >>
           jar cf CompEncryptedDataSourceFactory.jar *.class

So now we are ready to see the custom factory for encrypting the username and password.  Now write a normal JNDI lookup code and fetch your connection.

Here is the test code, and you have a connection at last with the encrypted username and password.
try{

InitialContext initialContext = new InitialContext();
DataSource dataSource = (DataSource)initialContext.lookup("java:comp/env/jdbc/test");

return dataSource.getConnection();
}catch(Exception e){
log.error("Exception in getInstance",e);
e.printStackTrace();
}

Open in new window

Hope this article makes a developer life easy in configuring a secured datasource.

Happy Coding...

-Murali*
2
Comment
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
0 Comments

Featured Post

VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

Join & Write a Comment

This tutorial will teach you the special effect of super speed similar to the fictional character Wally West aka "The Flash" After Shake : http://www.videocopilot.net/presets/after_shake/ All lightning effects with instructions : http://www.mediaf…
Visualize your data even better in Access queries. Given a date and a value, this lesson shows how to compare that value with the previous value, calculate the difference, and display a circle if the value is the same, an up triangle if it increased…
Other articles by this author
Suggested Courses

Keep in touch with Experts Exchange

Tech news and trends delivered to your inbox every month