2012-05-23 12 views
7

Tengo un montón de certificados raíz e intermedios proporcionados como matrices de bytes, y también tengo un certificado de usuario final. Quiero construir una cadena de certificados para un certificado de usuario final dado. En .NET framework puedo hacerlo así:Crear cadena de certificados en BouncyCastle en 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>(); 
} 

¿Cómo hacerlo en BouncyCastle? Probé con código de abajo pero me da 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>(); 
} 

Editar: he añadido la línea que fija mi problema. Se comenta con mayúsculas. Caso cerrado.

Respuesta

7

He hecho esto en Java varias veces. Dado que la API parece ser un puerto directo de Java, tomaré una puñalada.

  1. Estoy bastante seguro de que cuando se agrega la tienda al constructor, se espera que esa colección contenga todos los certs de la cadena que se construirán, no solo los intermedios. Por lo tanto, deberían agregarse rootCerts y primary.
  2. Si eso no resuelve el problema por sí mismo, también trataría de especificar el certificado deseado de una manera diferente. Puede hacer una de estas dos cosas:
    • Implemente su propio Selector que siempre coincida con su certificado deseado (primario en el ejemplo).
    • En lugar de establecer el titular.Certificado, establezca uno o más criterios en el titular. Por ejemplo, setSubject, setSubjectPublicKey, setIssuer.

Esos son los dos problemas más comunes que tuve con PkixCertPathBuilder.

+1

A mitad de la primera respuesta hizo es para mi. Agregar solo mi certificado de usuario final a lo que llamé tienda intermedia fue la solución. – Dialecticus

6

El código siguiente no responde a su pregunta (es una solución pura de Java). ¡Solo me acabo de dar cuenta ahora después de tipear todo lo que no responde a tu pregunta! ¡Olvidé que BouncyCastle tiene una versión de C#! Oops.

Aún podría ayudarlo a hacer rodar su propio generador de cadenas. Probablemente no necesites ninguna biblioteca o framework.

¡Buena suerte!

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

requiere que estos dos métodos adicionales:

isSelfSigned():

public static boolean isSelfSigned(X509Certificate cert) 
    throws CertificateException, InvalidKeyException, 
    NoSuchAlgorithmException, NoSuchProviderException { 

    return verify(cert, cert.getPublicKey()); 
} 

y verifique():

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

Una cosa es segura, su método IsSigned es mucho mejor que el mío (emisor == subject). – Dialecticus

+0

Creo que sería mejor tener ambas cosas, así que lanzarías menos excepciones –

Cuestiones relacionadas