devxlogo

Encrypt Sensitive Configuration Data with Java

Encrypt Sensitive Configuration Data with Java

hen application developers are developing, parameters are often hard-coded in the source code. These hard-coded parameters are often pulled out of the source code and put into property files or configuration files. System and network security policies may force a developer to address security concerns over the data that is stored in external files. So, how do you make sure that your sensitive external parameters are safe?

One way to solve this problem is to encrypt the data before it is written to the external file, then read and decrypt the data before using it in your application. The Java Cryptography Extension (JCE) provides an API that will let you do this.

Symmetric or “single key” encryption is a good choice for solving this type of problem. Single key encryption uses the same key to encrypt and decrypt data. DES and DESede are two single key encryption schemes. I will show you how to solve this problem using either of these encryption schemes with the JCE.

Author’s Note (added 7/19/04): Since writing this article I have received numerous e-mails asking how one would prevent somebody from decompiling the Encrypter class and taking the key. Several suggestions have been given to me. One is to obfuscate the class using an obfuscation utility. This may ward off the innocent/slightly curious hacker, but using clever debugging techniques, a motivated yet sinister cracker can still obtain the key. Another suggestion is to pull the encryption key out of the class and use file-system permissions to prevent access to the file. As I’m told, this is the same strategy that Unix OSs use for shadowed passwords.


You need some way to secure the parameters used by your source code when they are stored in external property or configuration files.


Encrypt the data before it goes in; decrypt it before your application uses it.

Encrypting a String
There are two main classes that I have created to solve this problem: StringEncrypter and EncryptionScheme. Figure 1 shows a UML representation of what is provided in the source code.

Figure 1. Stringing Along: This UML class diagram represents the contents of the StringEncrypter and EncryptionScheme classes.

The StringEncrypter class (Listing 1) provides methods that allow you to encrypt and decrypt a string using a given encryption scheme. The first constructor takes two parameters: an object of type EncryptionScheme and the encryption key that will be used to encrypt and decrypt. After you construct the StringEncrypter, you can encrypt and decrypt a given string using the eponymous methods, encrypt and decrypt.

I have provided a set of unit tests that demonstrate how the StringEncrypter works (see Listing 2).

EncryptionScheme is a Java interface that encapsulates the properties of different encryption schemes. A concrete class that implements the EncryptionScheme interface is passed into the StringEncrypter (this is known as the strategy design pattern). You can pass a custom key into the constructor, or you can use the one that is provided by default, DEFAULT_ENCRYPTION_KEY. If you use the single argument constructor, it will automatically use the default key.

The main calls in the encrypt and decrypt methods are the calls made to the Cipher class (see the next section). The Cipher.init() method takes an integer that represents the encryption mode (encrypt or decrypt) and the key as an argument. Static constants representing the encryption mode are defined on the Cipher class. The Cipher.doFinal() method performs an encryption or decryption, based on what mode was initialized.

The Java Cryptography Extension (JCE)
The EncryptionScheme object that is passed into the constructor of the StringEncrypter class is used to generate three class-level fields: a KeySpec, a SecretKeyFactory, and a Cipher. The encrypt and decrypt methods on the StringEncrypter also use the SecretKey class. These classes are part of the JCE, a framework that provides key generation and encryption, among other things. This package comes standard in J2SE v1.4 and is available as an optional package in Java 2 SDK, versions 1.2.x and 1.3.x.

Here is what the Java 1.4.1 API specification has to say about these classes:

  • KeySpec?”A (transparent) specification of the key material that constitutes a cryptographic key.”
  • SecretKeyFactory?”Key factories are used to convert keys (opaque cryptographic keys of type Key) into key specifications (transparent representations of the underlying key material), and vice versa. Secret key factories operate only on secret (symmetric) keys.”
  • Cipher?”This class provides the functionality of a cryptographic cipher for encryption and decryption. It forms the core of the JCE framework.”
  • SecretKey?”A secret (symmetric) key.”

There are two other classes that are used in the enrypt and decrypt methods. The classes are: BASE64Encoder and BASE64Decoder. These classes are not part of the JCE, but they are helpful for converting between byte arrays and strings. BASE64Encoder encodes a binary stream as a “Base64” format string. BASE64Decoder decodes a “Base64” format string into a binary stream

