Java/Keystore 验证已签名证书

4

我正在处理一个嵌入式jetty服务器和客户端之间的客户端证书认证。他们都使用密钥库。客户端证书由服务器证书签名,该证书由CA签名。Jetty使用两种方法来验证客户端证书,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,则代码可以工作,但我不确定自己在做什么。
我开始对Jetty的代码产生怀疑,但我不是证书和SSL握手方面的专家,因此这可能也与错误的密钥库/信任库有关。这就是为什么我没有在Jetty的论坛上创建问题并在此之前提问的原因,以确保需要更改代码。
另外,了解如何在Java中验证签名证书可能很有用。

1
为什么不让JSSE通过信任管理器来为您完成所有这些工作呢? - Bruno
因为我的服务器也接受非证书认证连接。我必须先让它接受所有连接,然后在某些情况下验证客户端的证书(如果有的话)。 - Ghetolay
4
不,你只需要使用wantClientAuth选项(而非need)来使客户端证书变成可选项。 - Bruno
1
是的,当您将客户端证书身份验证配置为可选时,它就应该是这样工作的。我认为如果证书不受信任(即,如果客户端的证书不受信任,则不应发送任何证书),连接将被拒绝。 - Bruno
你是如何获取服务器证书来签署客户端证书的?CA机构不会给你签名证书。而且为什么要这样做? - user207421
显示剩余2条评论
2个回答

0

实际上,我不需要自己进行验证。

SSLEngine已经在进行验证。如果客户端发送了有效的证书,您可以使用getPeerCertificateChain()获取它:如果客户端未发送证书或发送了无效的证书,则getPeerCertificateChain()会抛出异常。

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

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


getPeerCertificateChain() 不会返回 null,它会抛出一个异常。 - user207421
如果发送了无效证书,则握手过程会抛出异常,远在您调用getPeerCertificateChain()之前。 - user207421

-1
请检查您的证书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的“if”语句,但是这些“if”语句从未被执行,因为我没有设置这些选项。我应该设置吗?这是绝对必要的吗? - Ghetolay

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接