我正在研究嵌入式码头服务器和客户端之间的客户端证书身份验证 . 他们都使用密钥库 . 客户端证书由服务器的证书签名,该证书由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)似乎有效 .
调试输出太大,无法放在这里,但这里是stacktrace:
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握手方面的专家,所以它也可能来自糟糕的密钥库/信任库 . 这就是为什么我没有在码头上创建问题并在此之前询问,以确保代码需要更改 .
此外,了解如何在Java中验证签名证书可能很有用 .
2 回答
请检查您的证书CRL或OCSP是否可访问,您可以在证书中找到此类信息,例如
实际上我自己不需要进行验证 .
SSLEngine已经在执行此操作,如果客户端发送了有效证书,您可以使用getPeerCertificateChain()获取它,如果客户端没有发送证书或无效证书,则getPeerCertificateChain()返回null会抛出异常 .
使用jetty(或任何Java ServletContainer)我只需要检查HttpServletRequest的属性[“javax.servlet.request.X509Certificate”],以了解客户端是否发送了有效证书 .
我仍然不知道如何用Java验证证书,但这个解决方案对我来说已经足够了:)我不需要自己做了 . 感谢布鲁诺!