2010-09-22 18 views
34

Stiamo provando a generare un certificato X509 (inclusa la chiave privata) a livello di programmazione utilizzando C# e la libreria BouncyCastle. Abbiamo provato a utilizzare parte del codice da this sample by Felix Kollmann ma la parte della chiave privata del certificato restituisce null. Codice e unit test sono le seguenti:È possibile generare un certificato X509 in modo programmatico utilizzando 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"); 
     } 
    } 
} 

prova Unità:

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

risposta

27

solo per chiarire, un certificato X.509 non contiene la chiave privata. Il termine certificato viene talvolta utilizzato in modo improprio per rappresentare la combinazione del certificato e della chiave privata, ma sono due entità distinte. L'intero scopo dell'utilizzo dei certificati è di inviarli più o meno apertamente, senza inviare la chiave privata, che deve essere tenuta segreta. Un oggetto X509Certificate2 può avere una chiave privata ad esso associata (tramite la sua proprietà PrivateKey), ma questa è solo una comodità come parte del design di questa classe.

Nel primo esempio di codice BouncyCastle, newCert è in realtà solo il certificato e DotNetUtilities.ToX509Certificate(newCert) è stato creato solo dal certificato.

Considerando che il formato PKCS # 12 richiede la presenza di una chiave privata, sono piuttosto sorpreso che la parte seguente funzioni anche (considerando che si sta chiamando su un certificato che non può conoscere la chiave privata) :

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

(gen.Generate(kp.Private) firma il certificato con la chiave privata, ma non mette la chiave privata del certificato, che non avrebbe senso.)

Se si desidera che il metodo restituisca sia il certificato e la chiave privata è possibile:

  • restituire un oggetto X509Certificate2 in cui è stato inizializzato il PrivateKey proprietà
  • Costruire un PKCS # 12 vendite e restituisce il suo contenuto byte[] (come se fosse un file). Step 3 in the link you've sent (mirror) spiega come creare un negozio PKCS # 12.

La restituzione della struttura byte[] (DER) per il certificato X.509 non conterrà la chiave privata.

Se il problema principale (in base al test case) è verificare che il certificato sia stato creato da una coppia di chiavi RSA, è possibile invece controllare il tipo della sua chiave pubblica.

+0

+1, molto bella spiegazione. Hai esaminato la specifica PKCS # 12? È * BRUTALE *! Penso che tutto sia possibile con PKCS # 12; non è necessario avere una chiave privata. Di solito, PKCS # 12 viene utilizzato per contenere una chiave privata e un certificato associato, ma esistono altre possibilità. –

+4

Il collegamento fornito per il passaggio 3 sembra essere inattivo. Fortunatamente archive.org ha ancora i contenuti: http://web.archive.org/web/20100504192226/http://www.fkollmann.de/v2/post/Creating-certificates-using-BouncyCastle.aspx – Kyle

+0

@Zenox, sentiti libero di modificare la mia risposta e sostituirla, questo dovrebbe darti un paio di punti. – Bruno

6

Mi rendo conto che questo è un vecchio post, ma ho trovato questi ottimi articoli che vanno attraverso il processo:

Using Bouncy Castle from .NET

+1

per tali argomenti, meta è tuo amico: https://meta.stackexchange.com/questions/8231/are-answers-that-just- contiene-links-altrove-veramente-buone-risposte – MatthewMartin

+1

Ottenere avvisi di sicurezza per il collegamento .. –

+1

Ho aggiornato il collegamento. – haymansfield

Problemi correlati