É possível gerar programaticamente um certificado X509 usando apenas C #?

Estamos tentando gerar um certificado X509 (incluindo a chave privada) programaticamente usando C # e a biblioteca BouncyCastle . Nós tentamos usar alguns dos códigos desta amostra por Felix Kollmann, mas a parte da chave privada do certificado retorna null. Código e teste de unidade são como abaixo:

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 { ///  /// ///  /// Based on  ///  ///  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"); } } } 

Teste de unidade:

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

Só para esclarecer, um certificado X.509 não contém a chave privada. Às vezes, a palavra certificado é usada incorretamente para representar a combinação do certificado e a chave privada, mas são duas entidades distintas. O objective de usar certificados é enviá-los de forma mais ou menos aberta, sem enviar a chave privada, que deve ser mantida em segredo. Um object X509Certificate2 pode ter uma chave privada associada a ele (por meio de sua propriedade PrivateKey ), mas isso é apenas uma conveniência como parte do design dessa class.

Em seu primeiro exemplo de código do BouncyCastle, o newCert é realmente apenas o certificado e o DotNetUtilities.ToX509Certificate(newCert) é criado somente a partir do certificado.

Considerando que o formato PKCS # 12 requer a presença de uma chave privada, estou surpreso que a parte a seguir também funcione (considerando que você está chamando-a em um certificado que possivelmente não pode conhecer a chave privada):

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

( gen.Generate(kp.Private) assina o certificado usando a chave privada, mas não coloca a chave privada no certificado, o que não faria sentido.)

Se você quiser que seu método retorne o certificado e a chave privada, você poderá:

  • Retorna um object X509Certificate2 no qual você inicializou a propriedade PrivateKey
  • Construa uma loja PKCS # 12 e retorne seu conteúdo byte[] (como se fosse um arquivo). Passo 3 no link que você enviou ( espelho ) explica como construir uma loja PKCS # 12.

Retornar a estrutura byte[] (DER) para o próprio certificado X.509 não conterá a chave privada.

Se sua principal preocupação (de acordo com o caso de teste) é verificar se o certificado foi criado a partir de um par de chaves RSA, você pode verificar o tipo de sua chave pública.

Eu percebo que este é um post antigo, mas eu encontrei esses excelentes artigos que passam pelo processo:

Usando o Bouncy Castle do .NET

Intereting Posts