Question

Is there an option to encrypt keystorePass value in tomcat server.xml? I don't want it to be a plain text

    <Connector port="8403" //...
        keystorePass="myPassword" /> 
Was it helpful?

Solution 2

If someone has access to your server.xml, the plain text value of your keystorePass appearing are only one of your worries.

If someone has access from there, they could do much more harm. Encrypting the password here is really just moving the problem elsewhere as then someone could find the encryption key for this encryption key (a bit like a Russian doll).

If you want to encrypt the password, you have to override the Connector implementation to decrypt the encrypted password so that the real pwd is accessible or available to tomcat.

OTHER TIPS

There is a better way, than just using the XML encode.

Create an Encryption Class to encrypt and decrypt your password.

And override Http11Nio2Protocol class, something similar to the below code.

 public class Http11Nio2Protocol extends org.apache.coyote.http11.Http11Nio2Protocol {

@Override
public void setKeystorePass(String s) {
    try {
        super.setKeystorePass(new EncryptService().decrypt(s));
    } catch (final Exception e){
        super.setKeystorePass("");
    }
}

}

Note: EncryptService is our own encryption class.

And configure the overridden class in the protocol attribute in server.xml like below.

<Connector port="8443" protocol="<com.mypackage.overridden_Http11Nio2Protocol_class>"
           maxThreads="150" SSLEnabled="true" scheme="https" secure="true"
           clientAuth="false" sslProtocol="TLS" 
          keystoreFile="conf/.ssl/keystore.jks"        
           keystorePass="<encrypted_password>"/>

Hope this helps.

Faced with same problem. Customer demands to "hide" all passwords.

So, simplest way to pass audit - from Tomcat Wiki.

Go to page http://coderstoolbox.net/string/#!encoding=xml&action=encode&charset=none and encode you pass to XML-view.

Thus - <Connector> element looks like:

<Connector
  port="8443"
  protocol="HTTP/1.1"
  SSLEnabled="true"
  enableLookups="false"
  disableUploadTimeout="true"
  scheme="https"
  secure="true"
  clientAuth="want"
  sslProtocol="TLS"
  keystoreFile="conf/.ssl/keystore.jks"
  keyAlias="tomcat"
  keystorePass="&#99;&#104;&#105;&#107;&#115;"
  truststoreFile="conf/.ssl/trustedstore.jks"
  truststorePass="&#99;&#104;&#105;&#107;&#115;"
/>

We were also facing similar problem but we created our own encryption and decryption logic to tackle this. Here is the code

/* class is used to generate encrypted password */

public class ClientForPasswordGeneration {

    public static void main(String[] args) {
        //final String secretKey = "ssshhhhhhhhhhh!!!!";
        final String secretKey = PasswordKey.getEncryptionKey();
        GenerateLogic object = new GenerateLogic();

        String password = PasswordField.readPassword("Enter password: ");

        String encryptPassword = object.encrypt(password, secretKey);
        System.out.println("Encrypted Password:");
        System.out.println(encryptPassword);

    }

}

Another Class

class EraserThread implements Runnable {
    private boolean stop;

    /**
     * @param The
     *            prompt displayed to the user
     */
    public EraserThread(String prompt) {
        System.out.print(prompt);
    }

    /**
     * Begin masking...display asterisks (*)
     */
    public void run() {
        stop = true;
        while (stop) {

            System.out.print("\010*");

            try {

                Thread.currentThread().sleep(1);
                // System.out.println("current thread::" + Thread.currentThread());
            } catch (InterruptedException ie) {
                ie.printStackTrace();
            }
        }
    }

    /**
     * Instruct the thread to stop masking
     */
    public void stopMasking() {
        this.stop = false;
    }

}

Logic which generated hashed code

import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Base64;

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

public class GenerateLogic {
    private static SecretKeySpec secretKey;
    private static byte[] key;

