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

Posted

技术标签:

【中文标题】Java:使用自签名证书的 SSL 客户端身份验证【英文标题】:Java: SSL Clientside Authentication with self-signed certificates 【发布时间】:2011-05-24 05:17:21 【问题描述】:

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

服务器:包含新自签名证书的密钥库。 keytool -genkey -kelalg RSA ... 客户端:包含新自签名证书的密钥库。 keytool -genkey -kelalg RSA ... 服务器:包含导出客户端证书的信任库(来自上面的要点)。 keytool -export 导出客户端证书,keytool -import -v -trustcacerts 将其导入服务器的信任库 客户端:包含导出的服务器证书的信任库(从第一个要点开始)。 keytool -export 导出服务器证书,keytool -import -v -trustcacerts 将其导入客户端的信任库

信任库和密钥库已正确附加到服务器/客户端。我可以看到正在加载的证书(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)

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

非常感谢一些帮助。非常感谢!

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

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)

【问题讨论】:

如果您将客户端的自签名证书添加到其自己的信任库会发生什么? 没有任何区别。我仍然遇到同样的错误。 如果我的服务器信任库包含客户端证书并且我的客户端信任库包含服务器证书,它适用于我。 @GregS,实施您的第一条评论无济于事。 【参考方案1】:

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

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

【讨论】:

感谢您的回复。我重新检查了客户端密钥库,尽管我认为我已经这样做了 100 次。无论如何,我注意到密钥库只包含公钥而不包含客户端证书的私钥。不知怎的,我一定搞砸了。 这很有帮助,它澄清了 CA 签署的证书或私钥或整个密钥库必须丢失才能使此过程在这一点上失败*** Certificate chain ...对我来说问题是SSLSocketFactory.getSocketFactory() 自动加载-Djavax.net.ssl.trustStore 设置的信任库,但它不会自动加载-Djavax.net.ssl.keyStore 设置的密钥库!!所以我必须明确地这样做:smartjava.org/content/client-certificates-httpclient-4 只有默认的 SSLContext 服从 javax.ney.ssl.keyStore。出于某种原因,您使用 getInstance() 自己构建的那些不会。

以上是关于Java:使用自签名证书的 SSL 客户端身份验证的主要内容,如果未能解决你的问题,请参考以下文章

iOS 和 SSL:无法验证自签名服务器证书

Tomcat 服务器/客户端自签名 SSL 证书

用openssl生成的ssl证书和付费的有啥区别

手动验证 SSL 自签名证书签名 - javascript [重复]

Jboss EAP 7 - REST API客户端的动态SSL身份验证

相互证书身份验证失败并出现错误 403.16