简体   繁体   English

使用X509私钥对dotnet core v2(SHA256)中的数据进行签名

[英]Using an X509 private key to sign data in dotnet core v2 (SHA256)

I'm having trouble reproducing some cryptographic functionality in dotnet core v2.0. 我在dotnet core v2.0中复制一些加密功能时遇到了麻烦。 This is code ported from a .NET 4.5 project 这是从.NET 4.5项目移植的代码

.NET 4.5 code .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;
}

In dotnet core the ToXmlString() and FromXmlString() methods are not implemented, so I used a helper class workaround. 在dotnet核心中,没有实现ToXmlString()FromXmlString()方法,所以我使用了一个帮助器类解决方法。 Aside from that the dotnet core implementation works but, given the same input data and certificate it produces a different outcome. 除此之外,dotnet核心实现可以工作,但是,在相同的输入数据和证书的情况下,它会产生不同的结果。

dotnet core v2.0 code dotnet核心v2.0代码

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. dotnet核心签名数据未通过.NET 4.5代码库中的签名验证检查。 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 编辑2

For clarification this is what both code snippets are attempting: 为了澄清这两个代码片段正在尝试:

  1. Taking a X509 certificate's private key which is RSA-FULL and cannot sign using SHA256 encoding 使用X509证书的私钥是RSA-FULL ,无法使用SHA256编码进行签名
  2. Creating a new private key which is RSA-AES which can sign using SHA256 encoding 创建一个新的私钥 ,它是RSA-AES可以使用SHA256编码进行签名
  3. Import your X509 private key into this new private key 将X509私钥导入此新私钥
  4. Signing the required data using this private key. 使用此私钥对所需数据进行签名。

The complication comes when attempting the same thing in .NEt4.5 and dotnet core v2.0. 在.NEt4.5和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. 这个答案表明RSACryptoServiceProvider对象依赖于.NET 4.5中软件所在机器的CryptoAPI这篇信息性的帖子向您展示了在不同环境/框架中如何实现它的区别。

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. 我仍然在研究基于这些信息的解决方案,但是仍然存在核心问题,即使用此dotnet核心的签名数据无法通过.NET 4.5实现进行验证。

If you MUST stick with 4.5, your .NET Framework code is as good as it gets. 如果你必须坚持使用4.5,那么你的.NET Framework代码就像它一样好。 (Well, you could eliminate the usage of the XML format and just use ExportParameters directly) (好吧,你可以消除XML格式的使用,直接使用ExportParameters

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: 在.NET 4.6中,问题通过使用PrivateKey属性的软弃用(这意味着我告诉StackOverflow上的每个人都不使用它)来解决:

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). 这与您应该为.NET Core(所有版本)编写的代码相同。 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. 重构的部分原因是让人们远离RSACryptoServiceProvider类型,这在非Windows系统上不能很好地工作。

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... 更少的代码,更强的类型安全性,没有PROV_RSA_FULL问题,没有密钥导出/导入......

Solution

To be able to verify in .NET 4.5 the data signed using a X509 RSA private key in dotnet core v2.0 为了能够在.NET 4.5中验证使用dotnet core v2.0中的X509 RSA私钥签名的数据

Verification code (.NET 4.5) 验证码(.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");
        }
    }
}

Signing Code (dotnet core v2.0) 签名代码(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;
}

Helper Class 助手班

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));
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM