È ansible generare un certificato X509 in modo programmatico utilizzando solo C #?

Stiamo provando a generare un certificato X509 (inclusa la chiave privata) a livello di codice utilizzando C # e la libreria BouncyCastle . Abbiamo provato ad utilizzare parte del codice di questo esempio di Felix Kollmann ma la parte della chiave privata del certificato restituisce null. Codice e unit test sono i seguenti:

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

Test unitario:

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

Giusto per chiarire, un certificato X.509 non contiene la chiave privata. Talvolta il certificato di parole viene utilizzato in modo errato per rappresentare la combinazione del certificato e della chiave privata, ma si tratta di due entity framework 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 object 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 class.

Nel tuo 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 abbastanza sorpreso dal fatto che la parte seguente funzioni anche (considerando che la stai 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 utilizzando la chiave privata, ma non inserisce la chiave privata nel certificato, il che non avrebbe senso.)

Se vuoi che il tuo metodo restituisca sia il certificato che la chiave privata potresti:

  • Restituisce un object X509Certificate2 in cui è stata inizializzata la proprietà PrivateKey
  • Costruisci un archivio PKCS # 12 e restituisce il suo contenuto in byte[] (come se fosse un file). Il passaggio 3 nel link inviato ( mirror ) spiega come creare un negozio PKCS # 12.

Restituire la struttura byte[] (DER) per il certificato X.509 stesso non conterrà la chiave privata.

Se la tua preoccupazione principale (in base al tuo caso di test) è verificare che il certificato sia stato creato da una coppia di chiavi RSA, puoi invece controllare il tipo della sua chiave pubblica.

Mi rendo conto che questo è un vecchio post ma ho trovato questi articoli eccellenti che attraversano il processo:

Utilizzando Bouncy Castle da .NET