2010-09-22 7 views
34

Nous essayons de générer un certificat X509 (y compris la clé privée) par programmation C# et la bibliothèque BouncyCastle. Nous avons essayé d'utiliser une partie du code de this sample by Felix Kollmann mais la partie clé privée du certificat renvoie null. Code et test unitaire sont comme ci-dessous:Est-il possible de générer par programme un certificat X509 en utilisant uniquement 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"); 
     } 
    } 
} 

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

Répondre

27

Juste pour clarifier les choses, un certificat X.509 ne contient pas la clé privée. Le mot certificat est parfois mal utilisé pour représenter la combinaison du certificat et la clé privée, mais ils sont deux entités distinctes. Le but de l'utilisation des certificats est de les envoyer plus ou moins ouvertement, sans envoyer la clé privée, qui doit être gardée secrète. Un objet X509Certificate2 peut avoir une clé privée qui lui est associée (via sa propriété PrivateKey), mais c'est seulement une commodité dans le cadre de la conception de cette classe.

Dans votre premier exemple de code BouncyCastle, newCert est réellement le certificat et DotNetUtilities.ToX509Certificate(newCert) est créé à partir du certificat uniquement. Considérant que le format PKCS # 12 nécessite la présence d'une clé privée, je suis assez surpris que la partie suivante fonctionne même si vous l'appelez sur un certificat qui ne peut pas connaître la clé privée. :

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

(gen.Generate(kp.Private) signe le certificat à l'aide de la clé privée, mais ne met pas la clé privée dans le certificat, ce qui ne serait pas logique.)

Si vous voulez que votre méthode pour revenir à la fois le certificat et la clé privée vous pouvez soit:

  • Retour un objet X509Certificate2 dans lequel vous avez initialisé la propriété PrivateKey
  • construire un magasin PKCS # 12 et retourne son contenu byte[] (comme si elle était un fichier). Step 3 in the link you've sent (mirror) explique comment construire un magasin PKCS # 12.

De retour la structure du certificat X.509 lui-même ne byte[] (DER) contiennent la clé privée.

Si votre principale préoccupation (selon votre cas de test) est de vérifier que le certificat a été construit à partir d'une paire de clés RSA, vous pouvez vérifier le type de sa clé publique à la place.

+0

+1, Très belle explication. Avez-vous regardé la spécification PKCS # 12? C'est * BRUTAL *! Je pense que presque tout est possible avec PKCS # 12; vous n'avez pas besoin d'avoir une clé privée. Habituellement, PKCS # 12 est utilisé pour contenir une clé privée et un certificat associé, mais il existe d'autres possibilités. –

+4

Le lien fourni pour l'étape 3 semble être en panne. Heureusement archive.org a encore le contenu: http://web.archive.org/web/20100504192226/http://www.fkollmann.de/v2/post/Creating-certificates-using-BouncyCastle.aspx – Kyle

+0

@Zenox, n'hésitez pas à modifier ma réponse et le remplacer, cela devrait vous donner quelques points aussi. – Bruno

6

Je sais que c'est un ancien poste mais j'ai trouvé ces excellents articles qui passent par le processus:

Using Bouncy Castle from .NET

+1

pour ces sujets, meta est votre ami: https://meta.stackexchange.com/questions/8231/are-answers-that-just- contain-links-elsewhere-really-good-answers – MatthewMartin

+1

Obtention d'un avertissement de sécurité pour le lien .. –

+1

J'ai mis à jour le lien. – haymansfield

Questions connexes