JCE:验证X509自签名证书时出现异常

时间:2017-07-31 19:47:39

标签: java ssl x509certificate jce

我们为SSL / TLS开发了一个自定义JCE安全提供程序。

我们的一位用户在客户端获得服务器证书验证失败。这是通常的“无法找到有效的证书路径到请求的目标”错误。 (是的,证书在信任库中。)

注意:虽然我们正在实现自定义提供程序,但我们依赖于信任管理器的标准JCE提供程序,在TLS握手期间使用javax.net.ssl.X509TrustManager.checkServerTrusted(X509Certificate [] chain,String)。

sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
        at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:387)
        at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:292)
        at sun.security.validator.Validator.validate(Validator.java:260)
        at sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:324)
        at sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:229)
        at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:105)
        [snip]
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
        at sun.security.provider.certpath.SunCertPathBuilder.build(SunCertPathBuilder.java:146)
        at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:131)
        at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:280)
        at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:382)
        ...

与所有用户一样,他们安装了无限制的安全策略jar。

服务器证书是自签名的(不是CA)。他们使用127.0.0.1作为主机名,因为它们只是连接到后端进程。

JRE / JDK的默认安装(使用Sun / Oracle安全提供程序)。使用javax.net.debug输出,我已确认这些成功的连接使用相同的自签名证书。

然而,当我将一些代码混合在一起以简单地建立连接时,它在同一台机器上使用相同的信任库,密钥库,证书和JDK时没有问题。这使用相同的验证函数,该函数使用相同的证书和相同的authType字符串对X509TrustManager.checkServerTrusted()进行相同的调用。我不能为我的生活解释为什么checkServerTrusted()在这种情况下验证证书但在用户的情况下失败。

是否有可能通过某种方式调整JCE,以致X509TrustManager无法验证此证书?也许是因为它是自签名的,或者因为通用名称是服务的名称而不是域名?我在其JVM参数或安全属性中看不到任何内容来指示这一点。但也许他们正在进行一些JCE调用我不知道哪个修改了X509TrustManager的行为?

证书的javax.net.debug输出如下。通用名称只是我们采用者服务的名称。这对我来说似乎很可疑。但是,它适用于默认安全提供程序。

***
Found trusted certificate:
[
[
  Version: V3
  Subject: CN=[snip], OU=[snip], O=[snip], L=Bangalore, ST=[snip], C=IN
  Signature Algorithm: SHA256withRSA, OID = 1.2.840.113549.1.1.11

  Key:  Sun RSA public key, 2048 bits
  modulus: [snip]
  public exponent: 65537
  Validity: [From: Wed Feb 17 14:45:40 IST 2016,
               To: Thu Nov 20 14:45:40 IST 2070]
  Issuer: CN=[snip], OU=[snip], O=[snip], L=Bangalore, ST=[snip], C=IN
  SerialNumber: [    5cf68160]

Certificate Extensions: 1
[1]: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
[snip]
]
]

]
  Algorithm: [SHA256withRSA]
  Signature:
[snip]

]

添加一些额外的日志后,很明显,无论出于何种原因,所接受的发行人的填充方式都不同。在失败的案例中,从[trust-manager]返回了过多的已接受的发行人.getAcceptedIssuers [];它们不包括有问题的证书。在通过案例中,只有有问题的证书才包含在已接受的发行人中。

[编辑1]更正了证书。

[edit 2]更正粗体问题中的通用名称。

[编辑3]添加了已接受的发行人段落

1 个答案:

答案 0 :(得分:0)

事实证明,这是缓存信任管理员的问题。

使用默认提供程序或我们的自定义提供程序,信任管理器最初使用cacerts信任库进行实例化。

使用默认提供程序,稍后将使用javax.net.ssl.trustStore指定的信任库来实例化新的信任管理器。我们的自定义提供程序只是重新使用以前实例化的cacerts信任管理器。

解决方案:实例化一个新的信任管理器,它将尊重javax.net.ssl.trustStore。