Tomcat 7的双向认证

19

我正在尝试设置在Tomcat 7中运行的Java Web服务以使用双向身份验证。无论我做什么,似乎都无法连接到安全端口上的服务。

以下是我创建证书和密钥库等所做的操作:

//create the key and certificate for the tomcat server.
keytool -genkey -v -alias tomcat -keyalg RSA -validity 3650 -keystore tomcat.keystore

//create the key and certificate for the client machine.
keytool -genkey -v -alias clientkey -keyalg RSA -storetype PKCS12 -keystore client.p12

//export the client key
keytool -export -alias clientkey -keystore client.p12 -storetype PKCS12 -rfc -file client.cer

//import the client key into the server keystore
keytool -import -v -file client.cer -keystore tomcat.keystore

这是server.xml文件中的连接器:

<Connector port="8443"
    maxThreads="150"
    scheme="https"
    secure="true"
    sslProtocol="TLS"
    clientAuth="true"
    keystoreFile="tomcat.keystore"
    keystorePass="tomcat"
    truststoreFile="tomcat.keystore"
    truststorePass="tomcat"/>

tomcat-users.xml文件的样子如下:

<tomcat-users>
    <role rolename="tomcat"/>
    <role rolename="admin"/>
    <!-- note that the actual values for CN, OU, O, L, ST are different, but they match the values created in the client certificate -->
    <user username="CN=name, OU=unit, O=org, L=locality, ST=state, C=US" password="null" roles="admin" />
</tomcat-users>
以下内容在启动时设置:
-Djavax.net.ssl.keyStoreType=jks
-Djavax.net.ssl.keyStore=tomcat.keystore
-Djavax.net.ssl.keyStorePassword=tomcat
-Djavax.net.ssl.trustStore=tomcat.keystore
-Djavax.net.ssl.trustStorePassword=tomcat
-Djavax.net.debug=SSL

最后,我将client.p12文件复制到我的客户机上,并将其导入到Firefox的客户端证书中。

