Java:使用自签名证书的 SSL 客户端身份验证

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4421536/
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-14 17:18:08  来源:igfitidea点击:

Java: SSL Clientside Authentication with self-signed certificates

javasslself-signed

提问by Chris

I'm trying to secure a connection from a Java Client/Server application that communicates over the Internet. My idea was to use SSL sockets with self-signed certificates and client authentication. I did the following:

我正在尝试保护来自通过 Internet 通信的 Java 客户端/服务器应用程序的连接。我的想法是使用带有自签名证书和客户端身份验证的 SSL 套接字。我做了以下事情:

  • Server: Keystore containing new self-signed certificate. keytool -genkey -kelalg RSA ...
  • Client: Keystore containing new self-signed certificate. keytool -genkey -kelalg RSA ...
  • Server: Truststore containing exported client certificate (from bullet point above). keytool -exportto export client certificate and keytool -import -v -trustcacertsto import it into the server's truststore
  • Client: Truststore containing exported server certificate (from first bullet point). keytool -exportto export server certificate and keytool -import -v -trustcacertsto import it into the client's truststore
  • 服务器:包含新自签名证书的密钥库。 keytool -genkey -kelalg RSA ...
  • 客户端:包含新自签名证书的密钥库。 keytool -genkey -kelalg RSA ...
  • 服务器:包含导出的客户端证书的信任库(来自上面的要点)。keytool -export导出客户端证书并将keytool -import -v -trustcacerts其导入服务器的信任库
  • 客户端:包含导出的服务器证书的信任库(从第一个要点开始)。keytool -export导出服务器证书并将keytool -import -v -trustcacerts其导入客户端的信任库

Trust- and keystores are attached correctly to the server/client. I can see the certificates being loaded (SSL debug information). But the whole thing doesn't work. During the SSL handshake, I get the following error (SSL debug information):

信任和密钥库正确附加到服务器/客户端。我可以看到正在加载的证书(SSL 调试信息)。但整件事都行不通。在 SSL 握手期间,我收到以下错误(SSL 调试信息):

main, WRITE: TLSv1 Handshake, length = 897
main, READ: TLSv1 Handshake, length = 141
*** Certificate chain
***
main, SEND TLSv1 ALERT:  fatal, description = bad_certificate
main, WRITE: TLSv1 Alert, length = 2
main, called closeSocket()
main, handling exception: javax.net.ssl.SSLHandshakeException: null cert chain
main, IOException in getSession():  javax.net.ssl.SSLHandshakeException: null cert chain
javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated
    at com.sun.net.ssl.internal.ssl.SSLSessionImpl.getPeerCertificateChain(Unknown Source)
    at sslsocket.Server.getClientDistinguishedName(Server.java:86)
    at sslsocket.Server.main(Server.java:37)

When I disable client side authentication, it works flawlessly.

当我禁用客户端身份验证时,它可以完美运行。

I'd really appreciate some help. Thank you very much!

我真的很感激一些帮助。非常感谢!

Below you find the full, but anonymized output from the server:

您可以在下面找到来自服务器的完整但匿名的输出:

Initializing SSL
***
found key for : server
chain [0] = [
[
  Version: V3
  Subject: CN=xxxxxx Server, OU=communication, O=xxxxxx, L=Zuerich, ST=ZH, C=CH
  Signature Algorithm: SHA1withRSA, OID = 1.2.840.113549.1.1.5

  Key:  Sun RSA public key, 1024 bits
  modulus: 9487726xxxxxx15617628447913191
  public exponent: 65537
  Validity: [From: Thu Dec 09 17:04:05 CET 2010,
               To: Wed Jul 03 18:04:05 CEST 2109]
  Issuer: CN=xxxxxx Server, OU=communication, O=xxxxxx, L=Zuerich, ST=ZH, C=CH
  SerialNumber: [    4dxxxxxx5]

]
  Algorithm: [SHA1withRSA]
  Signature:
0000: 6F 06 1D EA E9 DC 5B 5D   EC EB 33 D4 47 01 94 1A  o.....[]..3.G...
xxxxxx
0070: 99 78 C4 31 5F 84 8F 7B   C1 2F 10 A1 9F 50 72 A1  .x.1_..../...Pr.

]
***
adding as trusted cert:
  Subject: CN=xxxxxx Client, OU=communication, O=xxxxxx, L=Zuerich, ST=ZH, C=CH
  Issuer:  CN=xxxxxx Client, OU=communication, O=xxxxxx, L=Zuerich, ST=ZH, C=CH
  Algorithm: RSA; Serial number: 0x4xxxxxx0
  Valid from Thu Dec 09 17:06:56 CET 2010 until Wed Jul 03 18:06:56 CEST 2109

