2010-09-22 16 views
34

Estamos intentando generar un certificado X509 (incluida la clave privada) programáticamente utilizando C# y la biblioteca BouncyCastle. Hemos intentado utilizar parte del código de this sample by Felix Kollmann, pero la parte de la clave privada del certificado devuelve nulo. Código y prueba de la unidad son las siguientes:¿Es posible generar un certificado X509 programáticamente utilizando solo C#?

using System; 
using System.Collections; 
using Org.BouncyCastle.Asn1; 
using Org.BouncyCastle.Asn1.X509; 
using Org.BouncyCastle.Crypto; 
using Org.BouncyCastle.Crypto.Generators; 
using Org.BouncyCastle.Crypto.Prng; 
using Org.BouncyCastle.Math; 
using Org.BouncyCastle.Security; 
using Org.BouncyCastle.X509; 

namespace MyApp 
{ 
    public class CertificateGenerator 
    { 
     /// <summary> 
     /// 
     /// </summary> 
     /// <remarks>Based on <see cref="http://www.fkollmann.de/v2/post/Creating-certificates-using-BouncyCastle.aspx"/></remarks> 
     /// <param name="subjectName"></param> 
     /// <returns></returns> 
     public static byte[] GenerateCertificate(string subjectName) 
     { 
      var kpgen = new RsaKeyPairGenerator(); 

      kpgen.Init(new KeyGenerationParameters(new SecureRandom(new CryptoApiRandomGenerator()), 1024)); 

      var kp = kpgen.GenerateKeyPair(); 

      var gen = new X509V3CertificateGenerator(); 

      var certName = new X509Name("CN=" + subjectName); 
      var serialNo = BigInteger.ProbablePrime(120, new Random()); 

      gen.SetSerialNumber(serialNo); 
      gen.SetSubjectDN(certName); 
      gen.SetIssuerDN(certName); 
      gen.SetNotAfter(DateTime.Now.AddYears(100)); 
      gen.SetNotBefore(DateTime.Now.Subtract(new TimeSpan(7, 0, 0, 0))); 
      gen.SetSignatureAlgorithm("MD5WithRSA"); 
      gen.SetPublicKey(kp.Public); 

      gen.AddExtension(
       X509Extensions.AuthorityKeyIdentifier.Id, 
       false, 
       new AuthorityKeyIdentifier(
        SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(kp.Public), 
        new GeneralNames(new GeneralName(certName)), 
        serialNo)); 

      gen.AddExtension(
       X509Extensions.ExtendedKeyUsage.Id, 
       false, 
       new ExtendedKeyUsage(new ArrayList() { new DerObjectIdentifier("1.3.6.1.5.5.7.3.1") })); 

      var newCert = gen.Generate(kp.Private); 
      return DotNetUtilities.ToX509Certificate(newCert).Export(System.Security.Cryptography.X509Certificates.X509ContentType.Pkcs12, "password"); 
     } 
    } 
} 

Unidad de prueba:

using System.Security.Cryptography; 
using System.Security.Cryptography.X509Certificates; 
using Microsoft.VisualStudio.TestTools.UnitTesting; 

namespace MyApp 
{ 
    [TestClass] 
    public class CertificateGeneratorTests 
    { 
     [TestMethod] 
     public void GenerateCertificate_Test_ValidCertificate() 
     { 
      // Arrange 
      string subjectName = "test"; 

      // Act 
      byte[] actual = CertificateGenerator.GenerateCertificate(subjectName); 

      // Assert 
      var cert = new X509Certificate2(actual, "password"); 
      Assert.AreEqual("CN=" + subjectName, cert.Subject); 
      Assert.IsInstanceOfType(cert.PrivateKey, typeof(RSACryptoServiceProvider)); 
     } 
    } 
} 

Respuesta

27

Solo para aclarar, un certificado X.509 no contiene la clave privada. La palabra certificado a veces se usa incorrectamente para representar la combinación del certificado y la clave privada, pero son dos entidades distintas. El objetivo de usar certificados es enviarlos más o menos abiertamente, sin enviar la clave privada, que debe mantenerse en secreto. Un objeto X509Certificate2 puede tener una clave privada asociada (a través de su propiedad PrivateKey), pero eso es solo una conveniencia como parte del diseño de esta clase.

En su primer ejemplo de código BouncyCastle, newCert es en realidad solo el certificado y DotNetUtilities.ToX509Certificate(newCert) se basa únicamente en el certificado.

Considerando que el formato PKCS # 12 requiere la presencia de una clave privada, me sorprende que la siguiente parte funcione (considerando que la está llamando en un certificado que posiblemente no pueda conocer la clave privada) :

.Export(System.Security.Cryptography.X509Certificates.X509ContentType.Pkcs12, 
    "password"); 

(gen.Generate(kp.Private) firma el certificado con la clave privada, pero no pone la clave privada en el certificado, que no tendría sentido.)

Si desea que su método devuelva tanto el certificado y la clave privada que podría:

  • devolver un objeto X509Certificate2 en la que ha inicializado el PrivateKey propiedad
  • Construir una tienda de PKCS # 12 y devuelve su contenido byte[] (como si fuera un archivo). Step 3 in the link you've sent (mirror) explica cómo construir una tienda PKCS # 12.

La devolución de la estructura byte[] (DER) para el certificado X.509 en sí no contendrá la clave privada.

Si su principal preocupación (de acuerdo con su caso de prueba) es comprobar que el certificado se creó a partir de un par de claves RSA, puede verificar el tipo de su clave pública en su lugar.

+0

+1, muy buena explicación. ¿Has mirado la especificación PKCS # 12? ¡Es * BRUTAL *! Creo que casi todo es posible con PKCS # 12; no necesita tener una clave privada. Generalmente, PKCS # 12 se usa para mantener una clave privada y un certificado asociado, pero hay otras posibilidades. –

+4

El enlace proporcionado para el paso 3 parece estar inactivo. Afortunadamente, archive.org aún tiene los contenidos: http://web.archive.org/web/20100504192226/http://www.fkollmann.de/v2/post/Creating-certificates-using-BouncyCastle.aspx – Kyle

+0

@Zenox, siéntete libre de editar mi respuesta y reemplazarla, esto también debería darte un par de puntos. – Bruno

6

me di cuenta que es una entrada antigua, pero me encontré con estos excelentes artículos que pasan por el proceso:

Using Bouncy Castle from .NET

+1

para tales temas, meta es tu amigo: https://meta.stackexchange.com/questions/8231/are-answers-that-just- contain-links-elsewhere-really-good-answers – MatthewMartin

+1

Obteniendo advertencia de seguridad para el enlace .. –

+1

He actualizado el enlace. – haymansfield

Cuestiones relacionadas