【问题标题】:Java/Keystore Verify Signed certificateJava/Keystore 验证签名证书
【发布时间】:2012-09-09 11:33:31
【问题描述】:

我正在研究嵌入式码头服务器和客户端之间的客户端证书身份验证。他们都使用密钥库。客户端证书由 CA 签署的服务器证书签署。 Jetty 使用 2 方法来验证客户端证书 javax.net.ssl.SSLEngine 这似乎可以工作,他们也使用上面的代码。

List<X509Certificate> certList = Certificate chain sent by the client
KeyStore truststore = server's truststore

//No use of CRL/OSCP/CRLDP
_crls = null;
_enableOCSP = false;
_enableCRLDP = false;

try{
 X509CertSelector certSelect = new X509CertSelector();
 certSelect.setCertificate((X509Certificate) certList.get(0));

 // Configure certification path builder parameters
 PKIXBuilderParameters pbParams = new PKIXBuilderParameters(truststore, certSelect);
 pbParams.addCertStore(CertStore.getInstance("Collection", new CollectionCertStoreParameters(certList)));

 // Set maximum certification path length
 pbParams.setMaxPathLength(-1);

 // Enable revocation checking
 pbParams.setRevocationEnabled(true);

 // Set static Certificate Revocation List
 if (_crls != null && !_crls.isEmpty())
     pbParams.addCertStore(CertStore.getInstance("Collection", new CollectionCertStoreParameters(_crls)));

  // Enable On-Line Certificate Status Protocol (OCSP) support
  if (_enableOCSP)
      Security.setProperty("ocsp.enable","true");

  // Enable Certificate Revocation List Distribution Points (CRLDP) support
  if (_enableCRLDP)
      System.setProperty("com.sun.security.enableCRLDP","true");

 // Build certification path
 CertPathBuilderResult buildResult = CertPathBuilder.getInstance("PKIX").build(pbParams);               

 // Validate certification path
 CertPathValidator.getInstance("PKIX").validate(buildResult.getCertPath(),pbParams);
}catch(GeneralSecurityException gse){
 ...
}

当然我必须使用第二种方式... 那么让我们专注于这段代码,这是验证签名证书的好方法吗? 这是我的密钥库的转储:

客户端密钥库:

Entry type: PrivateKeyEntry 
Certificate chain length: 2
Certificate[1]: 
Owner: EMAILADDRESS=truc@ok.com, CN=Servlet, OU=dev, O=Imbasoft, ST=Ile-de-France, C=FR 
Issuer: EMAILADDRESS=contact@greenpacs.com, CN=Greenpacs, OU=dev, O=Imbasoft, L=Bondy, ST=Ile-de-France, C=FR 
...

Certificate[2]: 
Owner: EMAILADDRESS=contact@greenpacs.com, CN=Greenpacs, OU=dev, O=Imbasoft, L=Bondy, ST=Ile-de-France, C=FR
Issuer: EMAILADDRESS=ghetolay@imbasoft.com, CN=Greenpacs Certificate Authority, OU=dev, O=Imbasoft, ST=Ile-de-France, C=FR 
...

服务器信任库:

Entry type: trustedCertEntry

Owner: EMAILADDRESS=contact@greenpacs.com, CN=Greenpacs, OU=dev, O=Imbasoft, L=Bondy, ST=Ile-de-France, C=FR
Issuer: EMAILADDRESS=ghetolay@imbasoft.com, CN=Greenpacs Certificate Authority, OU=dev, O=Imbasoft, ST=Ile-de-France, C=FR

我不确定这些密钥库,但我尝试了不同的密钥库(将 CA 证书添加到客户端的证书链,将证书添加到信任库)但验证仍然失败。使用这些密钥库,第一种验证方式 (SSLEngine) 似乎可以工作。

调试输出太大,无法放在这里,但这里是堆栈跟踪:

