Зашифровать пароль хранилища ключей tomcat

Есть ли возможность зашифровать значение keystorePass в server.xml tomcat? Я не хочу, чтобы это был простой текст

    <Connector port="8403" //...
        keystorePass="myPassword" /> 

person Adi Baron    schedule 24.04.2013    source источник
comment
Где вы думали хранить ключ шифрования, чтобы разблокировать пароль для разблокировки keystorePass? Просто из интереса.   -  person david99world    schedule 24.04.2013
comment
Я знаю, что это не простой вопрос. Я спрашиваю, есть ли стандартное решение.   -  person Adi Baron    schedule 24.04.2013
comment
Нет, есть стандартное решение, отличное от возможно переопределения соединителя, как я упомянул ниже, но это никоим образом не стандартное решение, а скорее прямой хак.   -  person david99world    schedule 24.04.2013


Ответы (7)


Если у кого-то есть доступ к вашему server.xml, простое текстовое значение вашего keystorePass — это только одна из ваших забот.

Если у кого-то есть доступ оттуда, они могут причинить гораздо больше вреда. Шифрование пароля здесь на самом деле просто переносит проблему в другое место, поскольку тогда кто-то может найти ключ шифрования для этого ключа шифрования (немного похоже на русскую куклу).

Если вы хотите зашифровать пароль, вам нужно переопределить реализацию соединителя, чтобы расшифровать зашифрованный пароль, чтобы настоящий pwd был доступен или доступен для tomcat.

person david99world    schedule 24.04.2013
comment
Небольшое исправление - поскольку коннекторы являются окончательными (по крайней мере, на tomcat 7), это протокол, который необходимо переопределить. - person Adi Baron; 02.05.2013
comment
Могу я спросить вас, пожалуйста, какой протокол нужно переопределить? и как? - person Hasan; 11.08.2017
comment
Я не согласен с вышесказанным. Весь смысл безопасности заключается в многоуровневой защите. Например, в облаке можно зашифровать данные, а затем разрешить доступ к ключам шифрования через IAM (на aws). Хранение паролей на диске в виде простого текста — это просто плохая безопасность. Такие секреты должны находиться в памяти процесса только в расшифрованном виде. - person Steve Owens; 09.03.2018
comment
@david99world, можете ли вы привести пример того, как возможность чтения server.xml может вызвать беспокойство? - person pointyhat; 06.09.2020

Есть лучший способ, чем просто использовать кодировку XML.

Создайте класс шифрования для шифрования и расшифровки вашего пароля.

И переопределите класс Http11Nio2Protocol, что-то похожее на приведенный ниже код.

 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("");
    }
}

}

Примечание. EncryptService — это наш собственный класс шифрования.

И настройте переопределенный класс в атрибуте протокола в server.xml, как показано ниже.

<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>"/>

Надеюсь это поможет.

person user3675974    schedule 12.04.2016
comment
Да, именно это мы и сделали. Смотрите мой комментарий к принятому ответу. - person Adi Baron; 17.04.2016
comment
Я пробовал это решение, но получаю сообщение об ошибке: java.lang.ClassNotFoundException: listener.Http11Nio2Protocol. - person Turik Mirash; 18.10.2016
comment
Я реализовал, что он не читает метод setKeystorePass!! - person Hasan; 11.08.2017

Столкнулся с такой же проблемой. Заказчик требует «скрыть» все пароли.

Итак, самый простой способ пройти аудит - с Tomcat Wiki.

Перейдите на страницу http://coderstoolbox.net/string/#!encoding=xml&action=encode&charset=none и кодировать, который вы передаете в XML-представление.

Таким образом - элемент <Connector> выглядит так:

<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;"
/>
person setevoy    schedule 17.06.2014
comment
Это не решения, это просто кодирование/декодирование, например Base64/Hex. Мы должны показать пароль. Но хорошая идея. Благодарность! - person 신동평; 23.12.2019

Мы также столкнулись с подобной проблемой, но для решения этой проблемы мы создали собственную логику шифрования и дешифрования. Вот код

