我在dotnet core v2.0中复制某些加密功能时遇到了麻烦。这是从.NET 4.5项目移植的代码
.NET 4.5代码
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;
}
在dotnet核心中,ToXmlString()
和FromXmlString()
方法未实现,因此我使用了辅助类解决方法。除此之外,dotnet核心实现可以工作,但是,如果输入数据和证书相同,它会产生不同的结果。
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;
}
修改
dotnet核心签名数据未通过.NET 4.5代码库中的签名验证检查。从理论上讲,签名方法应该没什么区别,所以这应该有效,但不是。
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");
}
}
}
有谁知道这两种数据签名方法之间是否存在兼容性问题?
编辑2
为了澄清这两个代码片段正在尝试:
在.NEt4.5和dotnet core v2.0中尝试同样的事情时会出现复杂情况。
似乎 框架,库和操作系统之间存在差异。 This answer声明RSACryptoServiceProvider
对象依赖于.NET 4.5中软件所在机器的 CryptoAPI ,而this informative post显示了这一点的差异在不同的环境/框架中实现。
我仍然在研究基于这些信息的解决方案,但遗留下来的核心问题,即上面使用此dotnet核心的签名数据无法通过.NET 4.5实现进行验证。
答案 0 :(得分:2)
<强>解决方案强>
为了能够在.NET 4.5中验证使用dotnet core v2.0中的X509 RSA私钥签名的数据
验证码(.NET 4.5)
public void VerifySignedData(byte[] originalData, byte[] signedData, X509Certificate2 certificate)
{
using (var rsa = (RSACryptoServiceProvider)certificate.PublicKey.Key)
{
if (rsa.VerifyData(originalData, CryptoConfig.MapNameToOID("SHA256"), signedData))
{
Console.WriteLine("RSA-SHA256 signature verified");
}
else
{
Console.WriteLine("RSA-SHA256 signature failed to verify");
}
}
}
签名代码(dotnet core v2.0)
private byte[] SignData(X509Certificate2 certificate, byte[] dataToSign)
{
// get xml params from current private key
var rsa = (RSA)certificate.PrivateKey;
var xml = RSAHelper.ToXmlString(rsa, true);
var parameters = RSAHelper.GetParametersFromXmlString(rsa, xml);
// generate new private key in correct format
var cspParams = new CspParameters()
{
ProviderType = 24,
ProviderName = "Microsoft Enhanced RSA and AES Cryptographic Provider"
};
var rsaCryptoServiceProvider = new RSACryptoServiceProvider(cspParams);
rsaCryptoServiceProvider.ImportParameters(parameters);
// sign data
var signedBytes = rsaCryptoServiceProvider.SignData(dataToSign, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
return signedBytes;
}
助手类
public static class RSAHelper
{
public static RSAParameters GetParametersFromXmlString(RSA rsa, string xmlString)
{
RSAParameters parameters = new RSAParameters();
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(xmlString);
if (xmlDoc.DocumentElement.Name.Equals("RSAKeyValue"))
{
foreach (XmlNode node in xmlDoc.DocumentElement.ChildNodes)
{
switch (node.Name)
{
case "Modulus": parameters.Modulus = Convert.FromBase64String(node.InnerText); break;
case "Exponent": parameters.Exponent = Convert.FromBase64String(node.InnerText); break;
case "P": parameters.P = Convert.FromBase64String(node.InnerText); break;
case "Q": parameters.Q = Convert.FromBase64String(node.InnerText); break;
case "DP": parameters.DP = Convert.FromBase64String(node.InnerText); break;
case "DQ": parameters.DQ = Convert.FromBase64String(node.InnerText); break;
case "InverseQ": parameters.InverseQ = Convert.FromBase64String(node.InnerText); break;
case "D": parameters.D = Convert.FromBase64String(node.InnerText); break;
}
}
}
else
{
throw new Exception("Invalid XML RSA key.");
}
return parameters;
}
public static string ToXmlString(RSA rsa, bool includePrivateParameters)
{
RSAParameters parameters = rsa.ExportParameters(includePrivateParameters);
return string.Format("<RSAKeyValue><Modulus>{0}</Modulus><Exponent>{1}</Exponent><P>{2}</P><Q>{3}</Q><DP>{4}</DP><DQ>{5}</DQ><InverseQ>{6}</InverseQ><D>{7}</D></RSAKeyValue>",
Convert.ToBase64String(parameters.Modulus),
Convert.ToBase64String(parameters.Exponent),
Convert.ToBase64String(parameters.P),
Convert.ToBase64String(parameters.Q),
Convert.ToBase64String(parameters.DP),
Convert.ToBase64String(parameters.DQ),
Convert.ToBase64String(parameters.InverseQ),
Convert.ToBase64String(parameters.D));
}
}
答案 1 :(得分:1)
如果你必须坚持使用4.5,那么你的.NET Framework代码就像它一样好。 (好吧,你可以消除XML格式的使用,直接使用ExportParameters
)
在.NET 4.6中,问题解决了{-1}}属性的软弃用(这只是告诉我StackOverflow上的每个人都不使用它):
PrivateKey
这与您应为.NET Core(所有版本)编写的代码相同。重构的部分原因是让人们离开using (RSA rsa = certificate.GetRSAPrivateKey())
{
return rsa.SignData(dataToSign, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
}
类型,这在非Windows系统上运行不佳。
验证码为
RSACryptoServiceProvider
代码少得多,类型安全性更强,没有PROV_RSA_FULL问题,没有密钥导出/导入......