在C#中使用BouncyCastle构建证书链

时间:2012-05-23 17:01:00

标签: c# bouncycastle

我有一堆以字节数组形式提供的根证书和中间证书,我也有最终用户证书。我想为给定的最终用户证书构建证书链。在.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>();
}

如何在BouncyCastle中完成?我尝试使用下面的代码,但我得到了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>();
}

修改:我添加了修复问题的行。它评论了全部大写。案件结案。

2 个答案:

答案 0 :(得分:8)

我已经在Java中做了很多次。鉴于API似乎是Java的直接端口,我将采取刺激。

  1. 我很确定当您将商店添加到构建器时,该集合应该包含要构建的链中的所有证书,而不仅仅是中间证书。所以应该添加rootCerts和primary。
  2. 如果这不能解决问题,我会尝试以不同的方式指定所需的证书。你可以做以下两件事之一:
    • 实现您自己的Selector,它始终只匹配您所需的证书(示例中为primary)。
    • 不是设置holder.Certificate,而是在holder上设置一个或多个条件。例如,setSubject,setSubjectPublicKey,setIssuer。
  3. 这是我使用PkixCertPathBuilder时遇到的两个最常见的问题。

答案 1 :(得分:6)

下面的代码没有回答您的问题(这是一个纯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;
    }
}