/* 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);

    }

}

Другой класс

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;
    }

}

Логика, сгенерировавшая хэш-код

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);
    }

}

Именно здесь мы расширили класс org.apache.coyote.http11.Http11Nio2Protocol, который присутствует в tomcat-coyote-8.0.29.jar, который присутствует в папке lib tomcat 8. Поэтому при компиляции этих классов tomcat-coyote-8.0. 29.jar должен присутствовать.

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("");
        }
    }

}

Здесь пользователь должен ввести пароль в cmd, который должен быть хэширован

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;
    }
}

Здесь вы храните свой ключ-пароль. Вы должны изменить его.

public class PasswordKey {

    private static String ENCRYPTION_KEY = "myKeysecretkey";

    protected static String getEncryptionKey()
    {
        return ENCRYPTION_KEY;
    }

}

скомпилируйте вышеуказанные классы для создания файлов классов с помощью приведенной ниже команды в cmd. Помните, что tomcat-coyote-8.0.29.jar должен находиться в той же папке, где находятся все файлы Java.

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

Создайте банку со сгенерированным файлом класса, используя эту команду в cmd

jar -cvf  PasswordEncryptor.jar  *.class

Это создаст jar-файл PasswordEncryptor.jar.

Вставьте сгенерированный файл PasswordEncryptor.jar в папку lib Tomcat8. то есть apache-tomcat-8.5.9\lib

Теперь перейдите в это место и введите команду ниже, чтобы сгенерировать хешированный пароль.

java -cp ".;PasswordEncryptor.jar" ClientForPasswordGeneration

Введите пароль, и он покажет хешированный пароль

Теперь перейдите в apache-tomcat-8.5.9\conf и отредактируйте server.xml.

Используйте хешированный пароль в keystorepasss сертификата

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

Обратите внимание, что протокол — это имя пользовательского класса.

Надеюсь, что это поможет вам.

Спасибо

person AKumar    schedule 27.12.2016

1) Создайте CustomEncryptService для шифрования и расшифровки вашего пароля.

2) Переопределите класс Http11Nio2Protocol, что-то похожее на приведенный ниже код. (Как упоминалось выше пользователем 3675974)

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) Настройте переопределенный класс в атрибуте протокола в server.xml, как показано ниже.

<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) Поскольку этот класс CustomHttp11Nio2Protocol должен быть доступен во время запуска, создайте Jar с классами CustomHttp11Nio2Protocol и CustomEncryptService и поместите его в свой tomcat/lib.

Надеюсь это поможет.

person Navneet Mishra    schedule 25.10.2019

Вот удобный однострочный код Perl для XML-кодирования пароля:

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

Нам нужно создать файл jar для расшифровки пароля, помещенного в keyStorePass. Вот шаги для создания файла jar с помощью Eclipse IDE. :

Шаг 1. В Eclipse IDE перейдите в раздел Создать->Другое->Java Project. Нажмите «Далее». Введите название проекта. Нажмите «Далее» и «Готово».

Шаг 2: Разверните только что созданный проект. Щелкните правой кнопкой мыши src->Создать->package. Создайте имя пакета как com.apache

Шаг 3: Создайте класс в пакете com.apache для расшифровки пароля:

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

Шаг 4. Создайте новый класс с именем CustomHttp11Nio2Protocol в пакете com.apache, чтобы расширить класс org.apache.coyote.http11.Http11Nio2Protocol.

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("");
   }
 }
}

Шаг 5: Вы получите некоторые ошибки в классе выше. Разрешите их, щелкнув правой кнопкой мыши имя проекта, затем «Свойства» -> «Путь сборки» -> «Вкладка «Библиотеки» -> нажмите «Добавить внешние банки». Выберите tomcat-coyote.jar и tomcat-util.jar в папке bin tomcat. Также выберите tomcat-juli.jar в папке bin tomcat. Теперь ошибки не должны быть видны в обоих классах.

Шаг 6: Экспортируйте проект в виде файла JAR. Щелкните правой кнопкой мыши имя проекта->Экспорт->В разделе Java выберите файл JAR->Нажмите «Далее»->Выберите пункт назначения экспорта в качестве папки Tomcat lib. Нажмите Готово.

Шаг 7: Вставьте следующий коннектор в файл server.xml tomcat. Обратите внимание, что значение протокола содержит имя пакета, которое мы определили на шаге 2, вместе с именем класса, использованным на шаге 4. Зашифрованный пароль должен быть помещен в keystorePass соединителя.

<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"/>

Шаг 8: Перезапустите Tomcat.

person Gaurav KR    schedule 24.06.2021