Java 与 Tomcat 7 的相互认证

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/27362588/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 04:13:53  来源:igfitidea点击:

Mutual authentication with Tomcat 7

javatomcatsslmutual-authentication

提问by wbj

I'm trying to set up a Java web service running in Tomcat 7 to use mutual (2-way) authentication. It seems like no matter what I do, connecting to the service on the secure port isn't working.

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

Here's what I did to create certificates and keystores and such:

这是我为创建证书和密钥库等所做的工作:

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

Here's the connector in the server.xml file:

这是 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"/>

The tomcat-users.xml file looks like this:

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>

The following are set on startup:

启动时设置如下:

-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

Finally, I copied the client.p12 file to my client machine, and imported it into Firefox's client certificates.

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

First problem: When I hit an endpoint on my service (example - https://my.server.com:8443/test) from Firefox, I get the response "Secure Connection Failed". SSL received a record that exceeded the maximum permissible length. (Error code: ssl_error_rx_record_too_long)

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

Second problem: I don't really want to run this connector on port 8443. I want to run it on port 7800 (which is our company standard for HTTPS). When I change the port on the Connector to 7800 and try to hit the endpoint (example - https://my.server.com:7800/test) then it never resolves the page.

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

So, somewhere I'm obviously missing a crucial piece. Can anyone see my error?

所以,在某个地方我显然错过了一个关键部分。谁能看到我的错误?

UPDATE: after feedback from @Dave G

更新:@Dave G 反馈后

Running the command:

运行命令:

openssl s_client -connect localhost:8443 -showcerts

produces the following output:

产生以下输出:

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
---

I also added -Djavax.net.debug=SSL to the startup. This generates the following in the beginning of the catalina.out file:

我还在启动中添加了 -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 Hymanson, OU=unit, O=org, L=Springfield, ST=MO, C=US
  Issuer:  CN=William Hymanson, 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

And then a LOT of:

然后很多:

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

采纳答案by wbj

Ok - after digging a lot more, I finally got this working. Much thanks to @Dave G and this tutorial: Configuring two-way SSL authentication on Tomcatfrom which most of these instructions are paraphrased.

好的 - 经过更多的挖掘,我终于开始工作了。非常感谢@Dave G 和本教程:在 Tomcat 上配置双向 SSL 身份验证,其中大部分说明都来自其中。

Generally, the steps to get mutual authentication functional are as follows:

通常,实现相互认证功能的步骤如下:

  1. Create a certificate for the tomcat server. The client has to trust this certificate.
  2. Create a keystore for the tomcat server, and import the server certificate into it.
  3. Create a certificate for the client. The server has to trust this certificate.
  4. Import the clientcertificate into the serverkeystore
  5. Update the tomcat server.xml file with the correct Connector XML.
  1. 为 tomcat 服务器创建证书。客户端必须信任此证书。
  2. 为tomcat服务器创建一个keystore,并将服务器证书导入其中。
  3. 为客户端创建证书。服务器必须信任此证书。
  4. 客户端证书导入服务器密钥库
  5. 使用正确的连接器 XML 更新 tomcat server.xml 文件。

The above steps are necessary on the server. Once completed, to set up the client, do the following:

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

  1. Copy the client certificate from the server to the client.
  2. Use the client certificate when communicating with the server (this process varies with the nature of the client application).
  1. 将客户端证书从服务器复制到客户端。
  2. 与服务器通信时使用客户端证书(此过程因客户端应用程序的性质而异)。

For the certificate configuration, I executed the following on the server machine:

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

# 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

Certificates should now be set up appropriately. The next step is to configure your connector in the tomcat server.xml. Add a connector element that looks like this:

现在应该适当地设置证书。下一步是在 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"/>      

Note that in the above XML:

请注意,在上面的 XML 中:

  1. The "port" attribute can be whatever you want.
  2. The "keystoreFile" and "truststoreFile" attributes should be full paths. Tomcat does not look in the same directory as server.xml by default.
  3. The "keystorePass" and "truststorePass" attributes should match the (password) value you used in the creation of the tomcat.keystore file.
  4. The "clientAuth" attribute mustbe set to "true". This is what triggers mutual authentication.
  1. “端口”属性可以是任何你想要的。
  2. “keystoreFile”和“truststoreFile”属性应该是完整路径。默认情况下,Tomcat 不会在与 server.xml 相同的目录中查找。
  3. “keystorePass”和“truststorePass”属性应与您在创建 tomcat.keystore 文件时使用的(密码)值匹配。
  4. “clientAuth”属性必须设置为“true”。这就是触发相互身份验证的原因。

Additionally, in the server.xml, ensure that you DO NOThave an AprLifecycleListner defined. The XML for that listener will look something like this:

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

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

That element should be delete/commented out. The AprLifecycleListener does not get configured the same way as described above, and will not work with these instructions.

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

Restart tomcat. The server configuration should be complete.

重启tomcat。服务器配置应该是完整的。

I tested my work using Firefox, because it's easy to add client certificates to it. Open up Firefox and try to connect to an endpoint of your tomcat service on the port defined in your connector.

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

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

When you do this, you should get the standard alert from Firefox about an untrusted connection because we created a self-signed certificate for our Tomcat server. Add an exception for the certificate so that our client (Firefox) trusts our server (Tomcat).

执行此操作时,您应该会收到来自 Firefox 的关于不可信连接的标准警报,因为我们为 Tomcat 服务器创建了自签名证书。为证书添加一个例外,以便我们的客户端 (Firefox) 信任我们的服务器 (Tomcat)。

Once you've added the exception, you should get a "Secure Connection Failed" message. The error code is "ssl_error_bad_cert_alert". This confirms that our Tomcat server is requesting authentication from the client. The request is failing because we have not configured Firefox to send our trusted client certificate yet.

添加异常后,您应该会收到“安全连接失败”消息。错误代码是“ssl_error_bad_cert_alert”。这证实了我们的 Tomcat 服务器正在请求来自客户端的身份验证。请求失败,因为我们还没有配置 Firefox 来发送我们的可信客户端证书。

To configure Firefox, we need to do a little more magic:

要配置 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-----");
  }
}

