【问题标题】:RSA Encryption of large data in C#C#中大数据的RSA加密
【发布时间】:2012-01-15 02:56:12
【问题描述】:

这是我的第一篇文章,所以希望我没有错过任何重要的内容。我正在用 C# 做一个项目,我需要使用公钥/私钥加密来加密消息,然后通过 SSL 连接发送它。

我选择使用RSACryptoService,根据文档,这是唯一用于加密数据的非对称加密方案。问题是我在这方面遇到了很多问题。 (我想做对称加密,但这不是我的老师要我做的,据他说,确定块大小应该很容易,然后它应该为你做所有的工作。)好吧,到目前为止没有运气,我尝试了一些不同的方法,但现在我回到了基础并再次尝试,这是我当前的代码:

    public string[] GenerateKeysToStrings(string uniqueIdentifier)
    {
        string[] keys;
        using (var rsa = new RSACryptoServiceProvider(4096))
        {
            try
            {
                string privateKey = rsa.ToXmlString(true);
                string publicKey = rsa.ToXmlString(false);

                this.pki.StoreKey(publicKey, uniqueIdentifier);

                keys = new string[2];
                keys[0] = privateKey;
                keys[1] = publicKey;
            }
            finally
            {
                //// Clear the RSA key container, deleting generated keys.
                rsa.PersistKeyInCsp = false;
            }
        }
        return keys;
    }

如您所见,我生成密钥并通过将公钥发送到存储它的简单类来模拟 PKI,然后将私钥写入文件 (请注意,我还有另一种方法可以执行相同的操作,但将其存储到数组中,只是因为我想测试和简化事情,因为我得到No such key exceptions,有时当我按照示例中所示的方式进行操作时会出现加密异常,所以我想通过简单地将rsa.ToXmlString 字符串存储为数组中的字符串来简化它,但没有运气。)

现在我有一个加解密方法如下:

    public string Encrypt(string keyString, string message)
    {
        string encryptedMessage;
        using (var rsa = new RSACryptoServiceProvider())
        {
            try
            {
                //// Load the key from the specified path
                var encryptKey = new XmlDocument();
                encryptKey.Load(@"C:\Test\PrivateKeyInfo.xml");
                rsa.FromXmlString(encryptKey.OuterXml);


                //// Conver the string message to a byte array for encryption
                //// var encoder = new UTF8Encoding();
                ASCIIEncoding byteConverter = new ASCIIEncoding();
                byte[] dataToEncrypt = byteConverter.GetBytes(message);

                byte[] encryptedData = rsa.Encrypt(dataToEncrypt, false);

                //// Convert the byte array back to a string message
                encryptedMessage = byteConverter.GetString(encryptedData);
            }
            finally
            {
                //// Clear the RSA key container, deleting generated keys.
                rsa.PersistKeyInCsp = false;
            }
        }
        return encryptedMessage;
    }

解密:

    public string Decrypt(string keyString, string message)
    {
        string decryptedText;
        using (var rsa = new RSACryptoServiceProvider())
        {
            try
            {
                //// Loads the keyinfo into the rsa parameters from the keyfile
                /*
                var privateKey = new XmlDocument();
                privateKey.Load(keyString);
                 */
                rsa.FromXmlString(keyString);

                //// Convert the text from string to byte array for decryption
                ASCIIEncoding byteConverter = new ASCIIEncoding();
                var encryptedBytes = byteConverter.GetBytes(message);

                //// Create an aux array to store all the encrypted bytes
                byte[] decryptedBytes = rsa.Decrypt(encryptedBytes, false);

                decryptedText = byteConverter.GetString(decryptedBytes);
            }
            finally
            {
                //// Clear the RSA key container, deleting generated keys.
                rsa.PersistKeyInCsp = false;
            }
        }
        return decryptedText;
    }

我知道这是一堵文字墙,但我希望你能帮帮我,因为我已经把头撞在墙上这么久了,这并不好笑:)

问题是,我该如何使用RSA(或任何其他公钥/私钥加密)加密消息

这里是测试客户端:

    public static void Main(string[] args)
    {
        PublicKeyInfrastructure pki = new PublicKeyInfrastructure();
        Cryptograph crypto = new Cryptograph();
        string[] keys = crypto.GenerateKeysToStrings("simonlanghoff@gmail.com");


        string plainText = "Hello play with me, please";
        string publicKey = crypto.GetPublicKey("simonlanghoff@gmail.com");

        string encryptedText = crypto.Encrypt(keys[0], plainText);


        string decryptedText = crypto.Decrypt(keys[1], encryptedText);

    }

正如我所提到的,字符串数组存在是因为我想消除 XML 文档中的错误解析...

