La exportación de certificados X.509 SIN clave privada
-
23-08-2019 - |
Pregunta
Me pareció que esto sería sencillo, pero al parecer no lo es. Tengo un certificado instalado que tiene una clave privada, exportable, y quiero exportar mediante programación con la clave pública sólo. En otras palabras, quiero un resultado equivalente a seleccionar "No exportar la clave privada" cuando se exporta a través certmgr y la exportación a .CER.
Parece que todos los métodos X509Certificate2.Export se exportar la clave privada si es que existe, como PKCS # 12, que es lo contrario de lo que quiero.
¿Hay alguna manera usando C # para lograr esto, o tengo que empezar a cavar en CAPICOM?
Solución
Para cualquier otra persona que pudiera haber tropezado con esto, lo he descubierto. Si especifica X509ContentType.Cert
como primer parámetro (y única) a X509Certificate.Export
, sólo se exporta la clave pública. Por otro lado, especificando X509ContentType.Pfx
incluye la clave privada si es que existe.
Podría haber jurado que estaba viendo un comportamiento diferente la semana pasada, pero ya tengo que tengan instalada la clave privada cuando tenía pruebas. Cuando he eliminado ese certificado hoy y empecé de nuevo desde cero, vi que no había llave privada en el certificado exportado.
Otros consejos
He encontrado el siguiente programa de ayuda para tranquilizar a mí mismo que la propiedad RawData
del certificado contiene sólo la clave pública (MSDN está claro en esto), y que la respuesta anterior con respecto X509ContentType.Cert
vs X509ContentType.Pfx
funciona como se espera:
using System;
using System.Linq;
using System.IdentityModel.Tokens;
using System.Security.Cryptography.X509Certificates;
class Program
{
static void Main( string[] args )
{
var certPath = @"C:\blah\somecert.pfx";
var certPassword = "somepassword";
var orig = new X509Certificate2( certPath, certPassword, X509KeyStorageFlags.Exportable );
Console.WriteLine( "Orig : RawData.Length = {0}, HasPrivateKey = {1}", orig.RawData.Length, orig.HasPrivateKey );
var certBytes = orig.Export( X509ContentType.Cert );
var certA = new X509Certificate2( certBytes );
Console.WriteLine( "cert A : RawData.Length = {0}, HasPrivateKey = {1}, certBytes.Length = {2}", certA.RawData.Length, certA.HasPrivateKey, certBytes.Length );
// NOTE that this the only place the byte count differs from the others
certBytes = orig.Export( X509ContentType.Pfx );
var certB = new X509Certificate2( certBytes );
Console.WriteLine( "cert B : RawData.Length = {0}, HasPrivateKey = {1}, certBytes.Length = {2}", certB.RawData.Length, certB.HasPrivateKey, certBytes.Length );
var keyIdentifier = ( new X509SecurityToken( orig ) ).CreateKeyIdentifierClause<X509RawDataKeyIdentifierClause>();
certBytes = keyIdentifier.GetX509RawData();
var certC = new X509Certificate2( certBytes );
Console.WriteLine( "cert C : RawData.Length = {0}, HasPrivateKey = {1}, certBytes.Length = {2}", certC.RawData.Length, certC.HasPrivateKey, certBytes.Length );
Console.WriteLine( "RawData equals original RawData: {0}", certC.RawData.SequenceEqual( orig.RawData ) );
Console.ReadLine();
}
}
Se muestra la siguiente información:
Orig : RawData.Length = 1337, HasPrivateKey = True cert A : RawData.Length = 1337, HasPrivateKey = False, certBytes.Length = 1337 cert B : RawData.Length = 1337, HasPrivateKey = True, certBytes.Length = 3187 cert C : RawData.Length = 1337, HasPrivateKey = False, certBytes.Length = 1337 RawData equals original RawData: True
Hay un OpenSSL .NET envoltorio pueden serle de utilidad.