Signing Java Objects for Secure Transfer

In distributed J2EE applications or in any application where you need to transfer Java objects to another system then there is always a security risk where the object can be intercepted which can result in data theft/loss. Especially in Serialization, (where the object is a physical file in the native file system) when the serialized Java objects are sent through the network, whoever knows the type of the object can always read it.

In this article, we will build two simple applications, one which generates the object, the keys (public & private) and signs the object with the private key. Other application which verifies the signed object in other end over the network or another application in the same machine. Both these apps can run independently in different machines. For signing the object we will be using Public-Key cryptography. This is one of the most widely used standards to sign data along with DSA & SHA1PRNG (cryptographically strong pseudo-random number generator (PRNG)). Public-Key cryptography is a asymmetric key algorithm, where the key used to encrypt a message is not the same as the key used to decrypt it.

This is the class diagram of the applications which we will be building. This article will be divided into two parts, the first part we will sign the object (serialized) and in the second part, we will verify it.

Sign the Java Object

First of all we need a class which will generate a public and private key. We will create a class named SecurityUtil which will generate those based on DSA (we can use RSA or any other algorithm as long as its available) and we will generate a cryptographically strong pseudo-random number generator (PRNG) which can be clubbed along with DSA (SHA1PRNG). The strength of the key will be 1024.

protected KeyPair generateKey () throws Exception {
    KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("DSA");
    SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
    keyPairGen.initialize(1024,secureRandom);
    KeyPair keyPair = keyPairGen.generateKeyPair();
    return keyPair;
}

Next we will create a class named EmployeeValueObject which is nothing but a POJO with a HashMap getter/setter. This will be the object which we will be transferring over the network/application. Since we serialize the object before transferring, this class should implement Serializable.

public class EmployeeValueObject implements Serializable {
    HashMap employeeSalary = new HashMap();
    public void setSalary (HashMap employeeSalary){
        this.employeeSalary = employeeSalary;
    }

    public HashMap getSalary () {
        return employeeSalary;
    }
}

Now we have all the supporting classes which we need and let’s start building the main application. Let’s call this class EmployeeDetails and this will create an object for the POJO which we created in our previous step and populate with some data. In addition to that, we will sign the POJO object and then serialize to a file. In this example we will be also serializing the public key to transfer to the other end. Note: In production implementations, both these objects shouldn’t be sent at the same time. The application at the other end should already have the public key)

Let’s create the POJO and populate with some data in the HashMap.

EmployeeValueObject employeeVO = new EmployeeValueObject();
employeeVO.setSalary(populateData());

private static HashMap populateData (){
    HashMap employeeSalary = new HashMap ();
    employeeSalary.put("3", "Johns, Galvin D. --> $18,000");
    employeeSalary.put("4", "Weber, Murphy I. --> $5,000");

    return employeeSalary;
}

Now let’s generate the public and private keys from SecutityUtil and sign the POJO which we created in the above step.

KeyPair keyPair = new SecurityUtil().generateKey();
PrivateKey privateKey = keyPair.getPrivate();
PublicKey publicKey = keyPair.getPublic();

Signature digitalSignature = Signature.getInstance(privateKey.getAlgorithm());
SignedObject digitalSignedObj =
    new SignedObject(employeeVO, privateKey, digitalSignature);

Now digitalSignedObj is a digitally signed data with the private key which we generated. Now let’s serialize this object for the secure transfer.

ileOutputStream serializedFileOutput = new FileOutputStream("employee.ser");
ObjectOutputStream serializedObjOutput = new ObjectOutputStream(serializedFileOutput);
serializedObjOutput.writeObject(digitalSignedObj);
serializedObjOutput.close();
serializedFileOutput.close();

We will also serialize the public key so that for this example we can send both of them to another machine to verify. Note: In production implementations, both these objects shouldn’t be sent at the same time. The application at the other end should already have the public key)

serializedFileOutput = new FileOutputStream("publickey.ser");
serializedObjOutput = new ObjectOutputStream(serializedFileOutput);
serializedObjOutput.writeObject(publicKey);
serializedObjOutput.close();
serializedFileOutput.close();

This will complete the creation of application one. When you run this application, it will create two new files in the same directory. employee.ser – which is the signed and serialized POJO (Salary details) & publickey.ser – public key to verify the POJO. Now using the appropriate protocol send these files to the other application (remote or local) and let’s start building the verification part.

Verification & De-Serializing the Java Object

As a start we have the files employee.ser & publickey.ser. Let’s start building up the class to verify and de-serialize these files. Let’s name this class DecryptEmployee. The following code should de-serialize the objects.

FileInputStream serializedPublicKeyIn = new FileInputStream("publicKey.ser");
ObjectInputStream serializedPublicKey = new ObjectInputStream(serializedPublicKeyIn);
PublicKey publicKey = (PublicKey) serializedPublicKey.readObject();

FileInputStream serializedEmployeeIn = new FileInputStream("employee.ser");
ObjectInputStream serializedEmployee = new ObjectInputStream(serializedEmployeeIn);
SignedObject digitalSignedObj = (SignedObject) serializedEmployee.readObject();

