使用 RSA C# 签名和验证签名

Posted

技术标签:

【中文标题】使用 RSA C# 签名和验证签名【英文标题】:Signing and verifying signatures with RSA C# 【发布时间】:2012-01-16 06:16:17 【问题描述】:

我最近发布了有关使用 RSA 加密大数据的问题,我终于完成了,现在我将继续使用用户的私钥实现签名并使用相应的公钥进行验证。但是,每当我比较签名数据和原始消息时,我基本上只会得到错误的返回。我希望你们中的一些人能看到我做错了什么。

代码如下:

public static string SignData(string message, RSAParameters privateKey)
    
        //// The array to store the signed message in bytes
        byte[] signedBytes;
        using (var rsa = new RSACryptoServiceProvider())
        
            //// Write the message to a byte array using UTF8 as the encoding.
            var encoder = new UTF8Encoding();
            byte[] originalData = encoder.GetBytes(message);

            try
            
                //// Import the private key used for signing the message
                rsa.ImportParameters(privateKey);

                //// Sign the data, using SHA512 as the hashing algorithm 
                signedBytes = rsa.SignData(originalData, CryptoConfig.MapNameToOID("SHA512"));
            
            catch (CryptographicException e)
            
                Console.WriteLine(e.Message);
                return null;
            
            finally
            
                //// Set the keycontainer to be cleared when rsa is garbage collected.
                rsa.PersistKeyInCsp = false;
            
        
        //// Convert the a base64 string before returning
        return Convert.ToBase64String(signedBytes);
    

这是第一步,对数据进行签名,接下来我将继续验证数据:

public static bool VerifyData(string originalMessage, string signedMessage, RSAParameters publicKey)
    
        bool success = false;
        using (var rsa = new RSACryptoServiceProvider())
        
            byte[] bytesToVerify = Convert.FromBase64String(originalMessage);
            byte[] signedBytes = Convert.FromBase64String(signedMessage);
            try
            
                rsa.ImportParameters(publicKey);

                SHA512Managed Hash = new SHA512Managed();

                byte[] hashedData = Hash.ComputeHash(signedBytes);

                success = rsa.VerifyData(bytesToVerify, CryptoConfig.MapNameToOID("SHA512"), signedBytes);
            
            catch (CryptographicException e)
            
                Console.WriteLine(e.Message);
            
            finally
            
                rsa.PersistKeyInCsp = false;
            
        
        return success;
    

这里是测试客户端:

public static void Main(string[] args)
    
        PublicKeyInfrastructure pki = new PublicKeyInfrastructure();
        Cryptograph crypto = new Cryptograph();
        RSAParameters privateKey = crypto.GenerateKeys("email@email.com");

        const string PlainText = "This is really sent by me, really!";

        RSAParameters publicKey = crypto.GetPublicKey("email@email.com");

        string encryptedText = Cryptograph.Encrypt(PlainText, publicKey);

        Console.WriteLine("This is the encrypted Text:" + "\n " + encryptedText);

        string decryptedText = Cryptograph.Decrypt(encryptedText, privateKey);

        Console.WriteLine("This is the decrypted text: " + decryptedText);

        string messageToSign = encryptedText;

        string signedMessage = Cryptograph.SignData(messageToSign, privateKey);

        //// Is this message really, really, REALLY sent by me?
        bool success = Cryptograph.VerifyData(messageToSign, signedMessage, publicKey);

        Console.WriteLine("Is this message really, really, REALLY sent by me? " + success);

    

我在这里错过了一步吗?根据 Cryptography API 和那里的示例,我不应该手动计算任何哈希值,因为我在方法调用本身中提供了算法。

任何帮助将不胜感激。

【问题讨论】:

PublicKeyInfrastructurecrypto.GenerateKeys 来自哪里? PublicKeyInfrastructure 只是一个集合类,在存储一些公钥信息时处理一些序列化。 GenerateKeys 方法来自生成公钥/私钥对的帮助程序类。很抱歉,这还不够清楚。 @SimonLanghoff 为什么在 VerifyData() 中计算 hashedData 然后不使用它?这是笔误吗? 在 VerifyData 中你应该调用 rsa.VerifyHash(hashedData) 吗?还是这与调用 rsa.VerifyData(data) 相同? 另见Signing and verifying signatures with RSA C#、how to sign bytes using my own rsa private key using rs256 algorithm?、Signing data with private key in c#、How can I sign a file using RSA and SHA256 with .NET?、Signing a string with RSA private key on .NET?等 【参考方案1】:

你的问题出在VerifyData方法的开头:

public static bool VerifyData(string originalMessage, string signedMessage, RSAParameters publicKey)

    bool success = false;
    using (var rsa = new RSACryptoServiceProvider())
    
        //Don't do this, do the same as you did in SignData:
        //byte[] bytesToVerify = Convert.FromBase64String(originalMessage);
        var encoder = new UTF8Encoding();
        byte[] bytesToVerify = encoder.GetBytes(originalMessage);

        byte[] signedBytes = Convert.FromBase64String(signedMessage);
        try
        ...

由于某种原因,您切换到 FromBase64String 而不是 UTF8Encoding.GetBytes

【讨论】:

另外,对于数据签名,您应该使用私钥,然后使用公钥解密使用私钥签名的数据。这是因为您分发的是公钥,而不是私钥。 我知道 RSA 可以加密的最大字符串大小。是否有最大字符串大小 RSA 也可以签名?还是可以签署任意长度的字符串? @KraangPrime 错误。当您签名时,您使用您的私钥签名,然后公共方使用您相应的公钥进行验证,而不是解密。在加密方面,公共方将使用您的公钥加密数据,然后您使用您的私钥对其进行解密

以上是关于使用 RSA C# 签名和验证签名的主要内容,如果未能解决你的问题,请参考以下文章

使用 mbedtls 生成的 RSA 签名,无法使用 C# (bouncycastle) 应用程序进行验证

C#中RSA加密解密和签名与验证的实现

使用 openssl_public_encrypt 验证 RSA 签名对 .NET 签名字符串失败

C#上位机开发(十八)—— 基于RSA算法实现数字签名与认证(私钥签名,公钥认证)

C#上位机开发(十八)—— 基于RSA算法实现数字签名与认证(私钥签名,公钥认证)

RSA加密解密及RSA签名和验证