    public static void setKey(String myKey) {
        MessageDigest sha = null;
        try {
            key = myKey.getBytes("UTF-8");
            sha = MessageDigest.getInstance("SHA-1");
            key = sha.digest(key);
            key = Arrays.copyOf(key, 16);
            secretKey = new SecretKeySpec(key, "AES");
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }

    public static String encrypt(String strToEncrypt, String secret) {
        try {
            setKey(secret);
            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
            cipher.init(Cipher.ENCRYPT_MODE, secretKey);
            return Base64.getEncoder().encodeToString(cipher.doFinal(strToEncrypt.getBytes("UTF-8")));
        } catch (Exception e) {
            System.out.println("Error while encrypting: " + e.toString());
        }
        return null;
    }

    public static String decrypt(String strToDecrypt) {
        try {
            //System.out.println("decryptedString methods");
            //String secret = "ssshhhhhhhhhhh!!!!";
            String secret = PasswordKey.getEncryptionKey();
            setKey(secret);
            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5PADDING");
            cipher.init(Cipher.DECRYPT_MODE, secretKey);
            //System.out.println("testing string values::" + new String(cipher.doFinal(Base64.getDecoder().decode(strToDecrypt))));
            return new String(cipher.doFinal(Base64.getDecoder().decode(strToDecrypt)));
        } catch (Exception e) {
            System.out.println("Error while decrypting: " + e.toString());
        }
        return null;
    }

    public static void main(String[] args) {
        final String secretKey = "ssshhhhhhhhhhh!!!!";

        String originalString = "changeit";
        String encryptedString = GenerateLogic.encrypt(originalString, secretKey);
        String decryptedString = GenerateLogic.decrypt(encryptedString);

        System.out.println(originalString);
        System.out.println(encryptedString);
        System.out.println(decryptedString);
    }

}

This is where we extended the class org.apache.coyote.http11.Http11Nio2Protocol which is present in tomcat-coyote-8.0.29.jar which is present in lib folder of tomcat 8. So while compiling these classes tomcat-coyote-8.0.29.jar should be present.

public class Http11Nio2Protocol extends org.apache.coyote.http11.Http11Nio2Protocol {

    @Override
    public void setKeystorePass(String s) {
        try {
            super.setKeystorePass(new GenerateLogic().decrypt(s));
        } catch (final Exception e) {
            super.setKeystorePass("");
        }
    }

}

This is where user has to enter password in cmd which should be hashed

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class PasswordField {

    /**
     * @param prompt
     *            The prompt to display to the user
     * @return The password as entered by the user
     */
    public static String readPassword(String prompt) {
        EraserThread et = new EraserThread(prompt);
        Thread mask = new Thread(et);
        mask.start();

        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
        String password = "";

        try {
            password = in.readLine();

        } catch (IOException ioe) {
            ioe.printStackTrace();
        }
        // stop masking
        et.stopMasking();
        // return the password entered by the user
        return password;
    }
}

This is where you keep your password key. You should change it.

public class PasswordKey {

    private static String ENCRYPTION_KEY = "myKeysecretkey";

    protected static String getEncryptionKey()
    {
        return ENCRYPTION_KEY;
    }

}

compile above classes to generate class files with below command in cmd. Remember tomcat-coyote-8.0.29.jar should be present in same folder where all the java files are present.

javac  -cp ".;tomcat-coyote-8.0.29.jar" *.java

Make a jar with the generated class file using this command in cmd

jar -cvf  PasswordEncryptor.jar  *.class

This will create a jar file PasswordEncryptor.jar

Paste the generated PasswordEncryptor.jar in lib folder of Tomcat8. i.e. apache-tomcat-8.5.9\lib

Now go to this location and type below command to generate the hashed password.

java -cp ".;PasswordEncryptor.jar" ClientForPasswordGeneration

Enter The password and it will show hashed password

Now go to apache-tomcat-8.5.9\conf and edit server.xml

Use the hashed password in keystorpasss of certificate

<Connector port="9443" protocol="Http11Nio2Protocol" SSLEnabled="true"
               maxThreads="150" scheme="https" secure="true"
               clientAuth="false" sslProtocol="TLS" 
        keystoreFile="C:\Certificates\SSLCert.cert" keystorePass="nOS74yuWW4s18TsL2UJ51A=="/>

Notice the protocol is the custom class name.

Hope this will help you.

Thanks

1) Create the CustomEncryptService to encrypt and decrypt your password.