java.security.cert.CertPathValidatorException: Could not determine revocation status
    at sun.security.provider.certpath.PKIXMasterCertPathValidator.validate(PKIXMasterCertPathValidator.java:153)
    at sun.security.provider.certpath.PKIXCertPathValidator.doValidate(PKIXCertPathValidator.java:325)
    at sun.security.provider.certpath.PKIXCertPathValidator.engineValidate(PKIXCertPathValidator.java:187)
    at java.security.cert.CertPathValidator.validate(CertPathValidator.java:267)
    at MainClass.main(MainClass.java:75)
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:197)
    at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:255)
    at sun.security.provider.certpath.CrlRevocationChecker.buildToNewKey(CrlRevocationChecker.java:583)
    at sun.security.provider.certpath.CrlRevocationChecker.verifyWithSeparateSigningKey(CrlRevocationChecker.java:459)
    at sun.security.provider.certpath.CrlRevocationChecker.verifyRevocationStatus(CrlRevocationChecker.java:339)
    at sun.security.provider.certpath.CrlRevocationChecker.verifyRevocationStatus(CrlRevocationChecker.java:248)
    at sun.security.provider.certpath.CrlRevocationChecker.check(CrlRevocationChecker.java:189)
    at sun.security.provider.certpath.PKIXMasterCertPathValidator.validate(PKIXMasterCertPathValidator.java:131)
    ... 4 more

如果我禁用吊销,或者如果我将最后一个证书(而不是第一个)设置为 X509CertSelector,代码可以工作,但我不确定我在做什么。

我开始怀疑码头代码,但我不是证书和 SSL 握手方面的专家,因此它也可能来自错误的密钥库/信任库。这就是为什么我没有在 jetty 的板上创建问题并在此之前询问过,以确保需要更改代码。

了解如何在 Java 中验证签名证书也很有用。

【问题讨论】:

  • 你为什么不让信任经理通过 JSSE 为你做这一切?
  • 因为我的服务器也接受非 cert-auth 连接。我必须先让它接受所有连接,然后在某些情况下验证客户端的证书(如果有)。
  • 不,您只需要使用 wantClientAuth 选项(而不是需要)使客户端证书成为可选。
  • 其实它是这样工作的,但我认为它没有对客户端的证书进行任何验证。在阅读您的评论后,我做了一些测试,这似乎是它的工作方式:如果客户端发送了有效的客户端证书,则接受连接并且请求属性 X509Certificate 设置为有效证书。如果发送无效证书或未发送证书,则仍接受连接,但未设置请求属性。这是对的吗?这意味着我只需要测试请求属性即可知道证书是否有效。你能确认一下这种行为吗?
  • 是的,当您将客户端证书身份验证配置为可选时,这就是它的工作方式。我认为如果证书不受信任,则连接将被拒绝(即,如果其证书不受信任,则客户端根本不应该发送证书)。

标签: java ssl certificate keystore truststore


【解决方案1】:

其实我不需要自己做验证。

SSLEngine 已经在这样做了。如果客户端发送了有效证书,您可以使用getPeerCertificateChain()获取它:如果客户端没有证书或发送无效证书getPeerCertificateChain()会抛出异常。

使用 Jetty(或我猜的任何 Java ServletContainer),您只需要检查 HttpServletRequest 的属性 ["javax.servlet.request.X509Certificate"] 即可了解客户端是否发送了有效证书。

我仍然不知道如何在 Java 中验证证书,但是这个解决方案对我来说已经足够了 :) 我不再需要自己做。感谢布鲁诺!

【讨论】:

  • getPeerCertificateChain() 不返回 null,它会引发异常。
  • 如果发送的证书无效,握手会引发异常,远在您调用getPeerCertificateChain()之前。
【解决方案2】:

请检查您的证书CRL或OCSP是否可以访问,您可以在证书中找到这些信息,例如

[1]CRL Distribution Point
     Distribution Point Name:
          Full Name:
               URL=http://crl.verisign.com/pca2-g2.crl

【讨论】:

  • 我不使用任何 CRL、OSCP 或 CRLDP。我的证书上没有这行。此外,显示的代码不完整,有一些 CRL/OSCP/CRLDP 的“如果”,但这些“如果”从未达到,因为我没有设置这些选项。我是不是该 ?有必要吗?
猜你喜欢
  • 1970-01-01
  • 2011-12-03
  • 1970-01-01
  • 1970-01-01
  • 2011-11-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多