Asymetric Encryption in C# and #Javascript
Here is a scenario, where you generate RSA asymetric encryption keys in C#, use the public key to encrypt a message, then decrypt it using Javascript.
First, generate a pair of keys in C#
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(2048);
tbPublicKey.Text = rsa.ToXmlString(false); // false to get the public key
tbPrivateKey.Text = rsa.ToXmlString(true); // true to get the private key
I’ve selected a key length of 2014 bits, this gives an output cypher text of 255 bytes, or 344 bytes when base64 encoded.
You then use code such as the following to encrypt:
static string EncryptText(string publicKey, string text)
{
// Convert the text to an array of bytes
UTF8Encoding byteConverter = new UTF8Encoding();
byte[] dataToEncrypt = byteConverter.GetBytes(text);// Create a byte array to store the encrypted data in it
byte[] encryptedData;
using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
{
// Set the rsa pulic key
rsa.FromXmlString(publicKey);// Encrypt the data and store it in the encyptedData Array
// MAX 245 bytes
encryptedData = rsa.Encrypt(dataToEncrypt, false);
}
// Base 64 encode enctrypted data
return Convert.ToBase64String(encryptedData);
}
Then the following code to decrypt
static string DecryptText(string privateKey, string cyphertext)
{
// read the encrypted bytes from the file
byte[] dataToDecrypt = Convert.FromBase64String(cyphertext);// Create an array to store the decrypted data in it
byte[] decryptedData;
using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
{
// Set the private key of the algorithm
rsa.FromXmlString(privateKey);
decryptedData = rsa.Decrypt(dataToDecrypt, false);
}// Get the string value from the decryptedData byte array
UTF8Encoding byteConverter = new UTF8Encoding();
return byteConverter.GetString(decryptedData);
}
Now, you may note the comment “// MAX 245 bytes”, This is important, since if you try to encrypt text over 245 bytes, the code will break. Note, I’m using UTF8 here, if you wanted to support chinese text, you’d need Unicode, which sets the max to 122
You can however, break the text into 245 byte chunks as follows; (Using a pipe char as a seperator)
static string EncryptLongText(string publicKey, string text)
{
var strOutput = “”;
var stringParts = Split(text, 245);
foreach(var stringPart in stringParts)
{
strOutput += EncryptText(publicKey, stringPart) + “|”;
}
return strOutput;
}public static IEnumerable<string> Split(string str, int chunkSize)
{
if (string.IsNullOrEmpty(str) || chunkSize < 1)
throw new ArgumentException(“String can not be null or empty and chunk size should be greater than zero.”);
var chunkCount = str.Length / chunkSize + (str.Length % chunkSize != 0 ? 1 : 0);
for (var i = 0; i < chunkCount; i++)
{
var startIndex = i * chunkSize;
if (startIndex + chunkSize >= str.Length)
yield return str.Substring(startIndex);
else
yield return str.Substring(startIndex, chunkSize);
}
}
Then reverse the process with a Regex;
static string DecryptLongText(string privateKey, string text)
{
var strOutput = “”;
var stringParts = Regex.Split(text, @”\|”);
foreach (var stringPart in stringParts)
{
if (stringPart != “”) strOutput += DecryptText(privateKey, stringPart);
}
return strOutput;
}
That’s all there is to it in C#, now to turn to Javascript, you will need to look at the format of the keys;
<RSAKeyValue>
<Modulus>….</Modulus>
<Exponent>…</Exponent>
<P>….</P>
<Q>….</Q>
<DP>….</DP>
<DQ>….</DQ>
<InverseQ>…</InverseQ>
<D>…</D>
</RSAKeyValue>
The parts of the keys (elided with …) are in base64 format, you will need this in Hex format for Javascript, and a tool such as https://cryptii.com/base64-to-hex can do this online for you.
Then following the code example on http://www-cs-students.stanford.edu/~tjw/jsbn/rsa2.html – the values match up as follows;
Modulus: Modulus (hex): [n]
Exponent : Public exponent (hex, F4=0x10001): [e]
P: P (hex): [p]
Q: Q (hex): [q]
DP: D mod (P-1) (hex): [dmp1]
DQ: D mod (Q-1) (hex): [dmq1]
InverseQ : 1/Q mod P (hex): [coeff]
D : Private exponent (hex): [d]