与 Tomcat 7 的相互身份验证
Posted
技术标签:
【中文标题】与 Tomcat 7 的相互身份验证【英文标题】:Mutual authentication with Tomcat 7 【发布时间】:2015-02-06 09:06:47 【问题描述】:我正在尝试设置在 Tomcat 7 中运行的 Java Web 服务以使用相互(2 路)身份验证。似乎无论我做什么,都无法连接到安全端口上的服务。
这是我为创建证书和密钥库等所做的:
//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 标准)上运行它。当我将连接器上的端口更改为 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 服务器在我的笔记本电脑上运行的 virtualbox VM 上运行 Centos 6.4。 Firefox 是 32.0.3 版,直接在我的笔记本电脑 (Mac) 上运行。 【参考方案1】:好的 - 在挖掘了更多之后,我终于得到了这个工作。非常感谢@Dave G 和本教程:Configuring two-way SSL authentication on Tomcat,其中大部分说明都被改写了。
一般来说,获得相互认证功能的步骤如下:
-
为 tomcat 服务器创建一个证书。客户端必须信任此证书。
为 tomcat 服务器创建一个密钥库,并将服务器证书导入其中。
为客户端创建证书。服务器必须信任此证书。
将客户端证书导入服务器密钥库
使用正确的连接器 XML 更新 tomcat server.xml 文件。
以上步骤在服务器上是必需的。完成后,要设置客户端,请执行以下操作:
-
将客户端证书从服务器复制到客户端。
与服务器通信时使用客户端证书(此过程因客户端应用程序的性质而异)。
对于证书配置,我在服务器机器上执行了以下操作:
# 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 中:
-
“端口”属性可以是任何你想要的。
“keystoreFile”和“truststoreFile”属性应该是完整路径。默认情况下,Tomcat 与 server.xml 不在同一目录中。
“keystorePass”和“truststorePass”属性应与您在创建 tomcat.keystore 文件时使用的(密码)值匹配。
“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 服务器端点获得成功的响应。
【讨论】:
我认为您不需要“将客户端证书导入服务器密钥库” - 您需要在服务器的受信任存储中拥有颁发客户端证书的 CA,而不是证书本身(除非它是自签名的) @TheFiddlerWins 之前生成了两条命令怎么能不自签名呢? 这太完美了!我摸不着头脑该怎么办,这个分步教程帮助了我很多。谢谢 这 - 拯救了我的一天 - 特别是AprLifecycleListner
部分!谢谢!!
将客户端 cer 导入服务器密钥库时,是否需要指定与导出时相同的 clientAlias?【参考方案2】:
@wbj,将 PrivateKeyEntry 从 JKS 导出到 PKCS #12 可以更容易地完成:
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
第 3 步:创建客户端密钥对
~$ 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
第 4 步: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/
参考文献
http://pages.cs.wisc.edu/~zmiller/ca-howto/
http://www.area536.com/projects/be-your-own-certificate-authority-with-openssl/
【讨论】:
完美。如果有人在使用 Java 程序来测试这个东西,那么可以看到blog.behrang.org/2019/01/30/ssl-mutual-authentication-java.html。干杯【参考方案4】:免责声明:仅在开发环境中使用自签名根证书。
如需更完整的概述(逐步):
在此示例中,我只是使用 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.jks
和mycert.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 工作。
【讨论】:
【参考方案5】:我会尝试以下步骤
-
按照您在 8443 上配置的方式启动容器。
使用 -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
---
两个东西立刻跳出来
-
服务器没有对等证书
没有列出客户端 CA 名称
所以对于(1):
-
使用 keytool 确保您的密钥库中确实有别名“tomcat”。
tomcat 中的存储/密钥密码东西很傻。为了理智起见,将 keystorePassword 和 keyPassword 属性添加到具有相同值的连接器。 documentation for Tomcat 7 表示如果未设置,keystorePass 将默认为 keyPass。如果您的 keyPass 和 keystorePass 相同,则仅设置 keyPass 属性。
现在,对于 (2),我们确实需要先让 (1) 工作 - 所以启动并运行它,然后我们会看到我们在那个时候的位置。
【讨论】:
好的,我执行了:openssl s_client -connect localhost:8443 -showcerts 响应是:CONNECTED(00000003) 140506026415944:error:140770FC:SSLroutines:SSL23_GET_SERVER_HELLO:unknown protocol:s23_clnt.c:766 : --- 没有可用的对等证书 --- 没有发送客户端证书 CA 名称 --- SSL 握手已读取 7 个字节并写入 263 个字节 --- 新,(无),密码为(无)不支持安全重新协商 压缩: NONE 扩展: NONE --- 另外,如果 cmets 使用相同的降价作为问题或答案,那就太棒了。叹。抱歉上面的格式不好。 @wbj 请用注释编辑您的问题并将输出放在那里。最佳选择:打开文本编辑器,粘贴,然后在每行的开头添加 4 个空格,不管缩进。 您对此有进一步了解吗? 感谢您的询问,感谢您的帮助 - 是的,我确实让它工作了。我正在整理我为实现它所采取的所有步骤。这个教程很有帮助:java-notes.com/index.php/two-way-ssl-on-tomcat以上是关于与 Tomcat 7 的相互身份验证的主要内容,如果未能解决你的问题,请参考以下文章
Undertow 是不是具有与 Tomcat Web 身份验证等效的 Web 层身份验证功能?
基于相互证书的身份验证能否与基于 SSL 的基本身份验证共存,用于同一应用程序中的不同路径?
Spring Security 3.1.4 taglib 授权/身份验证不适用于 Tomcat 7 上 JSF 2.2 中的角色层次结构