Java 存在根 CA 证书 - 出现 SSL 握手异常

Java Root CA Certificate present - getting SSL Handshake Exception

我编写了一个 java JAX-WS 网络服务客户端。当我尝试使用 public CA 签名证书访问服务器时,出现 SSL 握手异常:

com.sun.xml.internal.ws.client.ClientTransportException: HTTP transport error: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

我进一步调查并打开了 JVM 的网络跟踪,发现服务器 public CA 签名证书的颁发者是:

Issuer: CN=Symantec Class 3 Secure Server SHA256 SSL CA, OU=Symantec Trust Network, O=Symantec Corporation, C=US

我已验证此颁发者的根 CA 证书是:

CN=VeriSign Universal Root Certification Authority, OU="(c) 2008 VeriSign, Inc. - For authorized use only", OU=VeriSign Trust Network, O="VeriSign, Inc.", C=US

而且我还通过日志跟踪验证了该证书确实已加载。

这是 SSL 日志跟踪的一部分:

keyStore is : 
keyStore type is : jks
keyStore provider is : 

init keystore
init keymanager of type SunX509
trustStore is: C:\Program Files\Java\jdk1.7.0_79\jre\lib\security\cacerts
trustStore type is : jks
trustStore provider is : 
init truststore

[ omitted]

        adding as trusted cert:
      Subject: CN=VeriSign Universal Root Certification Authority, OU="(c) 2008 VeriSign
, Inc. - For authorized use only", OU=VeriSign Trust Network, O="VeriSign, Inc.", C=US
      Issuer:  CN=VeriSign Universal Root Certification Authority, OU="(c) 2008 VeriSign
, Inc. - For authorized use only", OU=VeriSign Trust Network, O="VeriSign, Inc.", C=US
      Algorithm: RSA; Serial number: 0x401ac46421b31321030ebbe4121ac51d
      Valid from Tue Apr 01 17:00:00 PDT 2008 until Tue Dec 01 15:59:59 PST 2037
[ omitted]

trigger seeding of SecureRandom
done seeding SecureRandom
Ignoring unavailable cipher suite: TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
Ignoring unavailable cipher suite: TLS_DHE_RSA_WITH_AES_256_CBC_SHA
Ignoring unavailable cipher suite: TLS_ECDH_RSA_WITH_AES_256_CBC_SHA
Ignoring unsupported cipher suite: TLS_DHE_DSS_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_DHE_DSS_WITH_AES_256_CBC_SHA256
Ignoring unsupported cipher suite: TLS_DHE_RSA_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_DHE_RSA_WITH_AES_256_CBC_SHA256
Ignoring unsupported cipher suite: TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
Ignoring unsupported cipher suite: TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384
Ignoring unsupported cipher suite: TLS_RSA_WITH_AES_256_CBC_SHA256
Ignoring unavailable cipher suite: TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
Ignoring unsupported cipher suite: TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
Ignoring unavailable cipher suite: TLS_DHE_DSS_WITH_AES_256_CBC_SHA
Ignoring unsupported cipher suite: TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384
Ignoring unsupported cipher suite: TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256
Ignoring unavailable cipher suite: TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA
Ignoring unavailable cipher suite: TLS_RSA_WITH_AES_256_CBC_SHA
Ignoring unsupported cipher suite: TLS_RSA_WITH_AES_128_CBC_SHA256
Allow unsafe renegotiation: false
Allow legacy hello messages: true
Is initial handshake: true
Is secure renegotiation: false
main, setSoTimeout(0) called
%% No cached client session
*** ClientHello, TLSv1
RandomCookie:  GMT: 1468680588 bytes = 
Session ID:  {}
Cipher Suites: [ ... ]
Compression Methods:  { 0 }
Extension elliptic_curves, curve names:
Extension ec_point_formats, formats: [uncompressed]
Extension server_name, server_name: [host_name: redacted]
***
[write] MD5 and SHA1 hashes:  len = 181

[omitted]

