【问题标题】:Build certificate chain in BouncyCastle in C#在 C# 中的 BouncyCastle 中构建证书链
【发布时间】:2012-05-30 06:37:17
【问题描述】:

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

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

【问题讨论】:

    标签: c# bouncycastle


    【解决方案1】:

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

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

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

    【讨论】:

    • 第一个答案的一半为我做了。仅将我的最终用户证书添加到我所谓的中间存储中就是解决方案。
    【解决方案2】:

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

    【讨论】:

    • 有一点是肯定的,你的 isSelfSigned 方法比我的要好得多(issuer == subject)。
    • 我认为两者兼备会更好,这样你会抛出更少的异常
    • 为什么verify() 如此复杂?这是为了加快速度吗?
    猜你喜欢
    • 2018-06-03
    • 1970-01-01
    • 2013-01-14
    • 2012-09-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-16
    • 1970-01-01
    相关资源
    最近更新 更多