【问题标题】:Getting root and intermediate certificates from an end-entity从终端实体获取根证书和中间证书
【发布时间】:2017-10-08 17:15:06
【问题描述】:

我仍然是密码学的菜鸟,我每天都会偶然发现一些简单的事情。而今天只是其中之一。

我想用 bouncy castle 库验证 java 中的 smime 消息,我想我几乎想通了,但此时的问题是 PKIXparameters 对象的构建。 假设我有一个具有以下结构的最终实体 x509certificate:

root certificate
 +->intermediate certificate
    +->end-entity certificate

为了验证消息,我需要先建立信任链,但我不知道如何从终端实体中提取根证书和中间证书。

我尝试以 root 身份使用终端实体,但没有成功:

InputStream isCert = GetFISCertificate();

List list = new ArrayList();
X509Certificate rootCert = (X509Certificate) certificateFactory.generateCertificate(isCert);
list.add(rootCert);
CollectionCertStoreParameters params = new CollectionCertStoreParameters(list);
CertStore store = CertStore.getInstance("Collection", params, BC);

//create cert path
List certChain = new ArrayList();
certChain.add(rootCert);
CertPath certPath = certificateFactory.generateCertPath(certChain);
Set trust = Collections.singleton(new TrustAnchor(rootCert, null));

//validation
CertPathValidator certPathValidator = CertPathValidator.getInstance("PKIX", BC);
PKIXParameters pKIXParameters = new PKIXParameters(trust);
pKIXParameters.addCertStore(store);
pKIXParameters.setDate(new Date());
try {
CertPathValidatorResult result = certPathValidator.validate(certPath, pKIXParameters);
System.out.println("certificate path validated");

} catch (CertPathValidatorException e) {
System.out.println("validation failed on certificate number " + e.getIndex() + ", details: " + e.getMessage());
}

遇到了这个异常:

validation failed on certificate number -1, details: Trust anchor for certification path not found.

顺便说一句,我可以只使用最终实体证书来验证消息,就好像它是自签名证书一样?

【问题讨论】:

    标签: java x509certificate bouncycastle


    【解决方案1】:

    顺便说一句,如果我们在 windows 中安装了证书,一切都会简单得多:

    KeyStore ks = KeyStore.getInstance("Windows-MY");
    ks.load(null, null);
    String alias = "your alias";
    ArrayList<X509Certificate> certsChain = new ArrayList<>();
    if (ks.isCertificateEntry(alias)) {
        Certificate[] chain = ks.getCertificateChain(alias);
        System.out.println("Chain length: " + chain.length);
            for(Certificate c : chain) certsChain.add((X509Certificate)c);
    }
    
    Collections.reverse(certsChain);
    certsChain.forEach(MainClass::printDBG);
    

    boom,整个证书链都准备好了

    【讨论】:

      【解决方案2】:

      我使用 BouncyCastle 1.56 进行此测试。

      从最终实体获取颁发者证书的一种方法是查找Authority Information Access extension

      此扩展可能存在(不是强制性的)并且可能包含获取颁发者证书的 URL(颁发者是证书”上面"当前的,所以最终实体的发行人是中间人,中间人的发行人是根)。

      您可以通过 BouncyCastle 获得此扩展值:

      import java.security.cert.X509Certificate;
      import org.bouncycastle.asn1.x509.AccessDescription;
      import org.bouncycastle.asn1.x509.AuthorityInformationAccess;
      import org.bouncycastle.asn1.x509.Extension;
      import org.bouncycastle.asn1.x509.GeneralName;
      import org.bouncycastle.asn1.x509.X509ObjectIdentifiers;
      import org.bouncycastle.jce.provider.BouncyCastleProvider;
      import org.bouncycastle.x509.extension.X509ExtensionUtil;
      
      X509Certificate cert = // end entity certificate
      
      // get Authority Information Access extension (will be null if extension is not present)
      byte[] extVal = cert.getExtensionValue(Extension.authorityInfoAccess.getId());
      AuthorityInformationAccess aia = AuthorityInformationAccess.getInstance(X509ExtensionUtil.fromExtensionValue(extVal));
      
      // check if there is a URL to issuer's certificate
      AccessDescription[] descriptions = aia.getAccessDescriptions();
      for (AccessDescription ad : descriptions) {
          // check if it's a URL to issuer's certificate
          if (ad.getAccessMethod().equals(X509ObjectIdentifiers.id_ad_caIssuers)) {
              GeneralName location = ad.getAccessLocation();
              if (location.getTagNo() == GeneralName.uniformResourceIdentifier) {
                  String issuerUrl = location.getName().toString();
                  // http URL to issuer (test in your browser to see if it's a valid certificate)
                  // you can use java.net.URL.openStream() to create a InputStream and create
                  // the certificate with your CertificateFactory
                  URL url = new URL(issuerUrl);
                  X509Certificate issuer = (X509Certificate) certificateFactory.generateCertificate(url.openStream());
              }
          }
      }
      

      因此您可以将此代码与最终实体证书一起使用以获取中间体。然后你再次使用它和中间来获取根。

      然后您将 root 添加到您的TrustAnchor 并且验证应该可以工作。


      注意:但正如我所说,这个扩展不是强制性的,可能不存在。在这种情况下,getExtensionValue 将返回null,我知道的唯一选择是在 google 中搜索证书并下载它们(这些证书链通常是公开的,不难找到)

      【讨论】:

      • 扩展权限信息访问对于 OCSP 信息非常常见。但我想我从未见过带有此扩展名的证书包含 id-ad-caIssuers。无论如何,很好的答案。
      • @Egl 我见过带有 id-ad-caIssuers 和 OCSP 的证书(google.com 的证书和巴西的 PKI 最终实体就是很好的例子)。我也见过只有其中一个的证书,不幸的是,CA 之间似乎没有标准。
      • @Hugo,我非常感谢,再一次。它编译并且可以工作,但我仍然觉得要经历黑暗,有很多事情我不完全理解,这让我很烦恼。你能推荐一些可以阅读的东西吗?我在考虑 David Hook 的“Beginning Cryptography with Java”,但它有点过时了(2005 年)。我对密码学有基本的了解,不是完全新手,但离专业人士也很远。
      • 我认为你可以从学习Public Key InfrastructurePublic Key Cryptography 开始。别担心,我花了好几年才真正理解所有这些东西是如何工作的(我仍然觉得自己不是专业人士,有很多细节我没有深入了解)。无论如何,如果你了解它是如何工作的(以及为什么),这对于大多数应用程序来说已经足够了(你不需要学习所有的数学细节,它们已经在 API 中实现了)
      • 还有一件事:正如我所说,最终实体证书可能包含有关其发行者的信息(在 AIA 扩展中,如答案中所述)。但它不包含有关整个链条的信息。您必须经历每一步:将实体结束到中间,然后将中间到另一个中间,直到获得根(理论上中间的数量没有限制,我相信,但 1 或 2 是最常见的情况)。但是一些中间人可能没有关于其发行人的信息(这不是强制性的),这真的很烦人..
      猜你喜欢
      • 1970-01-01
      • 2019-11-14
      • 2018-06-08
      • 1970-01-01
      • 2020-07-21
      • 2013-02-09
      • 1970-01-01
      • 1970-01-01
      • 2015-10-09
      相关资源
      最近更新 更多