如何创建两个tomcat在tomcat之间使用https时会满意的自签名证书



我正在尝试在两个tomcat服务器之间使用https。不幸的是,自签名证书导致了此错误:

Caused by: javax.net.ssl.SSLHandshakeException: 
sun.security.validator.ValidatorException: PKIX path building failed: 
sun.security.provider.certpath.SunCertPathBuilderException: 
unable to find valid certification path to requested target

具体来说,我有一个主tomcat和一些从tomcat服务器。master使用简单的HttpURLConnection从servlet进行通信。

对我来说,使用自己生成的证书颁发机构创建自签名证书的最简单方法是什么,这样每次添加新服务器时,我就不需要更改tomcat主服务器

我可以访问openssl和java7密钥工具

供参考我以前的配置:

server.xml连接器:

<Connector port="443" maxHttpHeaderSize="8192" maxThreads="150" 
minSpareThreads="25" maxSpareThreads="75" enableLookups="false" 
disableUploadTimeout="true" acceptCount="100" debug="0" scheme="https" 
secure="true" clientAuth="false" sslProtocol="TLS" keystoreType="PKCS12" 
keystoreFile="/usr/java/apache-tomee-plus/conf/keystore.ks" 
keystorePass="XXX_SSL" truststoreType="JKS" 
truststoreFile="/usr/java/apache-tomee-plus/conf/truststore.ks" 
SSLEnabled="true" maxPostSize="0"/>

启动脚本/etc/init.d/tomee

$DAEMON_HOME/jsvc 
-user $TOMCAT_USER 
-home $JAVA_HOME 
-pidfile $JSVC_PID_FILE 
-Dcatalina.home=$CATALINA_HOME 
-Djava.security.auth.login.config=$CATALINA_HOME/conf/jaas.conf 
-Djavax.net.ssl.keystore=$CATALINA_HOME/conf/keystore.ks 
-Djavax.net.ssl.keyStorePassword=XXX_SSL 
-Djavax.net.ssl.trustStore=$CATALINA_HOME/conf/truststore.ks 
-Djavax.net.ssl.trustStorePassword=changeit 
-Djava.awt.headless=true 
-Djava.io.tmpdir=$TMP_DIR 
-Dopenam.agents.bootstrap.dir=/home/tomcat/tomcat_v6_agent/Agent_001/config 
-Djava.net.preferIPv4Stack=true -Djava.net.preferIPv4Addresses 
-outfile $CATALINA_HOME/logs/catalina.out 
-errfile $CATALINA_HOME/logs/catalina.err 
$CATALINA_OPTS 
-cp $CLASSPATH 
org.apache.catalina.startup.Bootstrap

conf/jaas.conf

josso {
org.josso.tc55.agent.jaas.SSOGatewayLoginModule required debug=true;
};

它只用于遗留支持,将逐步取消。我不确定它是否加载,因为它是为tomcat 5.5…构建的

在代码中,我通过使用以下HostnameVerifier()来避免在CN=中使用IP地址的问题。

HostnameVerifier hv = new HostnameVerifier()
{
public boolean verify(String urlHostName, SSLSession session)
{
return true;
}
};
HttpsURLConnection.setDefaultHostnameVerifier(hv);
connection = (HttpURLConnection) servlet.openConnection();

------------更新---------------

这已经通过与@Bruno的长时间讨论解决了,请使用他的原始帖子和我们的长时间聊天讨论。

最后,我使用了Keytool Explorer和XCA工具,使我更容易学习和执行。

首先,您实际上并没有建立服务器到服务器的连接:这些连接仍然是客户端到服务器的。恰好您的客户端是在servlet容器中运行的Web应用程序。

这很重要,因为将用于建立连接的是客户端信任设置。这些与Tomcat连接器上的信任设置不同。

客户端Web应用程序如何使用其信任设置取决于它的实现方式和使用的库。可以公平地说,大多数客户端都会使用默认的JRE设置,除非有特定的代码可以这样做。如果没有任何设置,JRE的cacerts文件通常会被使用(请参阅《JSSE参考指南》中的自定义部分中的详细信息)。您还可以指定常用的javax.net.ssl.*属性(使用-Djavax.net.ssl....=....);这将需要在JAVA_OPTS中的catalina启动脚本文件(.bat.sh,取决于平台)中完成。这将影响在此JRE实例中运行的默认值,因此它可能不是您的首选(您可能想要更具体的、只影响给定Web应用程序的内容,但您需要知道该Web应用程序是如何实现的,或者它可以使用哪些可能的选项)。不过,<Connector .../>中的设置将覆盖这一点(除非您也想使用客户端证书身份验证,否则那里的信任库设置无关紧要)。