*** ServerHello, TLSv1
RandomCookie:  GMT: 1524806833 
Session ID:  
Cipher Suite: TLS_DHE_RSA_WITH_AES_128_CBC_SHA
Compression Method: 0
Extension renegotiation_info, renegotiated_connection: <empty>
Extension server_name, server_name: 
***
%% Initialized:  [Session-1, TLS_DHE_RSA_WITH_AES_128_CBC_SHA]
** TLS_DHE_RSA_WITH_AES_128_CBC_SHA
[read] MD5 and SHA1 hashes:  len = 85

[omitted]

*** Certificate chain
chain [0] = [
[
  Version: V3
  Subject: CN=redacted, OU=redacted, O=redacted, L=redacted, ST=redacted, C=US
  Signature Algorithm: SHA256withRSA, OID = 1.2.840.113549.1.1.11

  Key:  Sun RSA public key, 2048 bits
  modulus:
  public exponent: 65537
  Validity: [From: Sun Oct 16 17:00:00 PDT 2016,
               To: Thu Nov 02 16:59:59 PDT 2017]
  Issuer: CN=Symantec Class 3 Secure Server SHA256 SSL CA, 
          OU=Symantec Trust Network, O=Symantec Corporation, C=US
  SerialNumber: [ ... ]

Certificate Extensions: 9
[1]: ObjectId: 1.3.6.1.4.1.11129.2.4.2 Criticality=false
Extension unknown: DER encoded OCTET string =

[omitted]

***
%% Invalidated:  [Session-1, TLS_DHE_RSA_WITH_AES_128_CBC_SHA]
main, SEND TLSv1 ALERT:  fatal, description = certificate_unknown
main, WRITE: TLSv1 Alert, length = 2
[Raw write]: length = 7
0000: 15 03 01 00 02 02 2E                               .......
main, called closeSocket()
main, handling exception: javax.net.ssl.SSLHandshakeException:

有人对可能导致此问题的原因有任何建议吗?

信任树看起来像

root-> VeriSign Universal Root Certification Authority
chain-> Symantec Class 3 Secure Server SHA256 SSL CA
leaf-> somewebsite

您可以有多个链,也可以没有链。 浏览器倾向于在其信任库中包含根证书和流行的链证书。

但看起来 java 信任库缺少那个特定的链证书。
因此,当您尝试连接到它时,java 无法知道根证书最终是否信任叶证书,因为它找不到路径。
有两种处理方法,理想情况下,服务器操作员会将其服务器配置为呈现证书链,如果不可能,您可以手动将链证书导入 java 的信任库。

Download the cert
导入它

keytool -import -file Example.cer -keystore examplekeystore

unable to find valid certification path to requested target 表示由于

客户端不信任服务器证书
  1. 从叶证书到根证书的链不完整或
  2. 信任库中不存在根证书

我检查过 Symantec Class 3 Secure Server SHA256 SSL CA 是由 VeriSign Universal Root Certification Authority 发布的(参见 Symantec page

而 Verisign root 有效地包含在 jdk1.7 中。0_79,所以我放弃了 2)。因此,我的猜测是服务器端的链条不完整。

操作

  1. 检查 https://www.ssllabs.com 中的服务器寻找 'incomplete chain errors'。

  2. 验证中间CA确实是Symantec Class 3 Secure Server SHA256 SSL CA,序列号69 87 94 19 d9 e3 62 70 74 9d bb e5 9d c6 68 5e

  3. 如果第 1 步出现错误,请下载 Symantec 证书(从上面 link)并导入到您的信任库

  4. 如果中间证书不是来自 Symantec 的预期证书,则获取根 CA 并将其导入到您的 trustore

已编辑 SSL 信任验证

证书是按层次结构颁发的。每个证书都由上层的颁发者签署,从根 CA 到叶证书。数字签名允许检查认证链。

SSL服务器必须提供证书和链(不包括根)。信任管理器检查从叶到根的认证链。如果在信任库中找到任何证书,则证书为 "trusted"(即使它已过期...)

您应该在信任库中包含根 CA,而不是叶