I am trying to use RSA encryption in JavaScript and then decrypt it in C#. In JavaScript I am using the library jsencrypt. In
C# I using the API "bouncy castle". When I do the encryption/decryption within the same language everything works. I get back the
correct text when I decrypt it. When I try to decrypt in C# what was encrypted in JavaScript I get nothing close.
I am sure the keys are the same between the two.
An example of the code is below. Any help on how to solve this would be greatly appreciated.
JavaScript
//using jsencrypt.min.js
var encrypt = new JSEncrypt();
encrypt.setPublicKey($('#pubkey').val());
var encrypted = encrypt.encrypt($('#input').val());
take the value I get from JavaScript "encrypted" and use it in C# for "encyp"
AsymmetricCipherKeyPair KeyParameterPrivate;
byte[] cipheredBytes = Convert.FromBase64String(encyp);
string privateKeyFileName = @"C:\private.pem";
using (var fileStream2 = File.OpenText(privateKeyFileName))
{
PemReader pemReader2 = new Org.BouncyCastle.OpenSsl.PemReader(fileStream2);
KeyParameterPrivate = (Org.BouncyCastle.Crypto.AsymmetricCipherKeyPair)pemReader2.ReadObject();
}
IAsymmetricBlockCipher cipher2 = new Org.BouncyCastle.Crypto.Engines.RsaEngine();
RsaKeyParameters privateKey2 = (RsaKeyParameters)KeyParameterPrivate.Private;
//cipher.Init(false, publicKey4);
cipher2.Init(false, privateKey2);
byte[] deciphered = cipher2.ProcessBlock(cipheredBytes, 0, cipheredBytes.Length);
string decipheredText = utf8enc.GetString(deciphered);
Why would you want to torture yourself using BC for this?
The easiest approach to decryption here is :
// store is a X509Store pointing to the correct store on the target machine
// You have to ensure that the security principal for your app has access to the private key to decrypt
X509Certificate2 cert = store.Certificates.Find(X509FindType.FindByThumbprint, "sha1hash", false)[0];
var prov = (RSACryptoServiceProvider)cert.PrivateKey;
var decipheredText = Encoding.UTF8.GetString(prov.Decrypt(Convert.FromBase64String(target), false));
Obviously you can get your X509Certificate2 from file or any other means, e.g. X509Certificate2 cert = new X509Certificate2(@"C:\someCert.pfx", "somePass");
If you followed jsencrypt tutorial, use this openssl command to get the pfx out of the pems you have:
openssl pkcs12 -export -out certificate.pfx -inkey privateKey.pem -in publicKey.pem
You'll have to use new PKCS1Encoding(cipher2)
. The RSAEngine
only produces plain (also known as raw or textbook) RSA.
My advice is to keep it as simple as possible and not use Bouncy Castle for this. You need to create a public key for encryption, private key for decryption and a certificate to fetch the private key from.
First, create private key and certificate PEM files using OpenSSL:
openssl req -newkey rsa:1024 -nodes -keyout private_key.pem -x509 -days 365 -out certificate.pem
Then create a public key PEM file from the certificate that you created:
openssl x509 -pubkey -noout -in certificate.pem > public_key.pem
Then export a PFX file using the private key and certificate PEM files that you created:
openssl pkcs12 -export -out certificate.pfx -inkey private_key.pem -in certificate.pem
When you do the export, you'll be asked to provide a certificate password.
Now here is how to do the RSA decryption in C#:
var cert = new X509Certificate2(@"C:\path\to\certificate.pfx", "password");
var rsaCng = (RSACng)cert.PrivateKey;
var decryptedText = Encoding.UTF8.GetString(rsaCng.Decrypt(Convert.FromBase64String(encryptedText), RSAEncryptionPadding.Pkcs1));
Tell me if it works for you.
public string Decrypt(RSACryptoServiceProvider provider, string toDecrypt)
{
var input = Convert.FromBase64String(toDecrypt);
IEnumerable<byte> output = new List<byte>();
for (var i = 0; i < input.Length; i = i + input.Length)
{
var length = Math.Max(input.Length - i, 128);
var block = new byte[length];
Buffer.BlockCopy(input, i, block, 0, length);
var chunk = provider.Decrypt(block, false);
output = output.Concat(chunk);
}
return Encoding.UTF8.GetString(output.ToArray());
}