使用C sharp中的RSA对128字节的字节数组进行签名

时间:2011-06-09 21:21:35

标签: c# cryptography rsa

我对密码学完全不熟悉,我需要用一个用C sharp生成的RSA密钥签署一个128字节的字节数组。密钥必须是1024位。

我找到了一些如何将RSA与C sharp一起使用的示例,我目前正在尝试使用的代码是:

public static void AssignParameter()
{
    const int PROVIDER_RSA_FULL = 1;
    const string CONTAINER_NAME = "SpiderContainer";
    CspParameters cspParams;
    cspParams = new CspParameters(PROVIDER_RSA_FULL);
    cspParams.KeyContainerName = CONTAINER_NAME;
    cspParams.Flags = CspProviderFlags.UseMachineKeyStore;
    cspParams.ProviderName = "Microsoft Strong Cryptographic Provider";
    rsa = new RSACryptoServiceProvider(cspParams);
    rsa.KeySize = 1024;
}

public static string EncryptData(string data2Encrypt)
{
    AssignParameter();
    StreamReader reader = new StreamReader(path + "publickey.xml");
    string publicOnlyKeyXML = reader.ReadToEnd();
    rsa.FromXmlString(publicOnlyKeyXML);
    reader.Close();

    //read plaintext, encrypt it to ciphertext

    byte[] plainbytes = System.Text.Encoding.UTF8.GetBytes(data2Encrypt);
    byte[] cipherbytes = rsa.Encrypt(plainbytes, false);
    return Convert.ToBase64String(cipherbytes);
}

此代码适用于小字符串(因此短字节数组),但当我尝试使用128个字符的字符串时,我得到一个错误说: CryptographicException未处理:错误的长度 (好吧,它可能不会精确地说'错误的长度',我得到丹麦语的错误,那就是'Forkertlængde'直接转换为'错误的长度')。

有人能告诉我如何使用C sharp中的1024位RSA密钥加密128字节的字节数组吗?

提前致谢, LordJesus

修改

好的,只是为了澄清一点:我有一条消息,我使用SHA-256从中做出一个哈希。这给出了一个32字节的数组。使用自定义填充填充此数组,因此最终为128字节数组。然后,使用我的私钥将此填充哈希签名,以便接收方可以使用我的公钥来验证收到的消息是否与发送的消息相同。这可以用1024位的密钥完成吗?

6 个答案:

答案 0 :(得分:5)

如果您想要签名,则不希望加密。签名和加密是不同的算法。有一种众所周知的称为RSA的签名算法,以及一种众所周知的非对称加密算法(称为RSA),并且首先呈现(并且仍然在很多地方)签名算法为“你用私钥“。这简直令人困惑。

在RSA加密中,要加密的数据(使用公钥)必须用PKCS#1(RSA标准)描述为“类型2填充”和结果(具有相同的长度)填充然后通过模幂运算处理模数,该模幂运算是RSA的核心(在核心,但RSA不仅仅模幂运算;填充对于安全性非常重要。)

签名时,必须对要签名的数据进行哈希处理,然后将哈希值嵌入到描述刚刚使用的哈希函数的结构中,并且编码结构本身用“类型1填充”填充 - 而不是与填充填充相同的填充,这也很重要。

无论哪种方式,普通的RSA引擎都会执行类型1或类型2填充本身,并且大多数RSA 签名引擎也将自己处理标识所使用的哈希函数的结构。诸如RSACryptoServiceProvider之类的RSA签名引擎可以与SignHash()一起工作,它需要哈希值(从SHA-256获得32个字节,没有任何类型的封装结构或类型1填充 - RSACryptoServiceProvider处理它自己)或SignData(),它期望数据被签名(引擎也会进行哈希计算)。

总而言之,如果你自己做任何填充,那么你做错了。如果您使用Encrypt()来计算签名,那么您也做错了。

答案 1 :(得分:2)

当您在关闭OAEP时调用Encrypt时,加密128个字节的最小密钥大小为1112位。请注意,像rsa.KeySize = 1024这样设置密钥大小无济于事,您需要实际生成正确大小的密钥并使用它们。

这对我有用:

using System;
using System.IO;
using System.Security.Cryptography;

