Version 2

    If you are reading this article, it is likely that you are configuring JBoss to use SSL and you don't like specifying the -Djavax.net.ssl.keyStorePassword and/or -Djavax.net.ssl.trustStorePassword in a startup script in clear text.  Or maybe you've worked around that, but you don't want people to be able to look at the process table and see the -D system properties including your keystore passwords in clear text.

     

    This wiki will demonstrate a mechanism to list encoded system properties in a configuration file and then deploy a small SAR (service archive) on JBoss EAP 4.3 that will read the system properties from the config file, decode them, and set them as system properties.  Normally, this would be used for the javax.net.ssl.keyStorePassword and javax.net.ssl.trustStorePassword properties, but it can be used for any system properties that you don't want stored on the file system in clear text or that we don't want displayed as process arguments when listing running processes with a command like "ps".

     

    We will perform these steps:

     

    1.)  Create the properties file with encrypted property values

     

    2.)  Deploy the SAR that will read the properties file, decode the values, and set them as system properties.

     

    3.)  [optional] Deploy a simple WAR that will let us view all system properties to prove that our properties were set correctly.

     

    Here we go...

     

    1.)  Create the properties file with encrypted property values

     

       A.)  Download the attached EncryptSystemPropertiesService.sar archive and run the command shown below to get encrypted values of your system properties.

    #>java -cp $JBOSS_HOME/server/$CONFIG/lib/log4j.jar:EncryptSystemPropertiesService.sar org.jboss.example.EncryptSystemPropertiesService myPropertyValue

     

    Encrypted value: -5574105e85430e76b96d306ac6a8c0cd

     

       B.)  Create the following file with all your system properties:  $JBOSS_HOME/server/$CONFIG/conf/encrypted-properties.properties

    javax.net.ssl.keyStore=-518827a7c6c89d2f81ef3d2069227020
    javax.net.ssl.keyStorePassword=-518827a7c6c89d2fdaea6dc29af23db386121161fb6881cc
    another.system.property=-2aaa02a8274074e712bec0174403613a

     

    2.)  Now, we need to deploy the EncryptSystemPropertiesService.sar archive.  This can be done before or after the JBoss EAP 4.3 server is started.

    cp EncryptSystemPropertiesService.sar $JBOSS_HOME/server/$CONFIG/deploy

     

    This should produce the following in the log file (or to the server console):

    16:54:43,101 INFO  [EncryptSystemPropertiesService] Starting EncryptSystemPropertiesService
    16:54:43,101 INFO  [EncryptSystemPropertiesService] Loading system properties from: encrypted-properties.properties

     

    3.)  [optional]  Lastly, we can deploy the attached TestSystemProperties.war.

    cp TestSystemProperties.war $JBOSS_HOME/server/$CONFIG/deploy

     

    Now, access this link in your browser:  http://localhost:8080/TestSystemProperties/SystemProperties.jsp

     

    This should show a printout of all the system properties and you should see the properties that were specified in encrypted-properties.properties with the unencrypted values.

     

    system_properties.png

     

    Congratulations!  You have now specified the key store password and it is not stored unencoded on the filesystem or in the process parameters.

     

    So, let's dig a little bit into the details...

     

    There are really only two files of importance in this example:  encrypted-properties.properties and EncryptSystemPropertiesService.sar

     

    The first is pretty self explanatory.  The second is a simple JBoss service archive.  If you look in the SAR (just a zip file), you'll see that it's contents are:

    [apestel@localhost deploy]$ jar tvf EncryptSystemPropertiesService.sar
        25 Tue Mar 30 16:52:16 EDT 2010 META-INF/MANIFEST.MF
      4540 Tue Mar 30 16:32:30 EDT 2010 org/jboss/example/EncryptSystemPropertiesService.class
      3202 Tue Mar 30 16:32:30 EDT 2010 org/jboss/example/EncryptSystemPropertiesService.java
       347 Tue Mar 30 16:03:20 EDT 2010 org/jboss/example/EncryptSystemPropertiesServiceMBean.class
       236 Tue Mar 30 15:52:02 EDT 2010 org/jboss/example/EncryptSystemPropertiesServiceMBean.java
       685 Tue Mar 30 16:51:56 EDT 2010 META-INF/jboss-service.xml
    [apestel@localhost deploy]$

     

    The jboss-service.xml file is the metadata for this service.  Basically all it has is the filename that contains the system properties we want to load.

    <?xml version="1.0" encoding="UTF-8"?>
    <server>
        <mbean code="org.jboss.example.EncryptSystemPropertiesService"
               name="EncryptSystemProperties:service=EncryptSystemProperties">
              
            <!--
                Current service implementation will look for this file in
                $JBOSS_HOME/server/<CONFIG>/conf/encrypted_properties.properties
             -->  
            <attribute name="EncryptedSystemPropertiesFilename">encrypted-properties.properties</attribute>
           
            <!--
                Can add depends tag if we need this service to get started
                before some other service.
               
                <depends>jboss.web:service=WebServer</depends>
            -->
        </mbean>
    </server>

     

    Of the Java files in the SAR, the *ServiceMBean.java class is just an interface class and the *Service.java class is the real meat of our service as shown below:

    package org.jboss.example;

    import java.io.File;
    import java.io.FileInputStream;
    import java.io.InputStream;
    import java.math.BigInteger;
    import java.net.URL;
    import java.util.Enumeration;
    import java.util.Properties;

    import javax.crypto.Cipher;
    import javax.crypto.spec.SecretKeySpec;

    import org.apache.log4j.Logger;

    public class EncryptSystemPropertiesService implements
            EncryptSystemPropertiesServiceMBean {

        private Logger log = Logger.getLogger(this.getClass());
        private String encryptedSystemPropertiesFilename;
       
        public static void main(String[] args) throws Exception {
            if(args.length < 1) {
                System.err.println("Usage:  java org.jboss.example.EncryptSystemPropertiesService [string to encode]");
                System.exit(0);
            }
            System.out.println("Encrypted value: " + encode(args[0]));
        }

        public void start() {
            log.info("Starting EncryptSystemPropertiesService");
            loadSystemProperties();
        }

        public void stop() {
            log.info("Stopping EncryptSystemPropertiesService");
        }

        public String getEncryptedSystemPropertiesFilename() {
            return encryptedSystemPropertiesFilename;
        }

        public void setEncryptedSystemPropertiesFilename(String filename) {
            encryptedSystemPropertiesFilename = filename;

        }

        private void loadSystemProperties() {
            log.info("Loading system properties from: " + encryptedSystemPropertiesFilename);
           
            File file = null;
           
            try {
                // This will look for the filename in the
                // $JBOSS_HOME/server/<CONFIG>/conf/ directory
                URL url = this.getClass().getClassLoader().getResource(encryptedSystemPropertiesFilename);

                // Load the properties
                file = new File(url.getPath());
                Properties properties = new Properties();
                InputStream is = new FileInputStream(file);
                properties.load(is);

                // Loop though the properties in the file and decrypt the values
                for (Enumeration e = properties.propertyNames(); e
                        .hasMoreElements();) {
                    String key = (String) e.nextElement();
                    String encryptedValue = properties.getProperty(key);
                    String value = decode(encryptedValue);

                    // Set the decrypted value in the System properties
                    System.setProperty(key, value);
                }
            } catch (Exception e) {
                if(file == null) {
                    log.error("Failed to loadSystemProperties for: " + encryptedSystemPropertiesFilename, e);
                } else {
                    log.error("Failed to loadSystemProperties for: " + file.getAbsolutePath(), e);
                }
            }
        }

        private static String decode(String secret) throws Exception {
            byte[] kbytes = "jaas is the way".getBytes();
            SecretKeySpec key = new SecretKeySpec(kbytes, "Blowfish");
            BigInteger n = new BigInteger(secret, 16);
            byte[] encoding = n.toByteArray();
            Cipher cipher = Cipher.getInstance("Blowfish");
            cipher.init(Cipher.DECRYPT_MODE, key);
            byte[] decode = cipher.doFinal(encoding);
            return new String(decode);
        }

        private static String encode(String secret) throws Exception {
            byte[] kbytes = "jaas is the way".getBytes();
            SecretKeySpec key = new SecretKeySpec(kbytes, "Blowfish");
            Cipher cipher = Cipher.getInstance("Blowfish");
            cipher.init(Cipher.ENCRYPT_MODE, key);
            byte[] encoding = cipher.doFinal(secret.getBytes());
            BigInteger n = new BigInteger(encoding);
            return n.toString(16);
        }
    }

     

    The getters and setters are how we get configuration parameters from the jboss-service.xml to the Java service class.  The main() method only exists as a convenient way of getting the encoded value of the system properties to be set.  The start() and stop() methods are the lifecycle methods for the JBoss service.

     

    You'll notice that it is in the start() method where the service loads the system properties from the properties file, decodes them, and sets them as system properties.

     

    That's really all there is to it.  Here are a couple things you might want to consider:

     

    1.)  You could specify the system properties in the jboss-service.xml file itself if desired so that you only had to have one file to manage.

     

    2.)  If you wanted to be extra secure, you could put the two files in the JBoss server directories when starting the server and then remove them from the filesystem once the server is running such that there would be no trace of even the encoded values or the Java class file that is used to encode/decode the system property values.