with - ¿Es posible generar programáticamente un certificado X509 usando solo C#?
sign file with private key c# (2)
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 esta muestra de Felix Kollmann pero la parte de clave privada del certificado devuelve nulo. El código y la prueba unitaria son los siguientes:
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");
}
}
}
Prueba de unidad:
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));
}
}
}
Me doy cuenta de que esta es una publicación anterior, pero encontré estos excelentes artículos que pasan por el proceso:
Solo para aclarar, un certificado X.509 no contiene la clave privada. La palabra certificado a veces se utiliza 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 realmente solo el certificado y DotNetUtilities.ToX509Certificate(newCert)
se DotNetUtilities.ToX509Certificate(newCert)
solo a partir del certificado.
Teniendo en cuenta que el formato PKCS # 12 requiere la presencia de una clave privada, estoy bastante sorprendido de 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 coloca la clave privada en el certificado, lo que no tendría sentido).
Si desea que su método devuelva tanto el certificado como la clave privada, puede:
- Devuelve un objeto
X509Certificate2
en el que ha inicializado la propiedadPrivateKey
- Construya una tienda PKCS # 12 y devuelve su contenido
byte[]
(como si fuera un archivo). El paso 3 en el enlace que ha enviado ( espejo ) 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.