如果您需要客户端证书,请让 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);
检查 validate 的结果(当然,它没有引发验证异常)。在这里,我禁用了撤销检查以简化。您还可以设置PKIXParameters
策略检查的其他方面。这可能会变得相当复杂(以及为什么最好让默认的 JSSE 管理器为您做这件事)。
您还在 Security.SE 上提出的另一个问题的上下文中询问了所有这些:证书指纹的实际价值是什么?.
假设您有两个X509Certificate
s:serverCert
和caCert
,您要验证它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());
摘要本身将只是 using 的结果的一部分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.
(如果您想正确分析 ASN.1 结构,BouncyCastle可能会很有用。)
请注意,这些都不能验证时间有效性或任何其他属性。PKIX 合规性远不止检查签名(参见 RFC 3820 和 5820)。