namespace SO6299460
{
    class Program
    {
        static void Main()
        {
            GenerateKey();
            string data2Encrypt = string.Empty.PadLeft(128,'$');
            string encrypted = EncryptData(data2Encrypt);
            string decrypted = DecryptData(encrypted);

            Console.WriteLine(data2Encrypt);
            Console.WriteLine(encrypted);
            Console.WriteLine(decrypted);
        }

        private const string path = @"c:\";

        public static void GenerateKey()
        {
            RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(1112);
            string publickKey = rsa.ToXmlString(false);
            string privateKey = rsa.ToXmlString(true);

            WriteStringToFile(publickKey, path + "publickey.xml");
            WriteStringToFile(privateKey, path + "privatekey.xml");
        }

        public static void WriteStringToFile(string value, string filename)
        {
            using (FileStream stream = File.Open(filename, FileMode.Create, FileAccess.Write, FileShare.Read))
            using (StreamWriter writer = new StreamWriter(stream))
            {
                writer.Write(value);
                writer.Flush();
                stream.Flush();
            }
        }

        public static string EncryptData(string data2Encrypt)
        {
            RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
            StreamReader reader = new StreamReader(path + "publickey.xml");
            string publicOnlyKeyXML = reader.ReadToEnd();
            rsa.FromXmlString(publicOnlyKeyXML);
            reader.Close();

            //read plaintext, encrypt it to ciphertext

            byte[] plainbytes = System.Text.Encoding.UTF8.GetBytes(data2Encrypt);
            byte[] cipherbytes = rsa.Encrypt(plainbytes,false);

            return Convert.ToBase64String(cipherbytes);
        }

        public static string DecryptData(string data2Decrypt)
        {
            RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
            StreamReader reader = new StreamReader(path + "privatekey.xml");
            string key = reader.ReadToEnd();
            rsa.FromXmlString(key);
            reader.Close();

            byte[] plainbytes = rsa.Decrypt(Convert.FromBase64String(data2Decrypt), false);
            return System.Text.Encoding.UTF8.GetString(plainbytes);

        }


    }
}

但请注意,我没有使用加密容器,因此,我不需要您的AssignParameter,但如果您需要使用它,修改代码应该很容易。

如果您需要加密大量数据(远大于128字节)this article有关于如何执行此操作的示例代码。

答案 2 :(得分:1)

显然,根据这个问题 - how to use RSA to encrypt files (huge data) in C# - RSA只能加密比密钥长度短的数据。

怪异。 `RSACryptoServiceProvider.Encrypt()的MSDN文档说如果 rgb参数的长度大于允许的最大长度,则可能抛出CryptographicException。

好。这看起来很奇怪,特别是因为关于所述最大值的文档似乎没有多少。

进一步挖掘,在备注下有这个:

  

下表介绍了不同版本支持的填充   Microsoft Windows和最大允许的rgb长度不同   操作系统和填充的组合。

  • 如果您正在运行XP或更高版本并且您正在使用OAEP填充,那么该限制将被声明为

      

    模数大小-2 -2 * hLen,其中hLen是散列的大小

    不知道“散列的大小”是什么,因为除了数字签名之外,文档AFAICS并没有提到“散列”。

  • 如果您运行的是安装了“高加密包”的Windows 2000或更高版本(再次,不知道如何找到它),那么该限制将被声明为

      

    模数大小 - 11.(11个字节是可能的最小填充。)

  • 否则(没有上述“高加密包”的Windows 98,Millenium或Windows 2000或更高版本),您将获得“不支持直接加密和OAEP填充”,其中限制为

      

    对称密钥允许的最大大小。

    说......等一下...... RSA是一种非对称算法,对吗?

无价值的文件。啧。

答案 3 :(得分:0)

http://msdn.microsoft.com/en-us/library/system.security.cryptography.rsacryptoserviceprovider.encrypt.aspx。抛出的异常可能是“rgb参数的长度大于允许的最大长度。”

答案 4 :(得分:0)

通常RSA加密具有填充,并且由于加密数据大小达到密钥大小,因此没有填充空间。尝试使用更长的密钥或更少的数据大小来加密。

答案 5 :(得分:0)

你真的需要自定义填充吗?如果不是,您可以使用RSACryptoServiceProvider.SignData Method