Saltar al contenido

Firma de cómputo SignedXml con SHA256

Esta es la respuesta más acertada que te podemos brindar, sin embargo obsérvala pausadamente y analiza si se puede adaptar a tu trabajo.

Solución:

X509Certificate2 carga el privado key del archivo pfx al Proveedor criptográfico mejorado de Microsoft v1.0 (tipo de proveedor 1 alias PROV_RSA_FULL) que no es compatible con SHA-256.

Los proveedores criptográficos basados ​​en CNG (introducidos en Vista y Server 2008) admiten más algoritmos que los proveedores basados ​​en CryptoAPI, pero el código .NET todavía parece funcionar con clases basadas en CryptoAPI como RSACryptoServiceProvider en vez de RSACng así que tenemos que trabajar alrededor de estas limitaciones.

Sin embargo, otro proveedor de CryptoAPI, Proveedor de cifrado RSA y AES mejorado de Microsoft (tipo de proveedor 24 alias PROV_RSA_AES) admite SHA-256. Así que si obtenemos el privado key en este proveedor, podemos firmar con él.

Primero, tendrá que ajustar su X509Certificate2 constructor para habilitar el key para ser exportado fuera del proveedor que X509Certificate2 lo pone agregando el X509KeyStorageFlags.Exportable bandera:

X509Certificate2 cert = new X509Certificate2(
    @"location of pks file", "password",
    X509KeyStorageFlags.Exportable);

Y exportar lo privado. key:

var exportedKeyMaterial = cert.PrivateKey.ToXmlString(
    /* includePrivateParameters = */ true);

Luego crea una nueva RSACryptoServiceProvider instancia para un proveedor que admita SHA-256:

var key = new RSACryptoServiceProvider(
    new CspParameters(24 /* PROV_RSA_AES */));
key.PersistKeyInCsp = false;

E importar lo privado key en ello:

key.FromXmlString(exportedKeyMaterial);

Cuando hayas creado tu SignedXml ejemplo, dígale que use key en vez de cert.PrivateKey:

signedXml.SigningKey = key;

Y ahora funcionará.

Aquí está la lista de tipos de proveedores y sus códigos en MSDN.

Aquí está el código completo ajustado para su ejemplo:

CryptoConfig.AddAlgorithm(typeof(RSAPKCS1SHA256SignatureDescription), "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256");

X509Certificate2 cert = new X509Certificate2(@"location of pks file", "password", X509KeyStorageFlags.Exportable);

// Export private key from cert.PrivateKey and import into a PROV_RSA_AES provider:
var exportedKeyMaterial = cert.PrivateKey.ToXmlString( /* includePrivateParameters = */ true);
var key = new RSACryptoServiceProvider(new CspParameters(24 /* PROV_RSA_AES */));
key.PersistKeyInCsp = false;
key.FromXmlString(exportedKeyMaterial);

XmlDocument doc = new XmlDocument();
doc.PreserveWhitespace = true;
doc.Load(@"input.xml");

SignedXml signedXml = new SignedXml(doc);
signedXml.SigningKey = key;
signedXml.SignedInfo.SignatureMethod = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256";

// 
// Add a signing reference, the uri is empty and so the whole document 
// is signed. 
Reference reference = new Reference();
reference.AddTransform(new XmlDsigEnvelopedSignatureTransform());
reference.AddTransform(new XmlDsigExcC14NTransform());
reference.Uri = "";
signedXml.AddReference(reference);

// 
// Add the certificate as key info, because of this the certificate 
// with the public key will be added in the signature part. 
KeyInfo keyInfo = new KeyInfo();
keyInfo.AddClause(new KeyInfoX509Data(cert));
signedXml.KeyInfo = keyInfo;
// Generate the signature. 
signedXml.ComputeSignature();

La exportación y la reimportación ya se han dado como respuesta, pero hay un par de otras opciones que debe tener en cuenta.

1. Use GetRSAPrivateKey y .NET 4.6.2 (actualmente en versión preliminar)

El método GetRSAPrivateKey (extensión) devuelve una instancia de RSA del “mejor tipo disponible” para el key y plataforma (a diferencia de la propiedad PrivateKey que “todo el mundo sabe” devuelve RSACryptoServiceProvider).

En 99.99(etc)% de todos los RSA privados keys el objeto devuelto por este método es capaz de generar firmas SHA-2.

Si bien ese método se agregó en .NET 4.6(.0), el requisito de 4.6.2 existe en este caso porque la instancia de RSA que devolvió GetRSAPrivateKey no funcionó con SignedXml. Eso ya se ha solucionado (162556).

2. Vuelva a abrir la key sin exportar

Personalmente, no me gusta este enfoque porque usa la propiedad PrivateKey (ahora heredada) y la clase RSACryptoServiceProvider. Sin embargo, tiene la ventaja de funcionar en todas las versiones de .NET Framework (aunque no en .NET Core en sistemas que no sean Windows, ya que RSACryptoServiceProvider es solo para Windows).

private static RSACryptoServiceProvider UpgradeCsp(RSACryptoServiceProvider currentKey)

    const int PROV_RSA_AES = 24;
    CspKeyContainerInfo info = currentKey.CspKeyContainerInfo;

    // WARNING: 3rd party providers and smart card providers may not handle this upgrade.
    // You may wish to test that the info.ProviderName value is a known-convertible value.

    CspParameters cspParameters = new CspParameters(PROV_RSA_AES)
    
        KeyContainerName = info.KeyContainerName,
        KeyNumber = (int)info.KeyNumber,
        Flags = CspProviderFlags.UseExistingKey,
    ;

    if (info.MachineKeyStore)
    = CspProviderFlags.UseMachineKeyStore;
    

    if (info.ProviderType == PROV_RSA_AES)
    
        // Already a PROV_RSA_AES, copy the ProviderName in case it's 3rd party
        cspParameters.ProviderName = info.ProviderName;
    

    return new RSACryptoServiceProvider(cspParameters);

Si ya tiene cert.PrivateKey emitido como RSACryptoServiceProvider, puede enviarlo a través de UpgradeCsp. Ya que esto está abriendo un existente key no habrá material adicional escrito en el disco, usa los mismos permisos que el existente keyy no requiere que hagas una exportación.

Pero (¡CUIDADO!) NO establezca PersistKeyInCsp=falseporque eso borrará el original key cuando el clon está cerrado.

Si se encuentra con este problema después de actualizar a .Net 4.7.1 o superior:

.Net 4.7 y anteriores:

SignedXml signedXml = new SignedXml(doc);
signedXml.SigningKey = cert.PrivateKey;

.Net 4.7.1 y superior:

SignedXml signedXml = new SignedXml(doc);
signedXml.SigningKey = cert.GetRSAPrivateKey();

Créditos para vladimir kocjancic

Sección de Reseñas y Valoraciones

Acuérdate de que puedes agregar una reseña si te ayudó.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *