使用Java API验证X509证书

21

我正在尝试使用以下代码验证证书是否与java密钥库匹配。如果成功完成,则我假设验证已正确进行,否则如果抛出异常,则验证失败。

我的问题是:

以下代码是否足以验证证书?是否有什么我遗漏了(例如检查由发送给我的计算机签名的数据)? 2. 应该验证证书中包含的签名吗?如果是,如何验证?

提前感谢您的回复! pradeep

// To check the validity of the dates
cert.checkValidity();
//Check the chain
CertificateFactory cf = CertificateFactory.getInstance("X.509");
List<X509Certificate> mylist = new ArrayList<X509Certificate>();          
mylist.add(cert);
CertPath cp = cf.generateCertPath(mylist);
PKIXParameters params = new PKIXParameters(getTrustStore());
params.setRevocationEnabled(false);
CertPathValidator cpv =
      CertPathValidator.getInstance(CertPathValidator.getDefaultType());
PKIXCertPathValidatorResult pkixCertPathValidatorResult =
      (PKIXCertPathValidatorResult) cpv.validate(cp, params);

为什么要这么做?JSSE已经为你做了所有这些事情。 - user207421
@EJP,您能否详细介绍JSSE是如何实现的。谢谢。 - user674669
3个回答

8
通常,证书由中间颁发机构颁发,而不是“根”机构(应该只有它在您的信任存储中)。大多数协议鼓励发送“链”证书,而不仅仅是实体证书。
您应该添加所有中间证书,以便可以形成完整的链。
为了确保证书仍然有效,您不应禁用撤销检查。如果您不想检索CRL(可能很大),则颁发者可能提供OCSP支持。但是,这必须通过设置某些系统属性在Java运行时中启用。
如果路径验证器成功返回,则无需检查其他内容。如果证书无效,则会引发异常。
此外,对有效日期的明确检查是不必要的。这在验证期间发生(使用当前时间,除非您通过PKIXParameters指定时间)。

如果想更深入地讨论验证,包括示例代码,请查看我之前的一个回答


谢谢您的回复!您的意思是我需要将所有中间证书添加到证书列表中(mylist.add(cert);),然后使用CertPath创建链吗?谢谢! - user771870
2
我们需要设置哪些Java运行时属性才能启用证书吊销检查? - MayurB

4

如果您对默认的信任设置感到满意(因为它们将用于默认的SSLContext),则可以独立于 SSL/TLS 构建一个 X509TrustManager 并使用它来独立验证您的证书。

代码如下:

TrustManagerFactory trustManagerFactory =
    TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init((KeyStore)null);
// you could use a non-default KeyStore as your truststore too, instead of null.

for (TrustManager trustManager: trustManagerFactory.getTrustManagers()) {  
    if (trustManager instanceof X509TrustManager) {  
        X509TrustManager x509TrustManager = (X509TrustManager)trustManager;  
        x509TrustManager.checkServerTrusted(...);
    }  
}

您还应检查服务器的身份和证书是否匹配,详见RFC 6125(在传输层安全性(TLS)的上下文中使用X.509(PKIX)证书在Internet公共密钥基础设施中表示和验证基于域的应用程序服务标识)


1
嗯,...里面应该填什么? - Paul Draper
1
@PaulDraper 的第一个参数是 X509Certificate 数组,第二个参数是密钥交换算法(例如“RSA”),另请参阅 X509TrustManager 的 Javadoc https://docs.oracle.com/javase/7/docs/api/javax/net/ssl/X509TrustManager.html。 - Henno Vermeulen
1
请注意,如果TrustManagerFactory没有trustManager,则此代码不会抛出任何异常,从而接受证书。我查看了TrustManagerFactory的Javadoc,并且我相信如果您的默认密钥库不包含CA的X509证书,则可能会发生这种情况。为了更加安全,我建议更改代码,如果for循环未找到X509TrustManager,则抛出异常。 - Henno Vermeulen
你知道checkServerTrusted函数是否检查证书是否已过期,以及它是否检查发送有关证书的实体是否实际上是证书的所有者吗?还是它只验证证书是否由受信任的CA签名? - Milos Savanovic
看起来 checkServerTrusted 确实会检查证书是否已过期,因为它会抛出 CertificateExpiredException 和 CertificateNotYetValidException 异常。 - Milos Savanovic

1
你在这里做的是验证证书(例如 cert)是否已被信任的 CA 之一(直接地)签署。
此外,您检查到期但不执行吊销检查。
因此,如果 cert 没有被任何受信任的 CA 签署,您将收到异常。
所以该代码足以验证 cert 是否已被任何受信任的CA签署。

如果您正在参考服务器身份验证,则帖子中的代码不足够。
此代码仅验证特定证书是否由受信任的CA签名。
但是,您没有迹象表明向您发送此证书的“实体”实际上是证书的所有者(即他们拥有与此证书关联的私钥)。
这是SSL身份验证的一部分,例如,客户端使用远程服务器的公钥加密ClientKeyExchange消息,并确信如果另一方是假的,则无法解密该消息。


谢谢您的快速回复!上面的代码片段是否足以验证证书本身?我的意思是,如果我从服务器获取此证书,并执行上述检查,那么我可以安全地假设服务器的证书是有效的,并且服务器就是它所说的那个吗? 感谢您的帮助,非常感激。 - user771870
这确实有帮助。我正在尝试为SSH实现服务器身份验证。因此,一旦在初始交换期间从服务器获取证书,我正在尝试验证它以确保服务器不是伪造的,并执行类似于SSL执行相同操作的验证过程。 那么这是否意味着SSH会在密钥交换期间(在证书交换之前)处理服务器身份验证,而我所要做的就是确保证书是有效的?感谢您的回复,如果这些问题看起来很愚蠢,请原谅。我刚开始深入研究SSH工作流程 :) - user771870
如果SSH可以配置证书信任存储库,那么如果它的工作方式类似于SSL,SSH将需要验证通过证书的实体是否也拥有相应的私钥。 - Cratylus
啊!这正是我不确定如何实现的。这意味着我需要作为设置的一部分拥有此实体的公钥,对吗? 如果不是,那么如何通过X.509证书的字段完成此操作?我了解到签名字段是由CA加密而不是传递给我的证书实体加密的。有什么建议吗?谢谢! - user771870
我不太清楚你想做什么。你是想开发自己的SSH实现吗? - Cratylus
我正在尝试在SSH客户端连接到SSH服务器时通过X.509证书实现服务器身份验证。我正在使用一个SSH库(第三方),该库为我提供了服务器发送的证书(当服务器设置为发送证书而不是密钥时)。但是,这个证书的验证需要由我编写。因此,有以上问题。我正在使用Java keystore以及Keytools和Keystore类来处理keystore。 - user771870

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