Compile the java file with the following command:

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

javac DumpPrivateKey.java

Now we're going to use this little utility to extract a key from the client.keystore file we create above. Copy the client.keystore and client.cer files into the same directory as your DumpPrivateKey class. Execute the following:

现在我们将使用这个小实用程序从我们上面创建的 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

Note that in the above code, (password) should be the password you used to create the client.keystore.

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

Open up Firefox preferences. Click on the "Certificates" tab. Click on the "View Certificates" button. Click on the "Your Certificates" tab.

打开 Firefox 首选项。单击“证书”选项卡。单击“查看证书”按钮。单击“您的证书”选项卡。

Click on the "Import" button and browse to the "client.p12" file that was created previously. You should be prompted to enter the password for the client certificate.

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

Assuming the "client.p12" was imported successfully, you can now refresh you Firefox page, and you should get a successful response from your Tomcat server endpoint.

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

回答by Dave G

I would try the following steps

我会尝试以下步骤

  1. Spool up container as you have configured on 8443.
  2. Run up your client application with -Djavax.net.debug=SSL
  1. 如您在 8443 上配置的那样后台处理容器。
  2. 使用 -Djavax.net.debug=SSL 运行您的客户端应用程序

That command will spool out PILES of information. What you need to check on that is that the server is presenting a list of CA's that it will accept for mutual authentication. If the listed CAs do not contain your certificate then the client will have no idea how to locate a match for the server.

该命令将输出大量信息。您需要检查的是服务器是否提供了一个 CA 列表,它将接受相互身份验证。如果列出的 CA 不包含您的证书,则客户端将不知道如何为服务器找到匹配项。

This can be made much easier using the openssl command 's_client'

使用 openssl 命令“s_client”可以更容易地做到这一点

openssl s_client -connect localhost:8443 -showcerts

That will format out some information that can be incalculable in their value of debugging this.

这将格式化一些信息,这些信息在调试这个方面的价值是无法估量的。

If the server does not present a list of "acceptable" CAs you will have to do some magic when you produce your certificate set.

如果服务器没有提供“可接受的”CA 列表,您在生成证书集时将不得不做一些魔术。

Let me know what you find out and I can hopefully steer you in the right direction.

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

OP added additional information

OP 添加了附加信息

Ok so the following is a bit of a problem for you:

好的,以下对您来说有点问题:

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

Two things jump out immediately

两件事立即跳出来

  1. The server doesn't have a peer certificate
  2. There are no client CA names listed
  1. 服务器没有对等证书
  2. 没有列出客户端 CA 名称

So for (1):

所以对于(1):

  1. make sure your keystore in fact does have the alias 'tomcat' in it using keytool.
  2. the store/key password stuff in tomcat is goofy. for sanity's sake, add both keystorePassword and keyPassword attributes to your connector with identical values. The documentation for Tomcat 7indicates that the keystorePass will default to the keyPass if not set. If your keyPass and keystorePass are the same set the keyPass attribute only.
  1. 使用 keytool 确保您的密钥库实际上确实有别名“tomcat”。
  2. tomcat 中的存储/密钥密码内容很愚蠢。为了理智起见,将 keystorePassword 和 keyPassword 属性添加到具有相同值的连接器。Tomcat 7文档表明,如果未设置,keystorePass 将默认为 keyPass。如果您的 keyPass 和 keystorePass 相同,则仅设置 keyPass 属性。

Now for (2) we really need to have (1) working first - so get that up and running and we'll see where we are at that point.

现在对于 (2) 我们真的需要让 (1) 首先工作 - 所以启动并运行它,我们将看到我们在那个点上的位置。

回答by Dmytro Ozarkiv

@wbj, export of PrivateKeyEntry from JKS to PKCS #12 can be done much more easier:

@wbj,可以更轻松地将 PrivateKeyEntry 从 JKS 导出到 PKCS #12:

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

Cheers.

干杯。

回答by karun

It took me some time to get it working correctly using Openssl certificates, drafting my notes so that it may help others visiting this page.

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

Step 1: Create your own root CA

第 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

Step 2: Create Tomcat Server's Key Pair

第二步:创建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 

Step 3: Create Client Side Key Pair

第 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

Step 4: Tomcat Changes

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

Step 5: Restart Tomcat Server && check logs to ensure no errors at bootup

第 5 步:重新启动 Tomcat Server && 检查日志以确保启动时没有错误

Step 6: Upload Client cert to browser

第 6 步:将客户端证书上传到浏览器

In your browser, eg: firefox, navigate Preferences -> Advanced -> Certificate -> View Certificates -> Your Certificates

在您的浏览器中,例如:firefox,导航首选项 -> 高级 -> 证书 -> 查看证书 -> 您的证书

Import "tomcat-client.p12"

导入“tomcat-client.p12”

https://<tomcat ip>:8443/

References

参考

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

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

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

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