Since the public key was not signed, publicKey variable will be readable. But the employee POJO was signed, so we are reading the object as a SignedObject. Let’s move forward and verify this.

Signature digitalSignature = Signature.getInstance(publicKey.getAlgorithm());
boolean decryptFlag = digitalSignedObj.verify(publicKey, digitalSignature);

The decryptFlag contains the status of the verification. If the public key is incorrect or if the object was tampered, then this will return false and we won’t be able to verify the object. If its true then everything looks good and we can successfully verify the POJO and print the values from HashMap.

if(decryptFlag) {
    EmployeeValueObject employeeVO = (EmployeeValueObject) digitalSignedObj.getObject();
    HashMap employeeSalary = (HashMap) employeeVO.getSalary();
    Collection collHashMap = employeeSalary.values();
    Iterator collectionIterator = collHashMap.iterator();
    while (collectionIterator.hasNext()) {
        System.out.println(collectionIterator.next());
    }
} else {
    System.out.println ("Decryption Failed. Please check the Keys.");
}

If you run this application, we will get an output similar to below.

This can be used in any sensitive application to make sure that the objects which are transferred over the network are safe.

UPDATE: SignedObject signs the object, but it doesn’t encrypt it. So if you need encryption, you can use the Cipher class in Java.

Recover Database Passwords from Weblogic Server

In this post, I am going to explain how to Decrypt or recover the passwords which are encrypted (or hashed) in Weblogic 8.1 especially the database passwords. If you ever forgot the database password which is already configured with Weblogic or the password for the user which is used to start Weblogic, this will be handy. The hashed passwords can be normally found in config.xml and boot.properties inside the application domain. The database passwords will be in config.xml under the JDBC configuration and will look something like below.

$ PasswordEncrypted="{3DES}bDcllidskanDsaIsnaiG=="

To recover the passwords, we need some prerequisites which are listed below.

  1. weblogic.jar & jsafeFIPS.jar from the Weblogic server. (You can find this under WL_HOME/server/lib directory)
  2. SerializedSystemIni.dat from the application domain (You can find this under the application domain root)
  3. The encrypted password from config.xml / boot.properties including {3DES}

SerializedSystemIni.dat has the key to recover the passwords so this is absolutely essential and it should be from the same server since the key depends on the machine. If you are not able to find this file, then probably the server administrator hasn’t given read access to this file to all the users. For Production systems that should be a practice, only the id which will be starting the weblogic server should have read permissions to the SerializedSystemIni.dat file. If not, this will be a security risk, its like leaving the key for your Home on the street. Coming back to the subject, if you got the file then you are all set for the next step.

Assuming you have all the files needed, let’s proceed to the next step. You can copy all the prerequisite files to your workstation or if needed you can run the utility from the server too. As long as the machines have JDK installed it should be fine.

To recover the passwords we will be using a utility from Apache Geronimo 2.0.1 API - Weblogic81Utils.java. Although this file is a part of a package, this can be run as a standalone class with little modification. All you need to do is to remove the package reference and add a main() method to the class. The code for main() method is below.

public static void main(String args[]) {
    try{
        String beaDir = "$Server/lib or the Directory which has the requried JAR files$";
        String appDir = "$App Domain or the Directory which has SerializedSystemIni.dat$";
        String hashedPassword = "{3DES}Vdsds76nGsfdsfKJbg54ss==";
        Weblogic81Utils weblogic81Utils = new Weblogic81Utils(beaDir, appDir);
        String plainTextPassword = weblogic81Utils.decryptString(hashedPassword);
        String configXML = weblogic81Utils.getConfigXML();
        Properties bootProperties = (Properties) weblogic81Utils.getBootProperties();
        System.out.println("-----------------------------------------------------");
        System.out.println("hashedPassword" + " == " + plainTextPassword);
        System.out.println("boot.properties" + " <username> "
            + bootProperties.getProperty("username"));
        System.out.println("boot.properties" + " <password> "
            + bootProperties.getProperty("password"));
        System.out.println("-----------------------------------------------------");
    } catch (Exception e) {
        throw (RuntimeException)new IllegalArgumentException("Unable to initialize"
            + "encryption routines from provided arguments").initCause(e);
    }
}

beaDir - If you are running this on the server, it will be the $WL_HOME/server/lib directory. If you are running elsewhere it will be the path which has weblogic.jar and jsafeFIPS.jar.

appDir - If you are running this on the server, it will be your application domain directory. If you are running elsewhere, it will be the path which contains SerializedSystemIni.dat, config.xml and boot.properties.

hashedPassword – In this example, I am not reading config.xml directly instead I give the encrypted password as a input. So this will be the encrypted database password from your config.xml.

Save the class file and we are all set to run the program. Make sure you removed the package reference since we are running this as a standalone program. No other change is required. Compile the class and run the class. I tested this using JDK 1.6, but any JDK higher than 1.4 should work.

Sample output is given below.

This was tested with Weblogic Server 8.1 and JDK 1.6.