第一个问题: 当我从Firefox访问我的服务的端点(例如 - https://my.server.com:8443/test)时,我会收到“安全连接失败”的响应。SSL收到了超过最大允许长度的记录。(错误代码:ssl_error_rx_record_too_long)

第二个问题: 我真的不想在端口8443上运行此连接器。我想在端口7800上运行它(这是我们公司用于HTTPS的标准端口)。当我将Connector上的端口更改为7800并尝试访问该端点(例如 - https://my.server.com:7800/test)时,它永远无法解析页面。

因此,显然我错过了关键的部分。有人能看出我的错误吗?

更新:根据@Dave G的反馈运行以下命令:

openssl s_client -connect localhost:8443 -showcerts

生成以下输出:

CONNECTED(00000003)
140642290976584:error:140770FC:SSL routines:SSL23_GET_SERVER_HELLO:unknown protocol:s23_clnt.c:766:
---
no peer certificate available
---
No client certificate CA names sent
---
SSL handshake has read 7 bytes and written 263 bytes
---
New, (NONE), Cipher is (NONE)
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
---

我还在启动时添加了-Djavax.net.debug=SSL。这会在catalina.out文件的开头生成以下内容:

trustStore is: tomcat.keystore
trustStore type is : jks
trustStore provider is :
init truststore
adding as trusted cert:
  Subject: CN=localhost, OU=unit, O=org, L=Springfield, ST=MO, C=US
  Issuer:  CN=localhost, OU=unit, O=org, L=Springfield, ST=MO, C=US
  Algorithm: RSA; Serial number: 0x5485b5a5
  Valid from Mon Dec 08 14:28:53 UTC 2014 until Thu Dec 05 14:28:53 UTC 2024

adding as trusted cert:
  Subject: CN=William Jackson, OU=unit, O=org, L=Springfield, ST=MO, C=US
  Issuer:  CN=William Jackson, OU=unit, O=org, L=Springfield, ST=MO, C=US
  Algorithm: RSA; Serial number: 0x5485b6af
  Valid from Mon Dec 08 14:33:19 UTC 2014 until Sun Mar 08 14:33:19 UTC 2015

trigger seeding of SecureRandom
done seeding SecureRandom

然后有很多:

Ignoring unavailable cipher suite: <suite name>
Ignoring unsupported cipher suite: <suite name>

额外的背景信息:我的Tomcat服务器在运行Centos 6.4,它是在我的笔记本电脑上运行的VirtualBox虚拟机中运行的。Firefox版本为32.0.3,并直接在我的笔记本电脑(Mac)上运行。 - wbj
5个回答

35

好的-经过更多的挖掘,我最终使这个工作起来了。非常感谢@Dave G和这篇教程:在Tomcat上配置双向SSL身份验证,这些指令大多是改编自此处。

通常,使双向身份验证功能正常的步骤如下:

  1. 为Tomcat服务器创建证书。客户端必须信任此证书。
  2. 为Tomcat服务器创建密钥库,并将服务器证书导入其中。
  3. 为客户端创建证书。服务器必须信任此证书。
  4. 客户端证书导入服务器密钥库中
  5. 使用正确的连接器XML更新Tomcat server.xml文件。

以上步骤在服务器上是必要的。 完成后,要设置客户端,请执行以下操作:

  1. 从服务器复制客户端证书到客户端。
  2. 在与服务器通信时使用客户端证书(此过程因客户端应用程序的性质而异)。

对于证书配置,我在服务器机器上执行了以下操作:

# For the following commands, set the values in parenthesis to be whatever makes sense for your environment.  The parenthesis are not necessary for the command.

# This is an all-in-one command that generates a certificate for the server and places it in a keystore file, while setting both the certifcate password and the keystore password.
# The net result is a file called "tomcat.keystore". 

keytool -genkeypair -alias (serveralias) -keyalg RSA -dname "CN=(server-fqdn),OU=(organizationalunit),O=(organization),L=(locality),ST=(state),C=(country)" -keystore tomcat.keystore -keypass (password) -storepass (password)

# This is the all-in-one command that generates the certificate for the client and places it in a keystore file, while setting both the certificate password and the keystore password.
# The net result is a file called "client.keystore"

keytool -genkeypair -alias (clientalias) -keyalg RSA -dname "CN=(client),OU=(organizationalunit),O=(organization),L=(locality),ST=(state),C=(country)" -keypass (password) -keystore client.keystore -storepass (password) 

# This command exports the client certificate.  
# The net result is a file called "client.cer" in your home directory.

keytool -exportcert -rfc -alias (clientalias) -file client.cer -keypass (password) -keystore client.keystore -storepass (password)

# This command imports the client certificate into the "tomcat.keystore" file.

keytool -importcert -alias (clientalias) -file client.cer -keystore tomcat.keystore -storepass (password) -noprompt

证书现在应该已经适当地设置好了。下一步是在tomcat server.xml中配置您的连接器。添加一个看起来像这样的连接器元素:

<Connector port="8443"
    maxThreads="150"
    scheme="https"
    secure="true"
    SSLEnabled="true"
    truststoreFile="/full/path/to/tomcat.keystore"
    truststorePass="(password)"
    keystoreFile="/full/path/to/tomcat.keystore"
    keystorePass="(password)"
    clientAuth="true"
    keyAlias="serverkey"
    sslProtocol="TLS"/>      
请注意,上述XML中: 1. “port”属性可以是任何你想要的。 2. “keystoreFile”和“truststoreFile”属性应该是完整路径。Tomcat默认不会在与server.xml相同的目录中查找。 3. “keystorePass”和“truststorePass”属性应与创建tomcat.keystore文件时使用的(密码)值匹配。 4. “clientAuth”属性必须设置为“true”。这就是触发双向认证的方法。
此外,在server.xml中,请确保没有定义一个AprLifecycleListner。该监听器的XML代码如下:
<Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />

应删除/注释掉该元素。AprLifecycleListener的配置方式与上述描述不同,因此不能按照这些说明使用。

重新启动Tomcat。服务器配置应完成。

我使用Firefox测试我的工作,因为它很容易添加客户端证书。打开Firefox并尝试连接到定义在连接器中的Tomcat服务的端点。

Ex: https://mytomcatdomain.com:8443/test

当你这样做时,你应该从Firefox收到标准警告,提示存在不受信任的连接,因为我们为Tomcat服务器创建了自签名证书。添加一个证书例外,以便我们的客户端(Firefox)信任我们的服务器(Tomcat)。

一旦你添加了例外,你应该会收到一个"安全连接失败"的消息。错误代码是"ssl_error_bad_cert_alert"。 这证实了我们的Tomcat服务器正在请求客户端认证。请求失败是因为我们还没有配置Firefox发送我们所信任的客户端证书。

要配置Firefox,我们需要进行更多的魔法操作:

// Create a file called DumpPrivateKey.java.  The contents should look like so:
public class DumpPrivateKey {
public static void main(String[] args) throws Exception {
  final String keystoreName = args[0];
    final String keystorePassword = args[1];
    final String alias = args[2];
    java.security.KeyStore ks = java.security.KeyStore.getInstance("jks");
    ks.load(new java.io.FileInputStream(keystoreName), keystorePassword.toCharArray());
    System.out.println("-----BEGIN PRIVATE KEY-----");
    System.out.println(new sun.misc.BASE64Encoder().encode(ks.getKey(alias, keystorePassword.toCharArray()).getEncoded()));
    System.out.println("-----END PRIVATE KEY-----");
  }
}

使用以下命令编译Java文件:

javac DumpPrivateKey.java

现在我们将使用这个小工具从我们上面创建的client.keystore文件中提取一个密钥。将client.keystore和client.cer文件复制到与您的DumpPrivateKey类相同的目录中。执行以下操作:

# This extracts the client key from the client keystore

java DumpPrivateKey client.keystore (password) clientkey > clientkey.pkcs8

# This creates a client.p12 file that can be used by Firefox

openssl pkcs12 -export -in client.cer -inkey clientkey.pkcs8 -password pass:(password) -out client.p12
请注意,在上面的代码中,(password) 应该是您用于创建 client.keystore 的密码。
打开 Firefox 首选项。点击“证书”选项卡。点击“查看证书”按钮。点击“您的证书”选项卡。
点击“导入”按钮,浏览到之前创建的“client.p12”文件。您应该会被提示输入客户端证书的密码。
假设“client.p12”已成功导入,现在您可以刷新 Firefox 页面,您应该能从 Tomcat 服务器端点获得成功的响应。

9
我认为你不需要将“客户端证书导入服务器密钥库”,你只需要将颁发客户端证书的CA机构添加到服务器的受信任存储中,而不是证书本身(除非它是自签名的)。 - TheFiddlerWins
2
@TheFiddlerWins 如果它是在两个命令之前生成的,那么它怎么可能不是自签名的呢? - Andrey Regentov
1
太好了!我一直在想该怎么做,这个逐步教程帮了我很多。谢谢。 - ArielB
这个 - 特别是 AprLifecycleListner 部分 - 挽救了我的一天!谢谢!! - PKey
当将客户端证书导入服务器密钥库时,是否需要指定与导出时相同的clientAlias? - Duc Tran
一般来说,您应该将密钥库(您的身份)与信任库(您信任并允许使用X.509 authN进行身份验证的人)分开。因此,在这些说明中,您不应将客户端证书(或签名CA)导入服务器的密钥库,而是导入到一个单独的密钥库(命名为truststore.jks或其他)。然后将server.xml中的truststoreFile指向该文件。此外,按照惯例,信任库具有“changeit”的密码(因为密钥库需要一个密码)-信任库中的所有内容都是公共信息,但密钥库则不然。 - John Calcote

4

@wbj,从JKS到PKCS#12的PrivateKeyEntry导出可以更加容易:

keytool -importkeystore -srckeystore client.keystore -destkeystore client.p12 -deststoretype PKCS12 -srcalias client -deststorepass <password> -destkeypass <password>

欢呼。

3

我会尽力为您翻译。以下是需要翻译的内容:

使用 OpenSSL 证书使其正确工作花了我一些时间,我起草了笔记,以便帮助其他访问此页面的人。

步骤1:创建自己的根 CA

~/openssl$ mkdir -m 0700 /home/ubuntu/openssl/CA /home/ubuntu/openssl/CA/certs /home/ubuntu/openssl/CA/crl /home/ubuntu/openssl/CA/newcerts /home/ubuntu/openssl/CA/private
~/openssl$ touch /home/ubuntu/openssl/CA/indext.txt
~/openssl$ echo 1000 >> /home/ubuntu/openssl/CA/serial
~/openssl$ mv karun-tomcat-root-ca.key CA/private/

~/openssl$ sudo vi /etc/openssl.cnf
    # Make changes here
    dir = /home/ubuntu/openssl/CA
    #optionally change policy definitions as well
~/openssl$ openssl genrsa -des3 -out karun-tomcat-root-ca.key 2048

  #In below command make sure to use CN=<hostname of your machine>
~/openssl$ openssl req -new -x509 -days 36520 -key karun-tomcat-root-ca.key -out karun-tomcat-root-ca.crt -config openssl.cnf

~$ sudo cp ~/openssl/CA/certs/karun-tomcat-root-ca.crt /usr/share/ca-certificates/

  # make sure in the UI you enable/select the certificate created above
~$ sudo dpkg-reconfigure ca-certificates
  # Now reboot ubuntu machine just to make sure certificates are loaded successfully and tomcat picks it

步骤2:创建Tomcat服务器的密钥对

~$ openssl genrsa -out tomcat-server.key 2048

   # Use common name = <Give IP address>, department = Tomcat Server CSR
~$ openssl req -new -sha256 -config ~/openssl/openssl.cnf -key tomcat-server.key -out tomcat-server.csr
~$ openssl x509 -req -sha256 -days 36520 -in tomcat-server.csr -signkey tomcat-server.key -CA ~/openssl/CA/certs/karun-tomcat-root-ca.crt -CAkey ~/openssl/CA/private/karun-tomcat-root-ca.key -CAcreateserial -out tomcat-server.crt 
~$ openssl pkcs12 -export -name karun-tomcat-server-cert -in tomcat-server.crt -out tomcat-server.p12 -inkey tomcat-server.key -CAfile ~/openssl/CA/certs/karun-tomcat-root-ca.crt -caname karun-root -chain

~$ keytool -importkeystore -destkeystore tomcat-server.jks -srckeystore tomcat-server.p12 -srcstoretype pkcs12 -alias karun-tomcat-server-cert

~$ keytool -import -alias karun-root -keystore tomcat-server.jks -trustcacerts -file ~/openssl/CA/certs/karun-tomcat-root-ca.crt

# **(LATER)** Run this once client cert is generated
~$ keytool -importkeystore -alias karun-tomcat-client-cert -srckeystore ~/client-certs/tomcat-client.p12 -srcstoretype PKCS12 -destkeystore tomcat-server.jks -deststoretype JKS

# **(LATER)** Run this once tomcat server started successfully
~$ openssl s_client -connect localhost:8443 -cert ~/client-certs/tomcat-client.crt -key ~/client-certs/tomcat-client.key -debug -showcerts 

步骤三:创建客户端密钥对

~$ openssl genrsa -out tomcat-client.key 2048
  # Use common name = <tomcat-user.xml's user say 'admin'>, department = Tomcat Client CSR
~$ openssl req -new -sha256 -config ~/openssl/openssl.cnf -key tomcat-client.key -out tomcat-client.csr
~$ openssl x509 -req -sha256 -days 36520 -in tomcat-client.csr -signkey tomcat-client.key -CA ~/openssl/CA/certs/karun-tomcat-root-ca.crt -CAkey ~/openssl/CA/private/karun-tomcat-root-ca.key -CAcreateserial -out tomcat-client.crt 
~$ openssl pkcs12 -export -name karun-tomcat-client-cert -in tomcat-client.crt -out tomcat-client.p12 -inkey tomcat-client.key -CAfile ~/openssl/CA/certs/karun-tomcat-root-ca.crt -caname karun-root -chain
~$ (optional step) keytool -importkeystore -destkeystore tomcat-client.jks -srckeystore tomcat-client.p12 -srcstoretype pkcs12 -alias karun-tomcat-client-cert
~$ (optional step) keytool -import -alias root -keystore tomcat-client.jks -trustcacerts -file ~/openssl/CA/certs/karun-tomcat-root-ca.crt

步骤四:Tomcat更改

# Make this change in server.xml of tomcat server
<Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol"
           maxThreads="150" SSLEnabled="true" scheme="https" secure="true"
           keystoreFile="/opt/tomcat/openssl-certs/tomcat-server.jks"
           keystorePass="password"
           keyAlias="karun-tomcat-server-cert"
           truststoreFile="/opt/tomcat/openssl-certs/tomcat-server.jks"
           truststorePass="password"
           clientAuth="true" sslProtocol="TLS" />

步骤5:重新启动Tomcat服务器并检查启动时是否有错误日志
步骤6:上传客户端证书到浏览器
在您的浏览器中,例如: firefox, 导航到首选项->高级->证书->查看证书->您的证书
导入“tomcat-client.p12”
https://<tomcat ip>:8443/

References

http://pages.cs.wisc.edu/~zmiller/ca-howto/

http://www.area536.com/projects/be-your-own-certificate-authority-with-openssl/


完美。如果有人需要一个Java程序来测试这个东西,可以看看https://blog.behrang.org/2019/01/30/ssl-mutual-authentication-java.html。干杯 - Ketan

0

免责声明:仅在开发环境中使用自签名根证书。

有关更完整的概述(逐步):

在此示例中,我只是使用了 123456 作为密语。

Create a root certificate
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -sha256 -days 365

Create a key and CSR
openssl genrsa -out mycert.key 2048
openssl req -new -nodes -key mycert.key -out mycert.csr

Sign the CSR with your root certificate
openssl x509 -req -in mycert.csr -CA cert.pem -CAkey key.pem -CAcreateserial -out mycert.pem

Create a PKCS#12 certificate with the cert and key
openssl pkcs12 -export -out mycert.p12 -inkey mycert.key -in mycert.pem

Create a separate JKS keystore containing just the CA certificate (to use as the truststore)
keytool -import -alias my-ca -keystore truststore.jks -file cert.pem

truststore.jksmycert.p12 文件放置在您的 Tomcat 目录中。

<Connector port="8443" protocol="HTTP/1.1" SSLEnabled="true"
           maxThreads="150" scheme="https" secure="true"
           connectionTimeout="20000"
           keystoreFile="mycert.p12"
           keystoreType="PKCS12"
           keystorePass="123456"
           truststoreFile="truststore.jks"
           truststoreType="JKS"
           truststorePass="123456"
           clientAuth="true" sslProtocol="TLS" />

最后配置Tomcat conf/server.xml 以使双向TLS工作。


-1

我建议您尝试以下步骤:

  1. 按照您在8443上配置的方式启动容器。
  2. 使用-Djavax.net.debug=SSL运行客户端应用程序。

该命令将输出大量信息。您需要检查的是,服务器是否呈现了一个接受互相认证的CA列表。如果列出的CA不包含您的证书,则客户端将无法找到与服务器匹配的证书。

使用openssl命令's_client'可以使此过程更加简单。

openssl s_client -connect localhost:8443 -showcerts

这将格式化一些信息,这些信息在调试时具有不可估量的价值。

如果服务器没有提供“可接受”的CA列表,则在生成证书集时必须进行一些魔法操作。

让我知道你发现了什么,我希望能引导你朝正确的方向前进。

OP添加了额外的信息

好的,所以下面是你面临的一个问题:

---
no peer certificate available
---
No client certificate CA names sent
---
SSL handshake has read 7 bytes and written 263 bytes
---

立即跳出两件事情

  1. 服务器没有对等证书
  2. 没有列出客户端CA名称

所以对于(1):

  1. 确保您的密钥库实际上具有别名“tomcat”,使用keytool。
  2. 用相同的值将keystorePassword和keyPassword属性添加到连接器中,以确保tomcat中的存储/键密码设置正确。为了清晰起见,{{link1:Tomcat 7文档}}指示如果未设置,则keystorePass将默认为keyPass。如果您的keyPass和keystorePass相同,请仅设置keyPass属性。

现在对于(2),我们真的需要先让(1)工作-因此让它运行起来,然后我们将看到我们在那一点上。


好的,我执行了:openssl s_client -connect localhost:8443 -showcerts然后得到的回应是:

CONNECTED(00000003) 140506026415944:error:140770FC:SSL routines:SSL23_GET_SERVER_HELLO:unknown protocol:s23_clnt.c:766:

没有可用的对端证书

没有发送客户端证书CA名称

SSL握手已经读取了7个字节并写入263个字节

新建,(无), 密码是(无) 安全重协商不被支持 压缩:无 扩展:无

- wbj
另外,如果评论使用与问题或答案相同的Markdown语法,那就太棒了。唉,对于上面糟糕的格式化表示抱歉。 - wbj
@wbj 请编辑您的问题并在其中添加注释,然后将输出放入其中。最佳选项:打开文本编辑器,粘贴进去,然后无论缩进如何,在每行开头添加4个空格。 - Dave G
你在这方面有进展了吗? - Dave G
谢谢你的提问和帮助-是的,我已经让它正常工作了。我正在整理所有步骤的详细说明。这个教程非常有用:http://java-notes.com/index.php/two-way-ssl-on-tomcat - wbj
太棒了!很高兴听到这个消息。 - Dave G

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接