Te sugerimos que revises esta resolución en un ambiente controlado antes de enviarlo a producción, un saludo.
Solución:
RSA + SHA256 puede y funcionará…
Tu ejemplo posterior puede no funciona todo el tiempo, debe usar el OID del algoritmo hash, en lugar de su nombre. Según su primer ejemplo, esto se obtiene de una llamada a CryptoConfig.MapNameToOID(AlgorithmName)
donde AlgorithmName
es lo que está proporcionando (es decir, “SHA256”).
Lo primero que vas a necesitar es el certificado con el privado key. Normalmente leo el mío desde la tienda LocalMachine o CurrentUser usando un público key expediente (.cer
) para identificar el privado keyy luego enumere los certificados y haga coincidir el hash…
X509Certificate2 publicCert = new X509Certificate2(@"C:mycertificate.cer");
//Fetch private key from the local machine store
X509Certificate2 privateCert = null;
X509Store store = new X509Store(StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);
foreach( X509Certificate2 cert in store.Certificates)
if (cert.GetCertHashString() == publicCert.GetCertHashString())
privateCert = cert;
Independientemente de cómo llegue allí, una vez que haya obtenido un certificado con un key necesitamos reconstruirlo. Esto puede ser necesario debido a la forma en que el certificado crea su privacidad. key, pero no estoy muy seguro de por qué. De todos modos, hacemos esto exportando primero el key y luego volver a importarlo usando el formato intermedio que desee, el más fácil es xml:
//Round-trip the key to XML and back, there might be a better way but this works
RSACryptoServiceProvider key = new RSACryptoServiceProvider();
key.FromXmlString(privateCert.PrivateKey.ToXmlString(true));
Una vez hecho esto, ahora podemos firmar un dato de la siguiente manera:
//Create some data to sign
byte[] data = new byte[1024];
//Sign the data
byte[] sig = key.SignData(data, CryptoConfig.MapNameToOID("SHA256"));
Por último, la verificación se puede hacer directamente con el público del certificado. key sin necesidad de la reconstrucción como hicimos con la privada key:
key = (RSACryptoServiceProvider)publicCert.PublicKey.Key;
if (!key.VerifyData(data, CryptoConfig.MapNameToOID("SHA256"), sig))
throw new CryptographicException();
El uso de privateKey.toXMLString(true) o llave privada.exportParameters(true) no se pueden utilizar en un entorno seguro, ya que requieren su privacidad key ser exportable, lo cual NO es una buena práctica.
Una mejor solución es cargar explícitamente el proveedor de cifrado “Mejorado” como tal:
// Find my openssl-generated cert from the registry
var store = new X509Store(StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);
var certificates = store.Certificates.Find(X509FindType.FindBySubjectName, "myapp.com", true);
var certificate = certificates[0];
store.Close();
// Note that this will return a Basic crypto provider, with only SHA-1 support
var privKey = (RSACryptoServiceProvider)certificate.PrivateKey;
// Force use of the Enhanced RSA and AES Cryptographic Provider with openssl-generated SHA256 keys
var enhCsp = new RSACryptoServiceProvider().CspKeyContainerInfo;
var cspparams = new CspParameters(enhCsp.ProviderType, enhCsp.ProviderName, privKey.CspKeyContainerInfo.KeyContainerName);
privKey = new RSACryptoServiceProvider(cspparams);
Así es como lidié con ese problema:
X509Certificate2 privateCert = new X509Certificate2("certificate.pfx", password, X509KeyStorageFlags.Exportable);
// This instance can not sign and verify with SHA256:
RSACryptoServiceProvider privateKey = (RSACryptoServiceProvider)privateCert.PrivateKey;
// This one can:
RSACryptoServiceProvider privateKey1 = new RSACryptoServiceProvider();
privateKey1.ImportParameters(privateKey.ExportParameters(true));
byte[] data = Encoding.UTF8.GetBytes("Data to be signed");
byte[] signature = privateKey1.SignData(data, "SHA256");
bool isValid = privateKey1.VerifyData(data, "SHA256", signature);
Reseñas y valoraciones
Te invitamos a añadir valor a nuestra información colaborando tu veteranía en las anotaciones.