使用BouncyCastle接受过期的CRL

时间:2018-05-18 11:21:04

标签: java bouncycastle x509 certificate-revocation

我正在使用充气城堡离线验证X509证书,并且遇到旧版CRL的问题。我还没有发现有可能接受过期的CRL,在我看来,如果证书被撤销,它应该在CRL到期后被撤销。另外如果CRL是空的我只想接受这个,我现在无法获得更新的CRL。

只是澄清一下,这将是用例:

  1. 2015年创建证书,有效期为2015-2020
  2. 2017年以CRL撤销证书,密钥被盗,仅创建CRL 1年,因为我犯了错误或计划滚动而且从未接触过它
  3. 2019年检查证书,CRL已过期,bouncy castle再次接受撤销证书 - 这显然不是我想要的
  4. 目前,我将撤销检查设置为false并自行执行检查。我在网上找不到任何关于此事的信息。

    这是我的代码:

    final X509CertSelector endConstraints = new X509CertSelector();
    endConstraints.setSerialNumber(signer.getSID().getSerialNumber());
    
    final PKIXBuilderParameters buildParams = new PKIXBuilderParameters(trustAnchors, endConstraints);
    //a CertStore object with Certificates and CRLs
    buildParams.addCertStore(certificates);
    //currently deactivated
    buildParams.setRevocationEnabled(false);
    
    final CertPathBuilder builder = CertPathBuilder.getInstance(SignedFileVerifier.CERTIFICATE_PATH_ALGORITHM, SignedFileVerifier.PROVIDER);
    final CertPathBuilderResult result = builder.build(buildParams);
    
    //here I manually check the CRLs, which I don't want to do
    checkRevocation(result.getCertPath().getCertificates(), certificates, trustAnchors);
    
    //if this passes I return the found certificate
    return (X509Certificate) result.getCertPath().getCertificates().get(0);
    

    确切的例外是:

    Caused by: org.bouncycastle.jce.exception.ExtCertPathValidatorException: No CRLs found for issuer "cn=goodOldIssuerCA0,ou=jUnit Test Issuer,o=BOGO Company,c=AT"
        at org.bouncycastle.jce.provider.RFC3280CertPathUtilities.processCertA(Unknown Source)
        at org.bouncycastle.jce.provider.PKIXCertPathValidatorSpi.engineValidate(Unknown Source)
        at org.bouncycastle.jce.provider.PKIXCertPathBuilderSpi.build(Unknown Source)
        at org.bouncycastle.jce.provider.PKIXCertPathBuilderSpi.build(Unknown Source)
        ...
    

2 个答案:

答案 0 :(得分:2)

基本上我的整个问题发生在包PKIXCRLUtil#findCRLs中的方法org.bouncycastle.jce.provider中。这个方法用于加载CRL,并始终在此处检查日期:

if (crl.getNextUpdate().after(validityDate))
    {
        X509Certificate cert = crlselect.getCertificateChecking();

        if (cert != null)
            {
            if (crl.getThisUpdate().before(cert.getNotAfter()))
            {
                finalSet.add(crl);
            }
        }
        else
        {
            finalSet.add(crl);
        }
}

我最终使用的代码如下。基本上我首先将所有公钥按名称组合成地图(序列号会更好吗?),然后迭代我链中的所有证书。首先,我获得证书颁发者公钥,因为我需要它来验证来自同一发行者的CRL。然后我创建一个X509CRLSelector发行者并加载此发行者的所有CRL。然后我迭代我在商店中找到的CRL,通过发行者公钥验证它们,检查证书是否被撤销并在这种情况下抛出异常。在我当前的实现中,如果没有找到CRL就可以了,可以通过检查selectedCRLs是否为空来添加。

private void checkRevocation(final List<X509Certificate> certificates, final CertStore revocationLists, final Set<TrustAnchor> trustAnchors) throws GeneralSecurityException {
    final Map<String, PublicKey> publicKeyMap = extractPublicKeys(certificates, trustAnchors);

    //check the whole chain, we don't know if the issuer or the signer was revoked
    for(final X509Certificate certificate : certificates){
        final X500Principal issuerX500Principal = certificate.getIssuerX500Principal();

        //get the issuer of this certificate
        final PublicKey issuerPublicKey = publicKeyMap.get(issuerX500Principal.getName());

        if(issuerPublicKey == null){
            throw new GeneralSecurityException("Unable to find issuer for certificate '" + certificate.getSubjectX500Principal() + "'");
        }

        final X509CRLSelector crlSelector = new X509CRLSelector();
        //we only use the issuer, not the date or time, don't want CRLs to expire
        crlSelector.addIssuer(issuerX500Principal);

        //get all CRLs that match this issuer
        final Collection<? extends CRL> selectedCRLs = revocationLists.getCRLs(crlSelector);
        for(final CRL crl : selectedCRLs){
            final X509CRL x509CRL = (X509CRL)crl;
            //check first if the crl is really published by the issuer
            x509CRL.verify(issuerPublicKey);

            //check if the current certificate was revoked
            final X509CRLEntry revokedCertificate = x509CRL.getRevokedCertificate(certificate);

            //if we found a revoked certificate throw an exception
            if(revokedCertificate != null){
                throw new GeneralSecurityException(String.format("Unable to use certificate '%1$s', revocation after %2$tF %2$tT, reason: %3$s",
                        certificate.getSubjectX500Principal(), revokedCertificate.getRevocationDate(), revokedCertificate.getRevocationReason()));
            }
        }
    }
}

private Map<String, PublicKey> extractPublicKeys(final List<X509Certificate> certificates, final Set<TrustAnchor> trustAnchors) {
    final Map<String, PublicKey> certificateMap = new HashMap<>();
    for(final X509Certificate certificate : certificates){
        certificateMap.put(certificate.getSubjectX500Principal().getName(), certificate.getPublicKey());
    }

    for(final TrustAnchor trustAnchor : trustAnchors){
        final X509Certificate certificate = trustAnchor.getTrustedCert();
        certificateMap.put(certificate.getSubjectX500Principal().getName(), certificate.getPublicKey());
    }
    return certificateMap;
}

答案 1 :(得分:0)

完全禁用CRL检查。不,我不是在开玩笑。安全社区的普遍共识是revocation is broken。现代浏览器甚至不用担心检查CRL。如果您可以在浏览器中启用严格的CRL检查(许多人甚至不会再让您这么做了),那么您就会发现大部分网络无法访问。