【问题标题】:Encrypt tomcat keystore password加密tomcat密钥库密码
【发布时间】:2013-04-24 14:03:00
【问题描述】:

是否有加密tomcat server.xml 中的keystorePass 值的选项? 我不希望它是纯文本

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

【问题讨论】:

  • 您想在哪里存储加密密钥以解锁密码以解锁 keystorePass?只是出于兴趣。
  • 我知道这不是一个简单的问题。我想知道是否有标准解决方案。
  • 不,除了我在下面提到的可能覆盖连接器之外,还有其他标准解决方案,但这绝不是标准解决方案,更像是一种完全正确的黑客攻击。跨度>

标签: tomcat server.xml


【解决方案1】:

有一个比仅仅使用 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>"/>

希望这会有所帮助。

【讨论】:

  • 是的,这正是我们所做的。请参阅我对已接受答案的评论。
  • 我尝试了这个解决方案,但我得到了错误:java.lang.ClassNotFoundException: listener.Http11Nio2Protocol
  • 我已经实现了它不读取setKeystorePass方法!!
  • 嗨,我也收到了错误“java.lang.ClassNotFoundException”。有没有人找到解决此错误的解决方法?提前致谢。
【解决方案2】:

如果有人可以访问您的 server.xml,那么出现的 keystorePass 的纯文本值只是您的担心之一。

如果有人可以从那里访问,他们可能会造成更大的伤害。在这里加密密码实际上只是将问题转移到其他地方,因为然后有人可以找到此加密密钥的加密密钥(有点像俄罗斯娃娃)。

如果你想加密密码,你必须覆盖连接器 解密加密密码的实现,以便真正的密码是 可访问或可用于 tomcat。

【讨论】:

  • 一个小修正 - 因为连接器是最终的(至少在 tomcat 7 上),它是需要覆盖的协议
  • 请问需要覆盖哪个协议?以及如何?
  • 我不同意上述观点。整个安全点是分层防御。例如,在云中,可以加密数据,然后通过 IAM(在 aws 上)启用对加密密钥的访问。将密码以纯文本形式保存在磁盘上只是不安全的做法。此类机密应仅以其解密形式存在于进程内存中。
  • @david99world,你能举个例子读取 server.xml 会让人担心吗?
【解决方案3】:

面临同样的问题。客户要求“隐藏”所有密码。

因此,通过审核的最简单方法 - 来自 Tomcat Wiki

转到页面http://coderstoolbox.net/string/#!encoding=xml&action=encode&charset=none 并编码您传递给XML-view。

因此 - &lt;Connector&gt; 元素看起来像:

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

【讨论】:

  • 这不是解决方案,它只是像 Base64/Hex 一样编码/解码。我们应该显示密码。但好主意。谢谢!
【解决方案4】:

我们也面临类似的问题,但我们创建了自己的加密和解密逻辑来解决这个问题。这是代码

/* 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 8 的 lib 文件夹中的 tomcat-coyote-8.0.29.jar 中。所以在编译这些类时 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

jar -cvf  PasswordEncryptor.jar  *.class

这将创建一个 jar 文件 PasswordEncryptor.jar

将生成的 PasswordEncryptor.jar 粘贴到 Tomcat8 的 lib 文件夹中。即 apache-tomcat-8.5.9\lib

现在转到此位置并键入以下命令以生成散列密码。

java -cp ".;PasswordEncryptor.jar" ClientForPasswordGeneration

现在转到 apache-tomcat-8.5.9\conf 并编辑 server.xml

在证书的keystorpasss中使用散列密码

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

注意协议是自定义类名。

希望这会对你有所帮助。

谢谢

【讨论】:

    【解决方案5】:

    1) 创建CustomEncryptService 来加密和解密您的密码。

    2) 覆盖Http11Nio2Protocol 类,类似于下面的代码。 (如上面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) 在 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 类应该在启动期间可用,因此创建具有 CustomHttp11Nio2ProtocolCustomEncryptService 类的 Jar,并将其放在您的 tomcat/lib 中。

    希望这会有所帮助。

    【讨论】:

      【解决方案6】:

      这是一个方便的 Perl 单行代码,用于 XML 编码密码:

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

      【讨论】:

      • Perl 永远不会消亡,对吧? :)
      【解决方案7】:

      我们需要创建jar文件来解密keyStorePass中的密码。这里是使用Eclipse IDE创建jar文件的步骤。 :

      第 1 步:在 Eclipse IDE 中,转到 New->Other->Java Project。单击 Next。输入项目名称。点击下一步并完成。

      第 2 步:展开新创建的项目。 右键点击src->New->package。创建包名称为“com.apache”

      第三步:在com.apache包中创建一个类来解密密码:

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

      步骤 4:在 com.apache 包中创建一个名为 CustomHttp11Nio2Protocol 的新类,以扩展 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 步:您将在上述课程中收到一些错误。通过右键单击项目名称然后属性->构建路径->在库选项卡中->单击添加外部jar来解决它们。在tomcat的bin文件夹中选择tomcat-coyote.jar和tomcat-util.jar。还要选择tomcat的bin文件夹中的tomcat-juli.jar。这些错误现在在两个类中都不可见。

      第 6 步:将项目导出为 JAR 文件。 右击Project Name->Export->Under Java Select JAR File->点击Next->选择Export Destination作为Tomcat的lib文件夹。点击完成。

      第 7 步:将以下连接器粘贴到 tomcat 的 server.xml 中。 观察协议值包含我们在步骤 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。

      【讨论】:

        猜你喜欢
        • 2015-10-07
        • 2019-01-11
        • 1970-01-01
        • 1970-01-01
        • 2017-01-27
        • 2020-05-16
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多