patterncsharpMinor
Public key chunked encryption with C# method to decrypt
Viewed 0 times
encryptionmethodpublicwithchunkeddecryptkey
Problem
I have written a method in PHP which breaks up larger than the byte size of the key of the certificate used to encrypt, and a method in c# which will decrypt said chunks using the corresponding private key.
What I would like to be reviewed is the C#
In my tests using
to
... the performance increases a bit to 2.8 seconds.
Generate private and public key pair (run this code)
Save the results to
PHP: Encrypt using Public key (
```
// replace this line with the one from http://pastebin.com/6Q2Zb3j6
$output = '';
echo encrypt($data, 'public.txt');
// encrypt string using public key
function encrypt($string, $publickey, $chunkPadding = 16) {
$encrypted = '';
// load public key
$key = file_get_contents($publickey);
$pub_key = openssl_pkey_get_public($key);
$keyData = openssl_pkey_get_details($pub_key);
$chunksize = ($keyData['bits'] / 8) - $chunkPadding;
openssl_free_key( $pub
What I would like to be reviewed is the C#
decrypt() function to improve the performance of decrypting each chunk.In my tests using
StopWatch this part takes about 3 seconds to decrypt all chunks in the test string, and store them back as a string. If I change the linedecrypted = decrypted + Encoding.UTF8.GetString( rsa.Decrypt( buffer, false ) );to
byte[] tmp = rsa.Decrypt( buffer, false );... the performance increases a bit to 2.8 seconds.
Generate private and public key pair (run this code)
GenerateKeyPair(true);
// generate public and private key
function GenerateKeyPair($display = false) {
$config = array(
"digest_alg" => "sha512",
"private_key_bits" => 4096,
"private_key_type" => OPENSSL_KEYTYPE_RSA
);
$ssl = openssl_pkey_new( $config );
openssl_pkey_export($ssl, $privKey);
$pubKey = openssl_pkey_get_details($ssl)['key'];
if($display == true) {
// display keys in textareas
echo '
' . $privKey . '
' . $pubKey . '
';
}
// return keys as an array
return array(
'private' => $privKey,
'public' => $pubKey
);
}Save the results to
private.txt and public.txtPHP: Encrypt using Public key (
public.txt)```
// replace this line with the one from http://pastebin.com/6Q2Zb3j6
$output = '';
echo encrypt($data, 'public.txt');
// encrypt string using public key
function encrypt($string, $publickey, $chunkPadding = 16) {
$encrypted = '';
// load public key
$key = file_get_contents($publickey);
$pub_key = openssl_pkey_get_public($key);
$keyData = openssl_pkey_get_details($pub_key);
$chunksize = ($keyData['bits'] / 8) - $chunkPadding;
openssl_free_key( $pub
Solution
Replace all this junk:
with
We use a highly-optimized builtin class for converting hexadecimal description of a byte array into that byte array. Much simpler, much faster.
Then, we skip the MemoryStream entirely, since extra wrappers can only slow things down, and streams force us into sequential operation.
Finally, we use Parallel.For to process each block, passing it through
The result's no longer a string, you can turn it into one after the end of the parallel for if you need to, but generally byte array is better for strong arbitrary data anyway.
If your block operation changes the size (how does that work? and how did you figure out how much to stick into each block on the encryption side?) then
Note that the parallelization will only work if your
byte[] encryptedBytes = Enumerable.Range( 0, encrypted.Length -1 )
.Where( x => x % 2 == 0 )
.Select( x => Convert.ToByte( encrypted.Substring( x, 2 ), 16 ) )
.ToArray();
byte[] buffer = new byte[( rsa.KeySize / 8 )]; // the number of bytes to decrypt at a time
int bytesRead = 0;
using ( Stream stream = new MemoryStream( encryptedBytes ) ) {
while ( (bytesRead = stream.Read( buffer, 0, buffer.Length )) > 0 ) {
decrypted = decrypted + Encoding.UTF8.GetString( rsa.Decrypt( buffer, false ) );
}
}with
using System.Runtime.Remoting.Metadata.W3cXsd2001;
byte[] encryptedBytes = SoapHexBinary.Parse(encrypted);
byte[] decryptedBytes = new byte[encryptedBytes.Length];
int blockSize = rsa.KeySize >> 3;
int blockCount = 1 + (encryptedBytes.Length - 1) / blockSize;
Parallel.For(0, blockCount, (i) => {
var offset = i * blockSize;
var buffer = new byte[Math.Min(blockSize, encryptedBytes.Length - offset)];
Buffer.BlockCopy(encryptedBytes, offset, buffer, 0, buffer.Length);
Buffer.BlockCopy(rsa.Decrypt(buffer, false), 0, decryptedBytes, offset, buffer.Length);
});We use a highly-optimized builtin class for converting hexadecimal description of a byte array into that byte array. Much simpler, much faster.
Then, we skip the MemoryStream entirely, since extra wrappers can only slow things down, and streams force us into sequential operation.
Finally, we use Parallel.For to process each block, passing it through
rsa.Decrypt, and storing the output at the same array index the input came from.The result's no longer a string, you can turn it into one after the end of the parallel for if you need to, but generally byte array is better for strong arbitrary data anyway.
If your block operation changes the size (how does that work? and how did you figure out how much to stick into each block on the encryption side?) then
int blockSize = rsa.KeySize >> 3;
int blockCount = 1 + (encryptedBytes.Length - 1) / blockSize;
var decryptedChunks = new byte[][blockCount];
Parallel.For(0, blockCount, (i) => {
var offset = i * blockSize;
var buffer = new byte[Math.Min(blockSize, encryptedBytes.Length - offset)];
Buffer.BlockCopy(encryptedBytes, offset, buffer, 0, buffer.Length);
decryptedChunks[i] = rsa.Decrypt(buffer, false);
});
var decryptedBytes = decryptedChunks.SelectMany(x => x);Note that the parallelization will only work if your
rsa.Decrypt method is thread-safe/stateless. If it's not, find one that is.Code Snippets
byte[] encryptedBytes = Enumerable.Range( 0, encrypted.Length -1 )
.Where( x => x % 2 == 0 )
.Select( x => Convert.ToByte( encrypted.Substring( x, 2 ), 16 ) )
.ToArray();
byte[] buffer = new byte[( rsa.KeySize / 8 )]; // the number of bytes to decrypt at a time
int bytesRead = 0;
using ( Stream stream = new MemoryStream( encryptedBytes ) ) {
while ( (bytesRead = stream.Read( buffer, 0, buffer.Length )) > 0 ) {
decrypted = decrypted + Encoding.UTF8.GetString( rsa.Decrypt( buffer, false ) );
}
}using System.Runtime.Remoting.Metadata.W3cXsd2001;
byte[] encryptedBytes = SoapHexBinary.Parse(encrypted);
byte[] decryptedBytes = new byte[encryptedBytes.Length];
int blockSize = rsa.KeySize >> 3;
int blockCount = 1 + (encryptedBytes.Length - 1) / blockSize;
Parallel.For(0, blockCount, (i) => {
var offset = i * blockSize;
var buffer = new byte[Math.Min(blockSize, encryptedBytes.Length - offset)];
Buffer.BlockCopy(encryptedBytes, offset, buffer, 0, buffer.Length);
Buffer.BlockCopy(rsa.Decrypt(buffer, false), 0, decryptedBytes, offset, buffer.Length);
});int blockSize = rsa.KeySize >> 3;
int blockCount = 1 + (encryptedBytes.Length - 1) / blockSize;
var decryptedChunks = new byte[][blockCount];
Parallel.For(0, blockCount, (i) => {
var offset = i * blockSize;
var buffer = new byte[Math.Min(blockSize, encryptedBytes.Length - offset)];
Buffer.BlockCopy(encryptedBytes, offset, buffer, 0, buffer.Length);
decryptedChunks[i] = rsa.Decrypt(buffer, false);
});
var decryptedBytes = decryptedChunks.SelectMany(x => x);Context
StackExchange Code Review Q#151376, answer score: 8
Revisions (0)
No revisions yet.