trigger seeding of SecureRandom
done seeding SecureRandom
Opening socket
Waiting for clients...
Allow unsafe renegotiation: false
Allow legacy hello messages: true
Is initial handshake: true
Is secure renegotiation: false
matching alias: server
main, called closeSocket()
Allow unsafe renegotiation: false
Allow legacy hello messages: true
Is initial handshake: true
Is secure renegotiation: false
[read] MD5 and SHA1 hashes:  len = 3
0000: 01 03 01                                           ...
[read] MD5 and SHA1 hashes:  len = 98
0000: 00 3C 00 00 00 20 00 00   04 01 00 80 00 00 05 00  .<... ..........
xxxxxx
0060: 26 51                                              &Q
main, READ:  SSL v2, contentType = Handshake, translated length = 75
*** ClientHello, TLSv1
RandomCookie:  GMT: 1292088238 bytes = { 223,xxxxxx, 81 }
Session ID:  {}
Cipher Suites: [SSL_RSA_WITH_RC4_128_MD5, SSL_RSA_WITH_RC4_128_SHA, TLS_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_DSS_WITH_AES_128_CBC_SHA, SSL_RSA_WITH_3DES_EDE_CBC_SHA, SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA, SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA, SSL_RSA_WITH_DES_CBC_SHA, SSL_DHE_RSA_WITH_DES_CBC_SHA, SSL_DHE_DSS_WITH_DES_CBC_SHA, SSL_RSA_EXPORT_WITH_RC4_40_MD5, SSL_RSA_EXPORT_WITH_DES40_CBC_SHA, SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA, SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA, TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
Compression Methods:  { 0 }
***
%% Created:  [Session-1, SSL_RSA_WITH_RC4_128_MD5]
*** ServerHello, TLSv1
RandomCookie:  GMT: 1292088238 bytes = { 222,xxxxxx, 241 }
Session ID:  {77,xxxxxx, 235}
Cipher Suite: SSL_RSA_WITH_RC4_128_MD5
Compression Method: 0
Extension renegotiation_info, renegotiated_connection: <empty>
***
Cipher suite:  SSL_RSA_WITH_RC4_128_MD5
*** Certificate chain
chain [0] = [
[
  Version: V3
  Subject: CN=xxxxxx Server, OU=communication, O=xxxxxx, L=Zuerich, ST=ZH, C=CH
  Signature Algorithm: SHA1withRSA, OID = 1.2.840.113549.1.1.5

  Key:  Sun RSA public key, 1024 bits
  modulus: 948772xxxxxx17628447913191
  public exponent: 65537
  Validity: [From: Thu Dec 09 17:04:05 CET 2010,
               To: Wed Jul 03 18:04:05 CEST 2109]
  Issuer: CN=xxxxxx Server, OU=communication, O=xxxxxx, L=Zuerich, ST=ZH, C=CH
  SerialNumber: [    4d00fdf5]

]
  Algorithm: [SHA1withRSA]
  Signature:
0000: 6F 06 1D EA E9 DC 5B 5D   EC EB 33 D4 47 01 94 1A  o.....[]..3.G...
xxxxxx
0070: 99 78 C4 31 5F 84 8F 7B   C1 2F 10 A1 9F 50 72 A1  .x.1_..../...Pr.

]
***
*** CertificateRequest
Cert Types: RSA, DSS
Cert Authorities:
<CN=xxxxxx Client, OU=communication, O=xxxxxx, L=Zuerich, ST=ZH, C=CH>
*** ServerHelloDone
[write] MD5 and SHA1 hashes:  len = 897
0000: 02 00 00 4D 03 01 4D 04   B4 AE DE E4 AF 62 FA 48  ...M..M......b.H
0xxxxxx
0380: 00                                                 .
main, WRITE: TLSv1 Handshake, length = 897
main, READ: TLSv1 Handshake, length = 141
*** Certificate chain
***
main, SEND TLSv1 ALERT:  fatal, description = bad_certificate
main, WRITE: TLSv1 Alert, length = 2
main, called closeSocket()
main, handling exception: javax.net.ssl.SSLHandshakeException: null cert chain
main, IOException in getSession():  javax.net.ssl.SSLHandshakeException: null cert chain
javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated
    at com.sun.net.ssl.internal.ssl.SSLSessionImpl.getPeerCertificateChain(Unknown Source)
    at sslsocket.Server.getClientDistinguishedName(Server.java:86)
    at sslsocket.Server.main(Server.java:37)

采纳答案by user207421

The first SSL trace appears to be part of the second one, displayed at the server. Please confirm.

第一个 SSL 跟踪似乎是第二个的一部分,显示在服务器上。请确认。

The second trace shows that the server asked for an RSA or DSS certificate signed by 'CN=xxxxxx Client, OU=communication, O=xxxxxx, L=Zuerich, ST=ZH, C=CH', and the client replied by sending an empty certificate chain. That can only mean either that the client's keystore doesn't have such a cert in it or that the client wasn't using the correct keystore.

第二个跟踪显示服务器请求由 'CN=xxxxxx Client, OU=communication, O=xxxxxx, L=Zuerich, ST=ZH, C=CH' 签名的 RSA 或 DSS 证书,并且客户端通过发送一个回复空证书链。这只能意味着客户端的密钥库中没有这样的证书,或者客户端没有使用正确的密钥库。