我有一些遗留的.NET Framework代码用于签名和检查我需要移植到.NET Core的签名。
这是现有的.NET Framework代码:
public static bool CheckSignature_NetFramework(string payload, string signature, string publicKey)
{
try
{
using (var rsa = RSA.Create())
using (var shaHash = SHA256.Create())
{
rsa.FromXmlString(publicKey);
var hash = shaHash.ComputeHash(Encoding.UTF8.GetBytes(payload));
var rsaDeformatter = new RSAPKCS1SignatureDeformatter(rsa);
rsaDeformatter.SetHashAlgorithm("SHA256");
return rsaDeformatter.VerifySignature(hash, Convert.FromBase64String(signature));
}
}
catch
{
return false;
}
}
public static string CreateSignature_NetFramework(string payload, string privateKey)
{
using (var rsa = RSA.Create())
using (var shaHash = SHA256.Create())
{
rsa.FromXmlString(privateKey);
var hash = shaHash.ComputeHash(Encoding.UTF8.GetBytes(payload));
var rsaFormatter = new RSAPKCS1SignatureFormatter(rsa);
rsaFormatter.SetHashAlgorithm("SHA256");
return Convert.ToBase64String(rsaFormatter.CreateSignature(hash));
}
}
这就是我提出的等效.NET Core实现:
public static bool CheckSignature_NetCore(string payload, string signature, string publicKey)
{
try
{
using (var rsa = RSA.Create())
using (var shaHash = SHA256.Create())
{
rsa.FromXmlString(publicKey);
var hash = shaHash.ComputeHash(Encoding.UTF8.GetBytes(payload));
return rsa.VerifyData(hash, Convert.FromBase64String(signature), HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
}
}
catch
{
return false;
}
}
public static string CreateSignature_NetCore(string payload, string privateKey)
{
using (var rsa = RSA.Create())
using (var shaHash = SHA256.Create())
{
rsa.FromXmlString(privateKey);
var hash = shaHash.ComputeHash(Encoding.UTF8.GetBytes(payload));
return Convert.ToBase64String(rsa.SignData(hash, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1));
}
}
public static void FromXmlString(this 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.");
}
rsa.ImportParameters(parameters);
}
我可以成功验证使用相同环境创建的签名。但是使用CreateSignature_NetFramework
验证使用CheckSignature_NetCore
创建的签名失败。
所以看起来我的.NET Core实现并不完全等同于.NET Framework实现。
如何在.NET Core中验证使用CreateSignature_NetFramework
创建的签名?
答案 0 :(得分:2)
您使用了VerifyData
,但是您已预先计算了哈希值,因此您应该使用VerifyHash
,或让VerifyData为您执行哈希。
也就是说,你想要:
rsa.FromXmlString(privateKey);
var sig = rsa.SignData(
Encoding.UTF8.GetBytes(payload),
HashAlgorithmName.SHA256,
RSASignaturePadding.Pkcs1));
或者
rsa.FromXmlString(privateKey);
var hash = shaHash.ComputeHash(Encoding.UTF8.GetBytes(payload));
var sig = rsa.SignHash(hash, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1));
答案 1 :(得分:0)
我今天遇到了同样的问题。我不得不将一些遗留代码移植到.NET CORE 2。
这就是我所做的。
//<TargetFramework>net46</TargetFramework>
private bool hasValidSignature(string accessToken)
{
JwtParts jwt = new JwtParts(accessToken);
var bytesToSign = Encoding.UTF8.GetBytes(string.Concat(jwt.Header, ".", jwt.Payload));
byte[] signature = (new JwtBase64UrlEncoder()).Decode(jwt.Signature);
X509Certificate2 cert = publicCertService.GetPublicCertificate();
RSACryptoServiceProvider pub = (RSACryptoServiceProvider)cert.PublicKey.Key;
bool res = pub.VerifyData(bytesToSign, "2.16.840.1.101.3.4.2.1", signature);
return res;
}
感谢本文中的bartonjs,为我指出了正确的解决方案。 (https://github.com/dotnet/corefx/issues/26682)
我用JWT(https://github.com/jwt-dotnet/jwt)分割了accessToken并获得Header和Payload。
private bool HasValidSignature(string accessToken)
{
JwtParts jwt = new JwtParts(accessToken);
var bytesToSign = Encoding.UTF8.GetBytes(string.Concat(jwt.Header, ".", jwt.Payload));
byte[] signature = (new JwtBase64UrlEncoder()).Decode(jwt.Signature);
X509Certificate2 cert = new X509Certificate2(publicCertService.GetPublicCertificate());
RSA rsa = cert.GetRSAPublicKey();
bool res = rsa.VerifyData(bytesToSign, signature, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
return res;
}