Using the StringEncrypter
Along with all the implementation classes shown in the source code download, I have provided a set of unit tests that demonstrate how the StringEncrypter works.

Testing Encryption

public void testEncryptsUsingDesEde() throws Exception{     String stringToEncrypt = "test";     String encryptionKey = "123456789012345678901234567890";	     EncryptionScheme encryptionScheme = DesEdeEncryptionScheme.INSTANCE;     StringEncrypter encrypter =           new StringEncrypter( encryptionScheme, encryptionKey );     String encryptedString = encrypter.encrypt( stringToEncrypt );     assertEquals( "Ni2Bih3nCUU=", encryptedString );}

This test shows how a client application would encrypt a string using the DESede encryption scheme. First, I specified a string that I wanted to encrypt?”test”?and an encryption key. For DESede encryption, the encryption key must be more than 24 characters long. I used a 30-character encryption key just to be on the safe side.

There is no instance-specific data in the DesEdeEncryptionScheme class, thus it is designed using the Singleton design pattern so that you don’t have to maintain multiple instances of the class in memory. The EncryptionScheme object is obtained through a static constant defined on the DesEdeEncryptionScheme class.

Next, a StringEncrypter is constructed using the EncryptionScheme and the key. At this point, obtaining an encrypted string is as simple as calling the encrypt method, and passing in the String you wish to encrypt. The test asserts that the value of the encrypted string is correct according to its previously determined value (a regression test).

Decrypting the string is just as simple. A StringEncrypter is constructed by passing in the encryption key and the EncryptionScheme object. The encrypted string is passed to the decrypt method, which returns the unencrypted string (“test”).

Testing Decryption

public void testDecryptsUsingDesEde() throws Exception{     String string = "Ni2Bih3nCUU=";     String encryptionKey = "123456789012345678901234567890";     EncryptionScheme encryptionScheme = DesEdeEncryptionScheme.INSTANCE;     StringEncrypter encrypter =           new StringEncrypter( encryptionScheme, encryptionKey );     String decryptedString = encrypter.decrypt( string );     assertEquals( "test", decryptedString );}

Supporting Other Encryption Schemes
The EncryptionScheme interface allows you to add support for different symmetric encryption schemes. An abstract class called BaseEncryptionScheme is provided, which provides basic functionality for concrete EncryptionScheme classes. This abstract class is an extension tool for adding your own encryption schemes.

The code provided with this solution includes EncryptionScheme implementations for the DES and the DESede encryption schemes. Either of these schemes should suffice for solving the problem outlined in this 10-minute solution. Other symmetric encryption schemes include: HmacSHA1, Blowfish, HmacMD5, and TripleDES.

Making Good Use of String Encryption
So when would you use this wonderful string encryption tool? There are many possible applications.

One possible application, mentioned at the beginning of this solution, is to use the StringEncrypter to encrypt and decrypt sensitive data in a properties file. For example, you may use the java.util.Properties class to read and write a database password from a properties file. To keep the password secure, you may write a separate program that prompts you to enter your password, and then stores it in its encrypted form in the properties file. This might be a simple GUI-based program or even one that runs from the command line. Your main application will then use the same encryption key to decrypt the password before using it to obtain a connection from the database.

Of course, you may store passwords and other sensitive data in places other than properties files. You can implement the same scenario described above using an XML file, an environment variable, a registry location, or even a datasource such as an LDAP server or a database as a storage location for your encrypted data.

Another application would be to encrypt data before and after it is sent across a network. For example, you can encrypt an e-mail message before it is sent and decrypt it after it is received. Or, you could use the StringEncrypter to provide encryption for all transactions in a client/server application, or all messages sent to a messaging queue.

devxblackblue

About Our Editorial Process

At DevX, we’re dedicated to tech entrepreneurship. Our team closely follows industry shifts, new products, AI breakthroughs, technology trends, and funding announcements. Articles undergo thorough editing to ensure accuracy and clarity, reflecting DevX’s style and supporting entrepreneurs in the tech sphere.

See our full editorial policy.

About Our Journalist