Is there an option to encrypt keystorePass value in tomcat server.xml? I don\'t want it to be a plain text
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="chiks"
truststoreFile="conf/.ssl/trustedstore.jks"
truststorePass="chiks"
/>
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
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
Here's a handy Perl one-liner to XML encode a password:
$ perl -pe 's/(.)/"&#".ord($1).";"/eg;' <<< 'secret'
# secret
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.
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.