Channels ▼
RSS

Design

Encrypted Preferences in Java


The Preferences API is now included in the core Java release, as of Version 1.4. It provides a simple, fully cross-platform mechanism for storing small amounts of data, and uses a simple hierarchical "name/value" structure for organizing data. It is intended to be used for configuration and preference data.

Preference data is stored differently on each platform. In fact, is entirely up to each Java implementation how it will store the actual data that is, what backing store it will use. In general, the backing store is not intended to be secure. For example, it may be implemented on top of the Registry, or some other storage facility that does not provide a way to hide sensitive data.

This article considers the technique of automatically encrypting data before storing it in the preferences database. This permits applications to use the Preferences API even for sensitive data, such as passwords and personal information.

What You'll Learn

This article is not intended to provide a tutorial on encryption. It is assumed that you already understand how encryption in Java works, or that you are willing to learn about it elsewhere.

This article focuses on integrating encryption techniques with the Preferences API. We won't focus on the many encryption algorithm options — we'll use a simple DES key to perform encryption and decryption, with the understanding that you may well want to replace this approach with another Java-based encryption method.

The most important aspect of this technique is making the encryption transparent. We want the encryption to happen behind-the-scenes, with as little intervention as possible. As you'll see, we'll be creating an EncryptedPreferences object, which acts just like a regular Preferences object except that it transparently takes care of encryption for us.

If you haven't ever used the Preferences API, don't worry. You'll pick up what you need to know along the way.

A Simple Test Program

Before we get into the details of how it all works, let's take a look at a simple test program. This program (pkg.Test) stores a couple of values in the preferences database.

Preferences root =
  Preferences.userNodeForPackage( Test.class );

root.put( "not", "encrypted" );

Preferences subnode = root.node( "subnode" );
subnode.put( "also not", "encrypted" );

root.exportSubtree( System.out );

You can find the full source to pkg.Test in Listing One.

The first two lines acquire a Preferences object for this program, "Test.class." Or rather, for the package it's contained in, "pkg." Remember, each package gets its own private area within the preferences database. The userNodeForPackage() method gets the Preferences object for our private area. This is the root node of the area in which we will store data.

Listing One: A simple test program. It stores a value in the preferences database in the root node for its package ("pkg"), and another value in a subnode of the root node.
// $Id$

package pkg;

import java.util.prefs.*;

public class Test
{
  static public void main( String args[] ) throws Exception {
    Preferences root = Preferences.userNodeForPackage( Test.class );

    root.put( "not", "encrypted" );

    Preferences subnode = root.node( "subnode" );
    subnode.put( "also not", "encrypted" );

    root.exportSubtree( System.out );
  }
}

The next line stores a value— or rather, a key/value pair. The key is "not," and the value is "encrypted." Later on, you can ask for the value corresponding to the key "not," and you'll get back the value "encrypted."

The next two lines create a subnode of our main node. Into this subnode, we put another key/value pair. The key is "also not," and the value is "encrypted."

Finally, we take a look at what we've done by exporting the entire database— that is, the entire database for our program. While the backing store might store data in any format, the exported data always uses the same format, which you can see in Listing Two.

Listing Two: The preference data for our sample program pkg.Test, exported in XML format.
<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE preferences SYSTEM 'http://java.sun.com/dtd/preferences.dtd'>

<preferences EXTERNAL_XML_VERSION="1.0">
  <root type="user">
    <map />
    <node name="pkg">
      <map>
        <entry key="not" value="encrypted" />
      </map>
      <node name="encrypted">
        <map>
          <entry key="mjdajaddcioehjeljmahmpdbfhomifhp" value="hknhbmkdphkpbjipdijphpniboiecadn" />
        </map>
        <node name="subnode">
          <map>
            <entry key="eempdaneimckpiod" value="bkoaejfbcjpkckmflkijoomngbopblco" />
          </map>
        </node>
      </node>
      <node name="subnode">
        <map>
          <entry key="also not" value="encrypted" />
        </map>
      </node>
    </node>
  </root>
</preferences>

If the data is being stored in the Registry, you can see it by using regedit. In my system, the preferences data is stored in \HKEY_CURRENT_USER\Software\ JavaSoft\Prefs\pkg, as you can see in Figure 1.

Trying It with Encryption

Figure 1: The results of running pkg.Test

Using encrypted preferences is easy. Here's the encrypted version, pkg.encrypted.EncryptedTest, which does the same thing as pkg.Test, except that it uses encryption:

Preferences root =
  EncryptedPreferences.userNodeForPackage(
    EncryptedTest.class, secretKey );

root.put( "transparent", "encryption" );

Preferences subnode = root.node( "subnode" );
subnode.put( "also", "encrypted" );

root.exportSubtree( System.out );

You can find the full source to pkg.encrypted.EncryptedTest in Listing Three.

Listing Three: A simple test program, this time using encryption. It does more or less the same thing as the program in Listing One, except that this variant uses an Encrypted Preferences object, which transparently encrypts the data before storing it, and decrypts it before retrieving it.
// $Id$

package pkg.encrypted;

import java.security.*;
import java.util.prefs.*;
import javax.crypto.*;
import javax.crypto.spec.*;
import ep.*;
import pkg.Util;

public class EncryptedTest
{
  static private final String algorithm = "DES";

  static public void main( String args[] ) throws Exception {
    byte rawKey[] = Util.readFile( "key" );
    DESKeySpec dks = new DESKeySpec( rawKey );
    SecretKeyFactory keyFactory = SecretKeyFactory.getInstance( algorithm );
    SecretKey secretKey = keyFactory.generateSecret( dks );

    Preferences root =
      EncryptedPreferences.userNodeForPackage(
        EncryptedTest.class, secretKey );

    root.put( "transparent", "encryption" );

    Preferences subnode = root.node( "subnode" );
    subnode.put( "also", "encrypted" );

    root.exportSubtree( System.out );
  }
}

The most important thing to see here is that instead of using the Preferences.userNodeForPackage() method, we're using the EncryptedPreferences.userNodeForPackage() method. And this method returns an EncryptedPreferences, rather than a regular Preferences object.


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 

Video