-->

验证Java中的证书抛出一个例外 - 无法找到有效的证书路径请求的目标(Validating a c

2019-06-24 02:58发布

我有一个需要客户端发送它的证书和服务器验证证书的web应用程序(即看是否发行人是一个有效的发行人存在于服务器的信任)。 下面是代码:

FileInputStream fin=new FileInputStream("C:/trustedca");
    KeyStore anchors = KeyStore.getInstance("JKS","SUN");
    anchors.load(fin, "server".toCharArray());
    X509CertSelector target = new X509CertSelector();
    FileInputStream fin1=new FileInputStream("C:/client.crt");
    CertificateFactory cf=CertificateFactory.getInstance("X.509");
    X509Certificate cert=null;
    while (fin1.available() > 0) 
    {
     System.out.println("in while---------");
     cert =(X509Certificate) cf.generateCertificate(fin1);
    }
    target.setCertificate(cert);
    PKIXBuilderParameters params = new PKIXBuilderParameters(anchors, target);

    CertPathBuilder builder = (CertPathBuilder) CertPathBuilder.getInstance("PKIX").build(params);
    PKIXCertPathBuilderResult r = (PKIXCertPathBuilderResult) builder.build((CertPathParameters)params);<br>

但是,我得到一个异常:

sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid
 certification path to requested target<br>

注意 :
这里由客户端发送的证书是client.crt和用于签署client.crt证书cerificate是其存在于密钥库“trustedca”的ca.crt。 那为什么给这个例外?

Answer 1:

如果你期待一个客户端证书,让JSSE做这一切为您服务。 如果你想使用自己的信任存储特定连接,配置JSSE使用它。 检查定制JSSE的参考文档中节。

下面是构建一个简单的例子SSLContext使用自定义信任库。 (其它更复杂的X509TrustManager S能也可以使用,但您很少需要这一点。)

TrustManagerFactory tmf = TrustManagerFactory
    .getInstance(TrustManagerFactory.getDefaultAlgorithm());
KeyStore ks = KeyStore.getInstance("JKS");
FileInputStream fis = new FileInputStream("/.../example.jks");
ks.load(fis, null);
// or ks.load(fis, "thepassword".toCharArray());
fis.close();

tmf.init(ks);

SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, tmf.getTrustManagers(), null);

如果您使用的是现有的应用程序服务器,如何通过配置这一切都将取决于服务器,以及如何预期进行配置的。 使用JSSE这也将确保密钥使用属性是合适的。

如果你通过一些其他手段证书,要验证它,你需要使用PKI API 。 如果按照验证使用PKIX算法认证路径的例子 ,你应该得到的东西是这样的:

X509Certificate certToVerify = ...

CertificateFactory cf = CertificateFactory.getInstance("X.509");
CertPath cp = cf.generateCertPath(Arrays
    .asList(new X509Certificate[] { certToVerify }));

TrustAnchor trustAnchor = new TrustAnchor(caCert, null);

CertPathValidator cpv = CertPathValidator.getInstance("PKIX");
PKIXParameters pkixParams = new PKIXParameters(
    Collections.singleton(trustAnchor));
pkixParams.setRevocationEnabled(false);

cpv.validate(cp, pkixParams);

检查从验证结果(和它没有抛出验证异常,当然)。 在这里,我已禁用吊销检查,以简化。 您还可以设置等方面PKIXParameters的策略检查。 这可以得到相当复杂的(以及为什么它是更好地让默认JSSE经理为你做的)。


你还问了关于这一切,你问的这个Security.SE其他问题的背景: 什么是证书指纹的实际价值?

假设你有两个X509Certificate S: serverCertcaCert ,要验证serverCert由(匹配的公钥私钥)签署caCert

最简单的方法:

serverCert.verify(caCert.getPublicKey());

如果你想手动做这一点,使用Signature API:

System.out
     .println("Signature algorithm: " + serverCert.getSigAlgName());
Signature sig = Signature.getInstance(serverCert.getSigAlgName());
sig.initVerify(caCert.getPublicKey());
sig.update(serverCert.getTBSCertificate());
System.out
    .println("Verified? " + sig.verify(serverCert.getSignature()));

假设算法是SHA1withRSA ,你也可以计算摘要:

MessageDigest digest = MessageDigest.getInstance("SHA-1");
digest.reset();
digest.update(serverCert.getTBSCertificate());
byte[] digestBytes = digest.digest();

Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, caCert.getPublicKey());
byte[] cipherText = cipher.doFinal(serverCert.getSignature());

摘要本身将只使用结果的一部分Cipher :你会得到什么serverCert.getSignature()事实上是一个更为复杂的ASN.1结构,它包括摘要算法标识符,在这种情况下, digestBytes应该前缀有一些这样的 :

SHA-1:   (0x)30 21 30 09 06 05 2b 0e 03 02 1a 05 00 04 14 || H.

( BouncyCastle的 ,如果你想正确地分析ASN.1结构可能是有用的。)

需要注意的是这一切都不验证时间有效性或任何其他属性。 PKIX遵守远远超过检查签名(见RFC 3820和5820)。



Answer 2:

也许一个有效的路径无法建造,因为一些中间证书丢失。 你的循环加载证书丢弃所有,但最后一次。 相反,保存所有的证书,并将其传递给CertPathBuilder路径建设提供帮助。

另一个常见的问题是,吊销检查默认情况下,这是很好的安全进行。 如果你不知道如何获得CRL,或利用OCSP,可以减少您的安全和禁用吊销检查。 这也示于下面的例子。

...
CertificateFactory fac = CertificateFactory.getInstance("X.509");
FileInputStream is = new FileInputStream("client.crt");
Collection<? extends Certificate> intermediate;
try {
  intermediate = fac.generateCertificates(is);
} finally {
  is.close();
}
X509Certificate client = null;
for (Certificate c : intermediate)
  client = (X509Certificate) c;
if (client == null)
  throw new IllegalArgumentException("Empty chain.");
X509CertSelector t = new X509CertSelector();
t.setCertificate(client);
PKIXBuilderParameters params = new PKIXBuilderParameters(anchors, t);
CertStoreParameters store = new CollectionCertStoreParameters(intermediate);
params.addCertStore(CertStore.getInstance("Collection", store));
params.setRevocationEnabled(false);
...

这将有助于了解你是如何获得“client.crt”文件,其内容的预计。 像反应,我不知道为什么你不能使用JSSE的内置工具来执行此验证。



Answer 3:

即看,如果发行人是在服务器的信任有效的发行和现在

JSSE已经做了这一切。 你不必做任何的这个,也许除了验证对方的证书尚未过期。



文章来源: Validating a certificate in java throws an exception - unable to find valid certificate path to requested target