当我运行测试客户端时,如果我使用私钥加密和公钥解密,我会得到一个“密钥不存在异常”,如果我反过来做,我会得到一个坏数据异常.

请帮帮我,如果您知道任何好的指南,或者可以告诉我如何在字符串消息上稍微简单地实施公钥/私钥加密,请帮帮我。

感谢您的帮助。

【问题讨论】:

    标签: c# encryption rsa encryption-asymmetric


    【解决方案1】:

    这不是 RSA 加密的方式。

    RSA 是关于数学的。您加密的是一个数字,因此它必须是有限长度并与您使用的 RSA 密钥对长度相匹配。使用的填充(PKCS#1 或 OAEP)会进一步限制长度。

    如果您想使用 RSA 加密大数据,您需要间接进行 - 即使用对称密钥加密大数据并使用 RSA 公钥加密此密钥。

    您可以在我的blog 上阅读有关实现此功能的信息。

    【讨论】:

    • 我和我的教授谈过,当时我的观点是最好加密一个密钥,交换密钥然后使用它,作为对称算法的基础,比如 rijndael加密/解密消息,但是,他不希望我们使用对称加密,所以现在我处于一个暂时禁止使用的位置。性能方面,当我们讨论包含用户名和密码的 HTTP 消息时,加密消息需要多长时间,一次 501 个字节(使用 4096 位 RSA 密钥)?无论是否逐块编码,我在实际使用 RSA 时仍然存在问题 :(
    • 您可以循环 RSA 加密/解密以保持在大小限制(包括填充)内并连接/拆分结果。多少时间 ?这将取决于您的 CPU,它是旧智能手机和新的 8 核服务器吗? ;-) 但比正确的选择要长得多。
    • 我希望我能计时,但现在我什至无法让它适用于任何数据。我采用了简化版本并使用了它,但是,我仍然得到“密钥不存在”错误,我真正所做的只是重构设计,从单个主要方法到单独的方法。也许我正在使用构造函数?我导入密钥设置的部分?我知道分组密码比对称算法慢 1000 倍,但现在我只想让它工作,无论我是否必须对其进行硬编码,以便将每个数据块切成 (keysize/8 - 11(用于填充))字节。
    • 查看您的代码。您的 Encrypt 方法不使用 keyString 参数并加载 XML 文件(使用密钥?),但对其内容不执行任何操作。看起来您正在使用 自动 生成的密钥(默认为 1024 位)进行加密,因此这与解密不匹配。
    • 我想我已经找到了我得到密钥不存在错误的原因,而且每次我使用私钥加密数据时都会发生这种情况。我认为您使用什么密钥无关紧要,如果我在这方面错了,请纠正我,但我猜由于私钥加密用于验证而不是保密,那么加密和RSA CryptoService 提供程序的解密方法不允许您使用私钥,而应仅用于签名/验证。但这并不能解释为什么我会使用公钥得到错误数据异常?
    【解决方案2】:

    好的,我终于想出了解决我在原帖中提到的问题的方法。这是我没有彻底测试过的东西或任何东西,但我从一个小小的试错过程中发现了一些东西。

    这是我当前的代码:

        public static string Encrypt(string dataToEncrypt, RSAParameters publicKeyInfo)
        {
            //// Our bytearray to hold all of our data after the encryption
            byte[] encryptedBytes = new byte[0];
            using (var RSA = new RSACryptoServiceProvider())
            {
                try
                {
                    //Create a new instance of RSACryptoServiceProvider.
                    UTF8Encoding encoder = new UTF8Encoding();
    
                    byte[] encryptThis = encoder.GetBytes(dataToEncrypt);
    
                    //// Importing the public key
                    RSA.ImportParameters(publicKeyInfo);
    
                    int blockSize = (RSA.KeySize / 8) - 32;
    
                    //// buffer to write byte sequence of the given block_size
                    byte[] buffer = new byte[blockSize];
    
                    byte[] encryptedBuffer = new byte[blockSize];
    
                    //// Initializing our encryptedBytes array to a suitable size, depending on the size of data to be encrypted
                    encryptedBytes = new byte[encryptThis.Length + blockSize - (encryptThis.Length % blockSize) + 32];
    
                    for (int i = 0; i < encryptThis.Length; i += blockSize)
                    {
                        //// If there is extra info to be parsed, but not enough to fill out a complete bytearray, fit array for last bit of data
                        if (2 * i > encryptThis.Length && ((encryptThis.Length - i) % blockSize != 0))
                        {
                            buffer = new byte[encryptThis.Length - i];
                            blockSize = encryptThis.Length - i;
                        }
    
                        //// If the amount of bytes we need to decrypt isn't enough to fill out a block, only decrypt part of it
                        if (encryptThis.Length < blockSize)
                        {
                            buffer = new byte[encryptThis.Length];
                            blockSize = encryptThis.Length;
                        }
    
                        //// encrypt the specified size of data, then add to final array.
                        Buffer.BlockCopy(encryptThis, i, buffer, 0, blockSize);
                        encryptedBuffer = RSA.Encrypt(buffer, false);
                        encryptedBuffer.CopyTo(encryptedBytes, i);
                    }
                }
                catch (CryptographicException e)
                {
                    Console.Write(e);
                }
                finally
                {
                    //// Clear the RSA key container, deleting generated keys.
                    RSA.PersistKeyInCsp = false;
                }
            }
            //// Convert the byteArray using Base64 and returns as an encrypted string
            return Convert.ToBase64String(encryptedBytes);
        }
    
        /// <summary>
        /// Decrypt this message using this key
        /// </summary>
        /// <param name="dataToDecrypt">
        /// The data To decrypt.
        /// </param>
        /// <param name="privateKeyInfo">
        /// The private Key Info.
        /// </param>
        /// <returns>
        /// The decrypted data.
        /// </returns>
        public static string Decrypt(string dataToDecrypt, RSAParameters privateKeyInfo)
        {
            //// The bytearray to hold all of our data after decryption
            byte[] decryptedBytes;
    
            //Create a new instance of RSACryptoServiceProvider.
            using (RSACryptoServiceProvider RSA = new RSACryptoServiceProvider())
            {
                try
                {
                    byte[] bytesToDecrypt = Convert.FromBase64String(dataToDecrypt);
    
                    //// Import the private key info
                    RSA.ImportParameters(privateKeyInfo);
    
                    //// No need to subtract padding size when decrypting (OR do I?)
                    int blockSize = RSA.KeySize / 8;
    
                    //// buffer to write byte sequence of the given block_size
                    byte[] buffer = new byte[blockSize];
    
                    //// buffer containing decrypted information
                    byte[] decryptedBuffer = new byte[blockSize];
    
                    //// Initializes our array to make sure it can hold at least the amount needed to decrypt.
                    decryptedBytes = new byte[dataToDecrypt.Length];
    
                    for (int i = 0; i < bytesToDecrypt.Length; i += blockSize)
                    {
                        if (2 * i > bytesToDecrypt.Length && ((bytesToDecrypt.Length - i) % blockSize != 0))
                        {
                            buffer = new byte[bytesToDecrypt.Length - i];
                            blockSize = bytesToDecrypt.Length - i;
                        }
    
                        //// If the amount of bytes we need to decrypt isn't enough to fill out a block, only decrypt part of it
                        if (bytesToDecrypt.Length < blockSize)
                        {
                            buffer = new byte[bytesToDecrypt.Length];
                            blockSize = bytesToDecrypt.Length;
                        }
    
                        Buffer.BlockCopy(bytesToDecrypt, i, buffer, 0, blockSize);
                        decryptedBuffer = RSA.Decrypt(buffer, false);
                        decryptedBuffer.CopyTo(decryptedBytes, i);
                    }
                }
                finally
                {
                    //// Clear the RSA key container, deleting generated keys.
                    RSA.PersistKeyInCsp = false;
                }
            }
    
            //// We encode each byte with UTF8 and then write to a string while trimming off the extra empty data created by the overhead.
            var encoder = new UTF8Encoding();
            return encoder.GetString(decryptedBytes).TrimEnd(new[] { '\0' });
    
        }
    

    正如我所说,我没有对它进行太多测试,除了低于、等于和高于块大小的大小,但它似乎正在做它应该做的事情。我还是个新手,所以我真的希望你仔细检查我的代码:)

    【讨论】:

      【解决方案3】:

      也许我遗漏了一些东西,但您的 Encrypt() 函数似乎没有使用 keyString 参数或 encryptKey 的内容。

      【讨论】:

      • 是的,对此我感到非常抱歉,但我一定是忘了把它放在那里,但我向你保证,我已确保加载密钥。我现在已经更新了 OP。我刚刚尝试加载数据很多次,我一定是混淆了一些版本并且没有看到它:) 我的问题仍然存在。我现在遇到的问题是,当我使用我的私钥加密并使用公众解密时,我得到一个 keyDoesn'tExist 异常。但是,如果我反其道而行之,则会收到“错误数据”异常。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-04-07
      • 2019-01-16
      • 2010-11-15
      • 2015-03-16
      • 2013-04-22
      相关资源
      最近更新 更多