在 C# 中的 BouncyCastle 中构建证书链

本文关键字:构建 证书链 BouncyCastle 中的 | 更新日期: 2023-09-27 18:35:18

我有一堆以字节数组形式给出的根证书和中间证书,我也有最终用户证书。我想为给定的最终用户证书构建证书链。在.NET框架中,我可以这样做:

using System.Security.Cryptography.X509Certificates;
static IEnumerable<X509ChainElement>
    BuildCertificateChain(byte[] primaryCertificate, IEnumerable<byte[]> additionalCertificates)
{
    X509Chain chain = new X509Chain();
    foreach (var cert in additionalCertificates.Select(x => new X509Certificate2(x)))
    {
        chain.ChainPolicy.ExtraStore.Add(cert);
    }
    // You can alter how the chain is built/validated.
    chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
    chain.ChainPolicy.VerificationFlags = X509VerificationFlags.IgnoreWrongUsage;
    // Do the preliminary validation.
    var primaryCert = new X509Certificate2(primaryCertificate);
    if (!chain.Build(primaryCert))
        throw new Exception("Unable to build certificate chain");
    return chain.ChainElements.Cast<X509ChainElement>();
}

如何在充气城堡中做到这一点?我尝试使用下面的代码,但得到PkixCertPathBuilderException: No certificate found matching targetContraints

using Org.BouncyCastle;
using Org.BouncyCastle.Pkix;
using Org.BouncyCastle.Utilities.Collections;
using Org.BouncyCastle.X509;
using Org.BouncyCastle.X509.Store;
static IEnumerable<X509Certificate> BuildCertificateChainBC(byte[] primary, IEnumerable<byte[]> additional)
{
    X509CertificateParser parser = new X509CertificateParser();
    PkixCertPathBuilder builder = new PkixCertPathBuilder();
    // Separate root from itermediate
    List<X509Certificate> intermediateCerts = new List<X509Certificate>();
    HashSet rootCerts = new HashSet();
    foreach (byte[] cert in additional)
    {
        X509Certificate x509Cert = parser.ReadCertificate(cert);
        // Separate root and subordinate certificates
        if (x509Cert.IssuerDN.Equivalent(x509Cert.SubjectDN))
            rootCerts.Add(new TrustAnchor(x509Cert, null));
        else
            intermediateCerts.Add(x509Cert);
    }
    // Create chain for this certificate
    X509CertStoreSelector holder = new X509CertStoreSelector();
    holder.Certificate = parser.ReadCertificate(primary);
    // WITHOUT THIS LINE BUILDER CANNOT BEGIN BUILDING THE CHAIN
    intermediateCerts.Add(holder.Certificate);
    PkixBuilderParameters builderParams = new PkixBuilderParameters(rootCerts, holder);
    builderParams.IsRevocationEnabled = false;
    X509CollectionStoreParameters intermediateStoreParameters =
        new X509CollectionStoreParameters(intermediateCerts);
    builderParams.AddStore(X509StoreFactory.Create(
        "Certificate/Collection", intermediateStoreParameters));
    PkixCertPathBuilderResult result = builder.Build(builderParams);
    return result.CertPath.Certificates.Cast<X509Certificate>();
}

编辑:我添加了解决我问题的行。它以所有大写字母进行注释。 案件已结案。

在 C# 中的 BouncyCastle 中构建证书链

我在 Java 中做过很多次。鉴于 API 似乎是 Java 的直接端口,我会试一试。

  1. 我很确定当您将商店添加到构建器时,该集合应包含要构建的链中的所有证书,而不仅仅是中间证书。所以应该添加rootCerts和prime。
  2. 如果这本身不能解决问题,我会尝试以不同的方式指定所需的证书。您可以执行以下两项操作之一:
    • 实现您自己的选择器,该选择器始终仅与所需的证书(示例中的主要证书)匹配。
    • 而不是设置支架。证书,对持有人设置一个或多个标准。例如,setSubject,setSubjectPublicKey,setIssuer。

这是我在使用PkixCertPathBuilder时遇到的两个最常见的问题。

下面的代码没有回答你的问题(这是一个纯粹的Java解决方案)。 我现在才意识到,在输入所有内容后,它没有回答您的问题! 我忘了BouncyCastle有一个C#版本! 哎呀。

它仍然可以帮助您推出自己的链条构建器。 您可能不需要任何库或框架。

祝你好运!

http://juliusdavies.ca/commons-ssl/src/java/org/apache/commons/ssl/X509CertificateChainBuilder.java

/**
 * @param startingPoint the X509Certificate for which we want to find
 *                      ancestors
 *
 * @param certificates  A pool of certificates in which we expect to find
 *                      the startingPoint's ancestors.
 *
 * @return Array of X509Certificates, starting with the "startingPoint" and
 *         ending with highest level ancestor we could find in the supplied
 *         collection.
 */
public static X509Certificate[] buildPath(
  X509Certificate startingPoint, Collection certificates
) throws NoSuchAlgorithmException, InvalidKeyException,
         NoSuchProviderException, CertificateException {
    LinkedList path = new LinkedList();
    path.add(startingPoint);
    boolean nodeAdded = true;
    // Keep looping until an iteration happens where we don't add any nodes
    // to our path.
    while (nodeAdded) {
        // We'll start out by assuming nothing gets added.  If something
        // gets added, then nodeAdded will be changed to "true".
        nodeAdded = false;
        X509Certificate top = (X509Certificate) path.getLast();
        if (isSelfSigned(top)) {
            // We're self-signed, so we're done!
            break;
        }
        // Not self-signed.  Let's see if we're signed by anyone in the
        // collection.
        Iterator it = certificates.iterator();
        while (it.hasNext()) {
            X509Certificate x509 = (X509Certificate) it.next();
            if (verify(top, x509.getPublicKey())) {
                // We're signed by this guy!  Add him to the chain we're
                // building up.
                path.add(x509);
                nodeAdded = true;
                it.remove(); // Not interested in this guy anymore!
                break;
            }
            // Not signed by this guy, let's try the next guy.
        }
    }
    X509Certificate[] results = new X509Certificate[path.size()];
    path.toArray(results);
    return results;
}

需要以下两种附加方法:

isSelfSigned():

public static boolean isSelfSigned(X509Certificate cert)
    throws CertificateException, InvalidKeyException,
    NoSuchAlgorithmException, NoSuchProviderException {
    return verify(cert, cert.getPublicKey());
}

并验证():

public static boolean verify(X509Certificate cert, PublicKey key)
    throws CertificateException, InvalidKeyException,
    NoSuchAlgorithmException, NoSuchProviderException {
    String sigAlg = cert.getSigAlgName();
    String keyAlg = key.getAlgorithm();
    sigAlg = sigAlg != null ? sigAlg.trim().toUpperCase() : "";
    keyAlg = keyAlg != null ? keyAlg.trim().toUpperCase() : "";
    if (keyAlg.length() >= 2 && sigAlg.endsWith(keyAlg)) {
        try {
            cert.verify(key);
            return true;
        } catch (SignatureException se) {
            return false;
        }
    } else {
        return false;
    }
}