How to Use Rsa to Encrypt Files (Huge Data) in C#

how to use RSA to encrypt files (huge data) in C#

As mentioned in other answers asymmetric encryption is only designed for encrypting data smaller than its key size.

One option that I have implemented when needing to transfer large amounts of encrypted data between two systems is to have an RSA keypair whose public key is known to both the sender and the receiver then when data needs to be sent the receiver generates a new RSA keypair, encrypts the public key of that keypair with the common public key and sends the encrypted public key to the sender. The sender decrypts the receivers public key using its private key (which the receiver does not need to know, just as the sender does not need to know the receivers generated private key), generates a symmetric encryption key, encrypts the data with the symmetric key and then encrypts the symmetric key using the public key received from the receiver. Both the encrypted symmetric key and the encrypted data are then sent to the receiver which uses its generated private key to decrypt the symmetric key and then decrypts the data.

You can use the RSACryptoServiceProvider.ToXMLString() and RSACryptoServiceProvider.FromXMLString() methods to store the common public key as an XML string literal in the receiver application.

Don't forget, when you generate the symmetric encryption key to use RNGCryptoServiceProvider() to generate the key as it is a much more secure method of generating (pseudo) random numbers.

Also, I strongly recommend against using 3DES as your symmetric encryption algorithm, it is old and starting to show its age. Use AES symmetric encryption with either the AesCryptoServiceProvicer or RijndaelManaged classes.

RSA encryption large data by blocks

A developer who wrote DotnetCore.RSA suggest to break a long text into many small ones and encrypt them separately and concatenate them

That's not the best approach (politely said). Common approach to work with RSA (even for shorter inputs) is using hybrid encryption.

Reasons behind this approach are

  • RSA encryption is SLOW. Very slow comparing to symmetric one.
  • even for small output the RSA encryption output is way longer
  • RSA in certain situations (small message space) may have some weaknesses

Basically you create a random encryption key, encrypt data of any length using any secure symmetric cipher (e.g. AES, 3DES, Salsa, whatever..) and then use RSA to encrypt the symmetric encryption key.

RSA Encryption of large data in C#

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.

RSA of large file using C#

You will need to contrive something in order to accomplish this, perhaps by splitting it into blocks and encrypting each block.

It's not working because the plaintext length in RSA encryption is limited by the length of the key, i.e. it is impossible to encrypt a piece of data larger than the key used to encrypt it. Generally, a one-off exchange of a symmetric key is performed with RSA, and an agreed-upon symmetric algorithm is used from there.

Also, RSA is (compared to symmetric algorithms) very computationally expensive and breaking large data into many dozens (perhaps hundreds?) of small blocks would suffer terrible performance problems, you'd probably only be able to encrypt a few megabytes per second.

Strongly consider an alternate approach.

Edit: Also, consult the documentation of the program that must accept these encrypted strings, because it doesn't help you to use some nonstandard way of encrypting them if the program will be unable to decrypt them.

Encrypting data larger than the RSA key (modulus) size. Encryption of splitted data into chunks, seems not to be working. Any ideas?

I've slightly modified (look for the changed-comments) your code, it works:

public static IEnumerable<IEnumerable<TElement>> ChunkBy<TElement>(this IEnumerable<TElement> source, int chunkSize)
{
return source
.Select((x, i) => new { Index = i, Value = x })
.GroupBy(x => x.Index / chunkSize)
.Select(x => x.Select(v => v.Value).ToArray())
.ToArray();
}

public static byte[] ToUtf8EncodedByteArray(this string source)
{
// Changed: instead of source.ToCharArray() use source directly
return Encoding.UTF8.GetBytes(source);
}

public static string ToUtf8String(this byte[] sourceBytes)
{
return Encoding.UTF8.GetString(sourceBytes);
}

[STAThread]
public static void Main()
{
// Changed: Generate some sample data...
string data = string.Join(string.Empty, Enumerable.Repeat<string>("abcdefghijklmnopqrstuvwxyz0123456789<>!?", 100));
byte[] bytes = data.ToUtf8EncodedByteArray();

// I'm splitting utf8 encoded bytes into chunks of 32 bytes each.
IEnumerable<IEnumerable<byte>> batches = bytes.ChunkBy(32);

LinkedList<byte[]> encryptedChunks = new LinkedList<byte[]>();
LinkedList<byte[]> decryptedChunks = new LinkedList<byte[]>();

using (var rsa = new RSACryptoServiceProvider(2048))
{
rsa.PersistKeyInCsp = false;
var _publicKey = rsa.ExportParameters(false);
var _privateKey = rsa.ExportParameters(true);

rsa.ImportParameters(_publicKey);

byte[] encryptedBatch = null;
foreach (IEnumerable<byte> batch in batches)
{
encryptedBatch = rsa.Encrypt(batch.ToArray(), true);
encryptedChunks.AddLast(encryptedBatch);
}

rsa.ImportParameters(_privateKey);
byte[] decryptedBatch = null;
foreach (byte[] chunk in encryptedChunks)
{
decryptedBatch = rsa.Decrypt(chunk, true);
// Changed (critical): instead of chunk (the encrypted data) use the decrypted data
decryptedChunks.AddLast(decryptedBatch);
}
}

// After decryption of each encrypted chunk of data, I project it back into an array of bytes.
byte[] decrypted = decryptedChunks.SelectMany(chunk => chunk).ToArray();

var data2 = decrypted.ToUtf8String();
// Changed: Verify that input and output are the same
var equals = data.Equals(data2);
}

How / Should I Encrypt Variable Size Data with RSA (RSACryptoServiceProvider)

in most cases RSA is used to encrypt a symetric key (you don't really need to encrypt the IV, but hey...)

if you use RSA for encryption of data (instead of a key) you might run into the ECB (Electronic Code Book mode) problem that is known in the context of symetric block cyphers: for a given key, a clear-text is always mapped to the same cypher-text ... that alone doesn't help in breaking the encryption, but it can leak information since an attacker can identify which data packages contain the same clear-texts

i'd choose the hybrid approach, because it's suitable for arbitrary sized data, and won't be prone to this information leak unless you choose ECB for the mode of operation (CBC - Cypher Block Chaining mode - should do)



Related Topics



Leave a reply



Submit