I'm having trouble reproducing some cryptographic functionality in dotnet core v2.0. This is code ported from a .NET 4.5 project
.NET 4.5 code
public byte[] SignData(byte[] dataToSign, X509Certificate2 certificate)
{
var rsaCryptoServiceProvider = new RSACryptoServiceProvider();
var xml = certificate.PrivateKey.ToXmlString(true);
rsaCryptoServiceProvider.FromXmlString(xml);
var signedBytes = rsaCryptoServiceProvider.SignData(dataToSign, CryptoConfig.MapNameToOID("SHA256"));
return signedBytes;
}
In dotnet core the ToXmlString()
and FromXmlString()
methods are not implemented, so I used a helper class workaround. Aside from that the dotnet core implementation works but, given the same input data and certificate it produces a different outcome.
dotnet core v2.0 code
public byte[] SignData(byte[] dataToSign, X509Certificate2 certificate)
{
var rsaCryptoServiceProvider = new RSACryptoServiceProvider();
var rsa = (RSA)certificate.PrivateKey;
var xml = RSAHelper.ToXmlString(rsa);
var parameters = RSAHelper.GetParametersFromXmlString(rsa, xml);
rsaCryptoServiceProvider.ImportParameters(parameters);
SHA256 alg = SHA256.Create();
var signedBytes = rsaCryptoServiceProvider.SignData(dataToSign, alg);
return signedBytes;
}
EDIT
The dotnet core signed data fails a signature verification check in the .NET 4.5 codebase. Theoretically it should make no difference what the signing method was, so this should work but doesn't.
public void VerifySignature(byte[] signedData, byte[] unsignedData, X509Certificate2 certificate)
using (RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)certificate.PublicKey.Key)
{
if (rsa.VerifyData(unsignedData, CryptoConfig.MapNameToOID("SHA256"), signedData))
{
Console.WriteLine("RSA-SHA256 signature verified");
}
else
{
Console.WriteLine("RSA-SHA256 signature failed to verify");
}
}
}
Does anyone know if there are compatibility issues between the two methods of signing data?
EDIT 2
For clarification this is what both code snippets are attempting:
The complication comes when attempting the same thing in .NEt4.5 and dotnet core v2.0.
Seems there are differences between frameworks, libraries and OS's. This answer states that the RSACryptoServiceProvider
object relies on the CryptoAPI of the machine the software is on in .NET 4.5, and this informative post shows you the difference on how this is implemented in different environments/frameworks.
I'm still working on a solution based on this information but am left the central issue, namely that signed data using this dotnet core above cannot be verified by the .NET 4.5 implementation.
If you MUST stick with 4.5, your .NET Framework code is as good as it gets. (Well, you could eliminate the usage of the XML format and just use ExportParameters
directly)
In .NET 4.6 the problem was solved with the soft-deprecation (which just means I tell everyone on StackOverflow to not use it) of the PrivateKey
property:
using (RSA rsa = certificate.GetRSAPrivateKey())
{
return rsa.SignData(dataToSign, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
}
This is the same code you should write for .NET Core (all versions). Part of the reason for the refactoring was to get people off of the RSACryptoServiceProvider
type, which doesn't work well on non-Windows systems.
The verification code would be
using (RSA rsa = certificate.GetRSAPublicKey())
{
return rsa.VerifyData(
dataToSign,
signature,
HashAlgorithmName.SHA256,
RSASignaturePadding.Pkcs1);
}
Much less code, stronger type-safety, doesn't have the PROV_RSA_FULL problem, no key exporting/importing...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With