RSA Encryption of large data in C#

浪子不回头ぞ 提交于 2019-11-30 09:17:05
poupou

This is not how RSA encryption should be done.

RSA is all about math. What you encrypt is a number so it has to be of finite length and matching the RSA keypair length you're using. Further length limitations are imposed by the padding used (either PKCS#1 or OAEP).

If you want to encrypt large data with RSA you need to do it indirectly - i.e. use a symmetric key to encrypt the large data and encrypt this key using the RSA public key.

You can read about implementing this on my blog.

Okay, I've finally come up with a solution to the problem I stated in my original post. This is something I haven't thoroughly tested or anything, but something I figured out from a little trial and error process.

Here is the current code I have:

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

    }

As I said, I have not tested it much, other than sizes below, at and above the block-size, but it seems to be doing what it should. I am still a novice, so I would really like for you to scrutinize my code :)

Maybe I'm missing something, but it looks like your Encrypt() function doesn't make use of either the keyString parameter or the contents of encryptKey.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!