与 Tomcat 7 进行相互身份验证

2022-09-02 11:59:03

我正在尝试设置一个在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

下面是服务器.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>

答案 1

好吧 - 在挖掘了更多之后,我终于开始工作了。非常感谢@Dave G 和本教程:在 Tomcat 上配置双向 SSL 身份验证,其中大部分指令都是从中转述的。

通常,获取相互身份验证功能的步骤如下:

  1. 为 tomcat 服务器创建证书。客户端必须信任此证书。
  2. 为 tomcat 服务器创建一个密钥库,并将服务器证书导入其中。
  3. 为客户端创建证书。服务器必须信任此证书。
  4. 客户机证书导入服务器密钥库
  5. 使用正确的连接器 XML 更新 tomcat 服务器.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 服务器中配置连接器.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. “密钥库文件”和“信任库文件”属性应该是完整路径。默认情况下,Tomcat 不会与服务器.xml在同一目录中查找。
  3. “keystorePass”和“truststorePass”属性应与您在创建 tomcat.keystore 文件时使用的(密码)值匹配。
  4. “clientAuth”属性必须设置为“true”。这就是触发相互身份验证的原因。

此外,在服务器.xml中,请确保您没有定义 AprLifecycleListner。该侦听器的 XML 将如下所示:

<Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />

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

重新启动雄猫。服务器配置应完整。

我使用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 和客户机.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

请注意,在上面的代码中,(密码)应该是您用于创建 client.keystore 的密码。

打开火狐浏览器的偏好设置。单击“证书”选项卡。单击“查看证书”按钮。单击“您的证书”选项卡。

单击“导入”按钮,然后浏览到之前创建的“client.p12”文件。系统应提示您输入客户端证书的密码。

假设“client.p12”已成功导入,您现在可以刷新Firefox页面,并且您应该从Tomcat服务器端点获得成功的响应。


答案 2

@wbj,将 PrivateKeyEntry 从 JKS 导出到 PKCS #12 可以更容易得多:

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

干杯。