如果您有多个系统,那么可能不值得使用自签名证书:每个客户端都需要将所有这些自签名证书导入其信任存储中。相反,您可以创建自己的CA并仅导入该CA证书。(在本地部署中,一个顶级/根CA就足够了,不需要中间CA。)OpenSSL的CA.pl是构建小型CA的好脚本,但您可能会发现XCA或TinyCA等其他工具更方便。(请注意,您通常可以直接在Java中使用.p12文件,使用PKCS12密钥库类型,而不是默认的JKS。对于PKCS#12存储,密钥库密码和密钥/密钥管理器密码相同。)

由于您使用的是IP地址而不是名称,您还应该注意Java在IP地址上严格遵循RFC 2818(与许多浏览器不同)。特别是,使用者DN的CN中的IP地址将不起作用:它需要在使用者替代名称扩展中(类型为IPAddress,而不是DNSname)。


编辑:

首先,去掉主机名验证器:它为所有将使用该默认验证器的HttpsURLConnection引入了MITM攻击的漏洞,包括与外部服务器的可能连接。您可以使用SAN中的IP地址来解决此问题。

以下是您需要遵循的步骤:

  • 创建您的CA(例如使用XCA)。将其证书(而非私钥)导出到mycacert.pem

  • 如果您的应用程序可能与其他服务建立HTTPS连接,请将默认的cacerts文件复制到新文件mytruststore.jks中(如果您的程序没有建立任何其他连接,请不要复制该文件,keytool将创建它)。

  • keytool:将mycacert.pem导入mytruststore.jks

    keytool -import -keystore mytruststore.jks -alias my_alias_name -cert mycacert.pem
    
  • 您现在有了一个信任库,可以将其安装在所有需要与这些服务器通信的客户端中。(如果使用JRE更新了cacerts文件,则可能需要再次执行这些步骤。)

    • 在客户端中配置该信任库。在这里,这是在JAVA_OPTS:-Djavax.net.ssl.trustStore=...(和密码)中的Catalina脚本中完成的。除了这些JVM选项在Catalina启动严格中之外,它们与其他类型的客户端相同
    • 去掉-Djavax.net.ssl.keyStore=...,它只用于客户端证书身份验证,让Web应用程序使用其运行的服务器的证书进行客户端身份验证并不总是一个好主意
  • 为要使用的每台服务器创建一个具有该CA的证书。如果要使用IP地址,请确保使用的是IP地址SAN。(由于您可以控制一切,XCA应该允许您一步生成私钥和证书,这里不需要CSR,但如果您愿意,您可以。它还为web服务器扩展准备了配置文件,以及添加IP地址或其他类型的主题替代名称的选项。)

  • 将证书和私钥的组合导出到每个服务器的PKCS#12(例如server1.p12)中。每个服务器都应该有自己的,不要共享它们。您将能够在每个服务器配置中将其用作"密钥库密钥库"。

    • 在连接器配置中的每个服务器中配置这些密钥库(使用PKCS#12存储时,keystoreType="PKCS12",keyPasskeystorePass应该相同)
    • 除非使用客户端证书身份验证,否则连接器配置中不需要任何信任库设置

(使用客户端证书身份验证来确保与服务器的连接至少只来自同一个CA的另一个实体可能是值得的,缺少更精确的内容,但您在连接器上配置的信任库需要只包含您的CA证书,而不包含其他证书,因此为此创建一个全新的信任库。)

要回答您的问题,我认为您不需要证书链。我已经将tomcat与自签名服务器证书一起使用,并使用另一个自签名客户端证书连接到它。我不知道tomEE的情况,但我想它也会是一样的。

为了帮助您了解发生了什么,我考虑了以下可能的调试操作:

  • 在进入自动化之前,请尝试在没有脚本的情况下使事情正常工作。生成正确的配置会更容易。

  • 确保脚本正确运行:检查存储的tje内容是否如预期,并确保server.xml文件指向正确的文件。

  • 如果可以的话,请尝试使用浏览器客户端身份验证单独测试服务器。

最重要的是,确保您的服务器之间确实需要https。如果您处于受保护的领域,那么您可能过度使用了安全性,只是降低了应用程序的速度。

如何创建两个tomcat在tomcat之间使用https时会满意的自签名证书?

Tomcat必须允许您做以下两件事中的一件。第一种是运行私有PKI,第二种是使用pinset。


首先简单的答案是:pinset是一组可信任的证书或公钥,用于标识主机或一组主机。它是一个带有乘数的证书或公钥固定。在这种情况下,您希望获得3或4个证书,并将它们应用于每台服务器。然后,当对等方进行通信时,他们将隐含地信任对等方的证书。

Tomcat(或IIS或Apache)似乎不允许您指定针集。也就是说,您不能指定要信任的自签名证书的集合。因此,您必须使用私人PKI。


更难的答案是运行私有PKI。运行私有PKI没有什么困难。困难在于实现它,因为它有很多。没有银弹可以让它发挥作用,因为你需要私有PKI,你需要配置你的服务器来使用它。


关于"我如何使用自己的CA对每个证书进行签名"的问题,请参阅,例如,如何设置CA,甚至使用我的CA对证书进行签名并创建我的CA如何进行可信的自我认证。。。。


我无法帮助您配置Tomcat。但是,如果您正在构建客户端和服务器,以下是在OpenSSL中以编程方式完成此操作的步骤。这里,"客户端"one_answers"服务器"是指Tomcat服务器所扮演的角色(每个都扮演两个角色):

  • 客户端

    • 与您的CA一起致电SSL_CTX_load_verify_locations
    • 在上下文中调用SSL_CTX_set_default_verify_paths
    • SSL_VERIFY_PEER呼叫SSL_CTX_set_verify
  • 服务器

    • 呼叫SSL_CTX_use_certificate_chain_file
    • 呼叫SSL_CTX_use_PrivateKey_file
    • SSL_VERIFY_PEER|VERIFY_FAIL_IF_NO_PEER_CERT呼叫SSL_CTX_set_verify

当充当客户端时,Tomcat服务器需要知道组织的受信任根。这就是SSL_CTX_load_verify_locationsSSL_CTX_set_default_verify_paths所实现的。

当充当客户端时,SSL_CTX_set_verify确保Tomcat服务器验证对等方的标识(以OpenSSL中缺少主机名检查为模)。

当充当服务器时,Tomcat服务器需要知道要发送的证书和要使用的私钥。这就是SSL_CTX_use_certificate_chain_fileSSL_CTX_use_PrivateKey_file的作用

当充当服务器时,Tomcat服务器需要相互身份验证。这就是SSL_CTX_set_verifySSL_VERIFY_PEER|VERIFY_FAIL_IF_NO_PEER_CERT的作用。

不过,我不知道如何将这些功能移植到Tomcat配置中。

最新更新