15

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

编辑:我添加了解决我的问题的行。它的注释全部大写。结案。

4

2 回答 2

9

我已经用 Java 做过很多次了。鉴于 API 似乎是 Java 的一个直接端口,我会尝试一下。

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

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

于 2012-05-28T17:28:08.520 回答
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;
    }
}
于 2012-05-30T18:33:23.670 回答