使用 .NET Core 检查 .NET Framework 生成的签名

Check .NET Framework generated signature using .NET Core

我有一些旧的 .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);
}

我可以成功验证使用相同环境创建的签名。但是使用 CheckSignature_NetCore 验证由 CreateSignature_NetFramework 创建的签名失败。

看来我的 .NET Core 实现并不完全等同于 .NET Framework 实现。

如何验证在 .NET Core 中使用 CreateSignature_NetFramework 创建的签名?

您使用了 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));

我 运行 今天遇到了同样的问题。我不得不将一些遗留代码移植到 .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;
}

感谢 post 中的 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;
}