2017-07-31 187 views
1

我们为SSL/TLS开发自定义JCE安全提供程序。JCE:验证X509自签名证书时发生异常

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

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

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的行为?

cert的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]更正了证书。

修正了大胆问题中的通用名称。

[编辑3]增加了发行人接受款

+0

此问题不是名称检查,因为只有在路径验证成功后才会发生此问题,并且仅适用于某些应用程序,例如, HTTPS。但是,如果/完成后,您的文本显示“他们”(用户?)使用127.0.0.1,但您的跟踪显示证书具有CN = localhost;这些名称不是相同的名称,尝试时不会匹配,即使通常都是同一主机和伪接口的名称。至于你的实际问题,我可以确认selfsigned cert和hostname = localhost工作正常,就像你显然做的那样。 ... –

+0

...我想不出除(secprop)jdk.certpath.disabledAlgorithms之外的任何JVM设置,对于给定的JRE中的所有应用程序应该是相同的,并且不应该影响这种情况。最好我可以建议是尝试与sysprop java.security.debug = certpath运行,看看它说什么有所帮助。 : - ? –

+0

我意识到127.0.0.1和localhost是不同的。但是,在我的测试代码中,使用该证书成功建立连接,无论我是否使用127.0.0.1或localhost,无论是否使用我们的提供程序。 此外,我设法把错误的证书放在帖子中。这是来自以前的连接,我们充当服务器(没有问题)。 我会尝试certpath并查看给了我什么。谢谢。 –

回答

0

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

无论是默认提供程序还是我们的自定义提供程序,信任管理器最初都是通过cacerts信任库实例化的。

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

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