2012-05-23 4 views
7

У меня есть куча корневых и промежуточных сертификатов, заданных как массивы байтов, и у меня также есть сертификат конечного пользователя. Я хочу создать цепочку сертификатов для данного сертификата конечного пользователя. В .NET framework я могу сделать это следующим образом:Создать цепочку сертификатов в BouncyCastle в C#

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>(); 
} 

Редактировать: Я добавил строку, неподвижную мою проблему. Он прокомментирован всеми шапками. Дело закрыто.

ответ

7

Я делал это на Java несколько раз. Учитывая, что API, по-видимому, является прямым портом Java, я возьму удар.

  1. Я уверен, что когда вы добавляете хранилище в строитель, эта коллекция должна содержать все сертификаты в цепочке, которые должны быть построены, а не только промежуточные. Поэтому необходимо добавить rootCerts и primary.
  2. Если это не решит проблему самостоятельно, я бы попробовал также указать желаемый сертификат другим способом. Вы можете сделать одну из двух вещей:
    • Внесите свой собственный селектор, который всегда соответствует только вашему желаемому сертификату (первичный в примере).
    • Вместо установки держателя. Сертификат, установите один или несколько критериев на держателе. Например, setSubject, setSubjectPublicKey, setIssuer.

Это две наиболее распространенные проблемы, которые я имел с PkixCertPathBuilder.

+1

Половина первого ответа сделал это для меня. Решением был добавлен только мой сертификат конечного пользователя к тому, что я назвал промежуточным хранилищем. – Dialecticus

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; 
    } 
} 
+0

Одно можно сказать наверняка, ваш метод isSelfSigned лучше, чем мой (эмитент == subject). – Dialecticus

+0

Думаю, было бы лучше иметь и то, и другое, поэтому вы бы выбрали меньше исключений –

Смежные вопросы