2) Override Http11Nio2Protocol class, something similar to the below code. (As mentioned above by user3675974)

public class CustomHttp11Nio2Protocol extends org.apache.coyote.http11.Http11Nio2Protocol {

  @Override
  public void setKeystorePass(String s) {
    try {
      super.setKeystorePass(new CustomEncryptService().decrypt(s));
    } catch (final Exception e){
      super.setKeystorePass("");
    }
  }
}

3) Configure the overridden class in the protocol attribute in server.xml like below.

<Connector port="8443" 
  protocol="<com.mypackage.xyz....CustomHttp11Nio2Protocol>"
  maxThreads="150" 
  SSLEnabled="true" 
  scheme="https" 
  secure="true"
  clientAuth="false" 
  sslProtocol="TLS" 
  keystoreFile="conf/.ssl/keystore.jks"        
  keystorePass="<encrypted_password>"/>

4) Since this CustomHttp11Nio2Protocol class should be available during startup, create the Jar having the CustomHttp11Nio2Protocol and CustomEncryptService class, and put it inside your tomcat/lib.

Hope this helps.

Here's a handy Perl one-liner to XML encode a password:

$ perl -pe 's/(.)/"&#".ord($1).";"/eg;' <<< 'secret'
# &#115;&#101;&#99;&#114;&#101;&#116;

We need to create jar file to decrypt the password placed in keyStorePass.Here are the steps to create the jar file using Eclipse IDE. :

Step 1: In Eclipse IDE, Go to New->Other->Java Project.Click Next. Enter project name. Click Next and Finish.

Step 2: Expand the newly created project. Right click on src->New->package. Create package name as "com.apache"

Step 3: Create a class in com.apache package to decrypt password:

package com.apache;
 public class DecryptPassword {
    
    public static synchronized String decrypt(String str) throws Exception {
            /* Decryption Logic*/
     }
}

Step 4: Create a new class named as CustomHttp11Nio2Protocol in com.apache package to extend org.apache.coyote.http11.Http11Nio2Protocol class

package com.apache;
public class CustomHttp11Nio2Protocol extends org.apache.coyote.http11.Http11Nio2Protocol {

 @Override
 public void setKeystorePass(String s) {
   try {
      super.setKeystorePass(DecryptPassword.decrypt(s));
   } catch (final Exception e){
     super.setKeystorePass("");
   }
 }
}

Step 5: You will receive some errors in above class. Resolve them by right click on Project Name then Properties->Build Path->In Libraries Tab-> Click Add External jars. Select tomcat-coyote.jar and tomcat-util.jar in tomcat's bin folder. Also select the tomcat-juli.jar in tomcat's bin folder. The errors should not be visible now in both the classes.

Step 6: Export the project as JAR file. Right click on Project Name->Export->Under Java Select JAR File->Click Next-> Choose the Export Destination as Tomcat's lib folder. Click Finish.

Step 7: Paste the following connector in tomcat's server.xml. Observe that the protocol value contains the package name we had defined in step 2 along with class name used in Step 4. Encrypted password must be placed in keystorePass of the Connector.

<Connector
  protocol="com.apache.CustomHttp11Nio2Protocol"
  port="8443" maxThreads="200"
  scheme="https" secure="true" SSLEnabled="true"
  keystoreFile="serverCert.p12"
  keystorePass="<encrypted_password>"
  clientAuth="false" sslProtocol="TLS"/>

Step 8: Restart Tomcat.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top