Reputation: 2624
Please, read this first:
- Generate random key for symmetric algorithm
- Encrypt the data using symmetric algorithm and the random key
- Encrypt the random key using your public key and store it in the encrypted form next (or before) the data.
But I must accomplish the given task by all means. Thanks for understanding.
I greatly abstracted and simplified the example to make it clear about the problem I'm struggling with.
string data = "message 700-1300 letters long";
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;
_publicKey = rsa.ExportParameters(false);
_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);
}
// Then encryptedChunks.ToArray() will be send over the network
// When encrypted bytes are received at another endpoint, they have to be decrypted.
rsa.ImportParameters(_privateKey);
byte[] decryptedBatch = null;
foreach (byte[] chunk in encryptedChunks)
{
decryptedBatch = rsa.Decrypt(chunk, true);
decryptedChunks.AddLast(chunk);
}
}
// After decryption of each encrypted chunk of data, I project it back into an array of bytes.
byte[] decrypted = decryptedChunks.SelectMany(chunk => chunk).ToArray();
// When trying to display decoded bytes as a string (as it was initially), instead of original message I get hieroglyphs.
Console.Out.WriteLine($"Decrypted message: {decrypted.ToUtf8String()}");
Here are extension methods (that are used within my code), if needed:
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)
{
return Encoding.UTF8.GetBytes(source.ToCharArray());
}
public static string ToUtf8String(this byte[] sourceBytes)
{
return Encoding.UTF8.GetString(sourceBytes);
}
P.S. I also tried before encrypting the data (text), encoding it into ASCII, instead of UTF8. This didn't help as well.
Any ideas? (except switching to encryption with symmetric key. As I mentioned above, I'm not allowed to do it.)
Upvotes: 0
Views: 916
Reputation: 2123
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);
}
Upvotes: 2