15

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" /> 
Adi Baron
  • 546
  • 2
  • 5
  • 21
  • 4
    Where were you thinking of storing the encryption key to unlock the password to unlock the keystorePass? Just out of interest. – David Apr 24 '13 at 14:22
  • 2
    I am aware that this is not a simple question. I am asking to know if there is a standard solution. – Adi Baron Apr 24 '13 at 14:35
  • No, there's on standard solution other than _maybe_ overriding the Connector as I mentioned below, but that's in no way a standard solution and more of a down right hack. – David Apr 24 '13 at 14:37

7 Answers7

17

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.

user3675974
  • 191
  • 1
  • 5
12

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.

David
  • 19,577
  • 28
  • 108
  • 128
  • A small correction - as connectors are final (at least at tomcat 7) it is the protocol one needs to override – Adi Baron May 02 '13 at 13:57
  • Could I please Ask you please which protocol one needs to override? and how? – Hasan Aug 11 '17 at 12:24
  • 1
    I disagree with the above. The whole point of security is a layered defense. In the cloud for example, it is possible to encrypt data and then enable access to the encryption keys via IAM (on aws). Keeping passwords on disk in plain text is just bad security. Such secrets should only ever be in process memory in their decrypted form. – Steve Owens Mar 09 '18 at 17:11
  • @david99world, can you provide an example how being able to _read_ server.xml would be a worry? – pointyhat Sep 06 '20 at 04:00
10

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;"
/>
setevoy
  • 4,374
  • 11
  • 50
  • 87
  • 2
    It's not a solutions, It's just encode/decode like Base64/Hex. We should show the password. But nice idea. thanks! – 신동평 Dec 23 '19 at 08:37
2

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

AKumar
  • 95
  • 2
  • 14
1

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.

Pawel Uchida-Psztyc
  • 3,735
  • 2
  • 20
  • 41
1

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.

Gaurav KR
  • 41
  • 3
0

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;
Nicholas Sushkin
  • 13,050
  • 3
  • 30
  • 20