RSA Public and Private keys in string format - c#

How to use Public and Private RSA keys provided as string for encryption and decryption. as i'm using RSACryptoServiceProvider, it requires XML format, so is there any possibility to use string as provided. Thanks.

You have to parse the initial string to get all vectors, public keys, exponents separately. Then convert this strings to byte and use this piece of code as an example:
RSAParameters parameters = new RSAParameters();
//here you have to set all provided exponents, vectors
parameters.Exponent = myExponent; //...and other properties
//then - create new RSA cryptoservise provider and import parameters
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
RSA.ImportParameters(parameters);
To convert string to byte use this expression - it's already tested:
public static byte[] String2ByteArray(string Hex, int j = -2) =>
(Hex.StartsWith("0x") ? Hex = Hex.Substring(2) : Hex).Where((x, i) => i % 2 == 0)
.Select(x => byte.Parse(Hex.Substring(j += 2, 2), NumberStyles.HexNumber)).ToArray();
public static Action<string> writeOutput = (x) => Debug.WriteLine(x);
And if you're given XML like so:
<RSAKeyValue>
<Modulus>…</Modulus>
<Exponent>…</Exponent>
<P>…</P>
<Q>…</Q>
<DP>…</DP>
<DQ>…</DQ>
<InverseQ>…</InverseQ>
<D>…</D>
</RSAKeyValue>
You can parse this XML to retrieve all the parameters. So assign them to
parameters.Modulus
parameters.Exponent
parameters.P
parameters.D
parameters.DP
parameters.DQ
parameters.InverseQ

Having used Bouncy castle dll, pasting key info into text file and using below code i've solved the problem:
public string RSABouncyEncrypt(string content)
{
var bytesToEncrypt = Encoding.UTF8.GetBytes(content);
AsymmetricKeyParameter keyPair;
using (var reader = File.OpenText(#"E:\......\public.pem"))
{
keyPair = (AsymmetricKeyParameter)new Org.BouncyCastle.OpenSsl.PemReader(reader).ReadObject();
}
var engine = new RsaEngine();
engine.Init(true, keyPair);
var encrypted = engine.ProcessBlock(bytesToEncrypt, 0, bytesToEncrypt.Length);
var cryptMessage = Convert.ToBase64String(encrypted);
//Logs.Log.LogMessage("encrypted: " + cryptMessage);
//Console.WriteLine(cryptMessage);
//Decrypt before return statement to check that it has been encrypted correctly
//RSADecrypt(cryptMessage);
return cryptMessage;
}
public string RSADecrypt(string string64)
{
var bytesToDecrypt = Convert.FromBase64String(string64); // string to decrypt, base64 encoded
AsymmetricCipherKeyPair keyPair;
using (var reader = File.OpenText(#"E:\.....\private.pem"))
keyPair = (AsymmetricCipherKeyPair)new Org.BouncyCastle.OpenSsl.PemReader(reader).ReadObject();
var decryptEngine = new RsaEngine();
decryptEngine.Init(false, keyPair.Private);
var decrypted = Encoding.UTF8.GetString(decryptEngine.ProcessBlock(bytesToDecrypt, 0, bytesToDecrypt.Length));
//Logs.Log.LogMessage("decrypted: " + decrypted);
//Console.WriteLine(decrypted);
return decrypted;
}

Related

Decrypt and verify hash for SHA256 - c#

I am trying to decrypt and validate the hash but at times of decrypting, it throws me the error 'Key does not exist' and at the time of validating the hash return False
https://payvyne.readme.io/docs/webhooks
Signature:
HEjoCsghC9X0slrE2DprptDLYdoA7jaw4Jl7vpJVxzx9GNJEiO3pYGLDPhLmVqk98QJJ/FuiS5J+fvp+msr3Y8aFzKqjRQXj5TBELT38N+A7I8y3Vc0mgeR0aDMx7I83yhfkcoyhdiGJibzqQ5SYFZ0nnEVHYXheLUlga45yg/McDICtMm6lhnrPWEuHzoZTQkhsrLN/1W1PtLjJ2DickWB78PmhpeflL2Cpe6qS3qCclqFGZ7HIl9OoxU4WXpTYgxw7eixAKB7apFdFqea4BnGravfENNl97pOBuU6fRof4KtMczVagQw3QnxFD3BBtpTepRaT+jHY8wStXUG1bxllH32WiA9CVcpY4mxKhpxzQ8YD0b+3OgkpzZYS+BVVAdVazMJeEAw7v/zaxpjbR+Zo5l9vOLdyatwM75qpwMoKnMeKJHeRytEOK54al49OHiaE+v1OkOhJA0zh5nLzEIZanIdf+hXHDz3Euecs/p0cABiFNmhzYY5fl8qEytK6j2CjXQOYgljG5dqPm7M9CW36ntZTDaIEVWql3jdi9frxc4/82w1jhROFL0pBG1zz8nimAEesB1AaxmNqW7BIxULweX7eaReeo/dIqDSbmFuT+TikPQo4XRtmpDqO37Y9P6q7ZXtHOFopSaykHUHs+NgrKlBJMM5ADg5bHWm2Qows=
Public key:
pA6ULfXWrIMq-qvxn_0CykoStq0ZMYm63lHsuXTsE4q4tgekLJDW2Lnf35ilbFU_vybBdyeJAphpsYc4P0eJBt_z2T62HAV3gnwp_GU6hWIo8faK31TSXIrLmGjZlAVynAxjFYZoNxMeZuwEXpxG4bRGs58P7XSx1fAzedX6oGIlcSLljKH4I1BHt6gJhPIHYNXQzq_a0hX54C1m1VDVP_kot8ui1YKZil_riROK_Xk4ktnOTAqXo9z4uNBqzzH2k0J2YNiCb8VOdbp7kjmH9sPLI-jb-ociy0wSkGZc1e8saGIkkSm4eUASvX_M_TTDD99OrgoIS2Vx07Tw4lK5yd28EMVBUzy2OypuPVf9PyoDGv_4241x5PpJsA9IKocD7AgwxJ3E7FBFhvuSP8c5wspkbQxBwv5nnk2zAxuZsiJeK0o3JSxjkZJEkeVY4mA3VV9SvSXEKAFg2h9J3CR9PTwrZoVBruycVtWJ4it5jroXff-aGlLoRAO0g3gtfjkJb3tw6SJTFOA49iJci76Mj8Adz3eeEEGxTxfDzh_lq0jXxTk7cQSaR2_ChYLHaoorrrFmAvWgDH_lSvlISIgey-SzUoJM9RAy4gVFdmg-XCQQlpMh_d1-IACO3EfBvYKWE-6uGIqx1nZhn9WIDdSqMp6940xRxl0vQy8vYCQ5q8U
Data for Sign in string:
{"type":"PAYMENT_STATUS_CHANGE","paymentId":"1c6e834f074ec941","status":"FAILED","timestamp":1652688286662,"amount":"164.69","currency":"GBP","description":"This is test payment","paymentType":"ONE_OFF","bankName":"Diamond bank","destinationAccount":"GBP2","createdAt":"2022-05-16T08:04:32.994","updatedAt":"2022-05-16T08:04:46.662","customerReference":"1199","refundedAmount":"0.00"}
Expo (exponent):
AQAB
Below is the code to Decrypt the signature using public key.
public static void DecryptUsingPublicKey(string publicKey, string expo, string signature)
{
var modulus = ConvertToBase64(publicKey);
var exponent = Convert.FromBase64String(expo);
RSACryptoServiceProvider csp = new RSACryptoServiceProvider(2048);
var _publicKey = csp.ExportParameters(false);
_publicKey.Modulus = modulus;
_publicKey.Exponent = exponent;
csp.ImportParameters(_publicKey);
var dataBytes = ConvertToBase64(signature);
var plainText = csp.Decrypt(dataBytes, false);
var returnData = Encoding.Unicode.GetString(plainText);
Console.WriteLine($"value: {returnData}");
}
Below is the code for Verify signature using public key
public static void VerifySignature(string signature, string pKey, string dataForSign)
{
string pKeyNew = pKey;
pKeyNew = pKeyNew.Replace("_", "/").Replace("-", "+");
string publicKey = $"<RSAKeyValue><Modulus>{pKeyNew}==</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>";
var encoder = new UTF8Encoding();
byte[] dataForSignAsBytes = encoder.GetBytes(dataForSign);
byte[] signatureAsBytes = ConvertToBase64(signature);
RSACryptoServiceProvider rsaCryptoServiceProvider = new RSACryptoServiceProvider();
rsaCryptoServiceProvider.FromXmlString(publicKey);
var hashData = SHA256.Create().ComputeHash(dataForSignAsBytes);
var result1 = rsaCryptoServiceProvider.VerifyData(dataForSignAsBytes, CryptoConfig.MapNameToOID("SHA256"), signatureAsBytes);
var result2 = rsaCryptoServiceProvider.VerifyHash(hashData, CryptoConfig.MapNameToOID("SHA256"), signatureAsBytes);
var result3 = rsaCryptoServiceProvider.VerifyHash(hashData, signatureAsBytes, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
var result4 = rsaCryptoServiceProvider.VerifyData(dataForSignAsBytes, signatureAsBytes, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
Console.WriteLine(result1);
Console.WriteLine(result2);
Console.WriteLine(result3);
Console.WriteLine(result4);
}
ConvertToBase64 function
public static byte[] ConvertToBase64(string data)
{
byte[] cyperBuffer;
string dataNew = data;
dataNew = dataNew.Replace("_", "/").Replace("-", "+");
try
{
if (dataNew.Substring(dataNew.Length - 1) != "=")
{
dataNew += "=";
}
cyperBuffer = Convert.FromBase64String(dataNew);
}
catch
{
dataNew += "=";
try
{
cyperBuffer = Convert.FromBase64String(dataNew);
}
catch
{
//If any error occured while convert to base64 then append '=' at the end.
dataNew += "=";
cyperBuffer = Convert.FromBase64String(dataNew);
}
}
return cyperBuffer;
}
This is a conversion mistake; you need to decode the base 64 signature, not encode the signature, so the following line is wrong:
byte[] signatureAsBytes = ConvertToBase64(signature);
it should be something like:
byte[] signatureAsBytes = ConvertFromBase64(signature);
Decryption is modular exponentiation with a private key. Furthermore, encryption normally uses a different padding scheme than signature generation, so you'd expect that the unpadding would fail if you try and decrypt. Only verification is possible.

Trying to decrypt a string with public key(not private) using bouncycastle in .net

As a sample app, I encrypted a string using an rsa public key(supplied by a 3rd party api) using BouncyCastle library in .NET. When I send this encrypted string to the said 3rd party api endpoint, it is able to decrypt it(using its private key). I don't have their private key, but, is it possible to decrypt the string on my end using just the public key that I have?
From my understanding of RSA public/private key pair, when decrypting, you use the private key stored with you to decrypt a string and use the public key to confirm that you are receiving data from a said source.
public string RsaEncryptWithPublic(string clearText
, string publicKey)
{
var bytesToEncrypt = Encoding.UTF8.GetBytes(clearText);
var encryptEngine = new Pkcs1Encoding(new RsaEngine());
using (var txtreader = new StringReader(publicKey))
{
var keyParameter = (AsymmetricKeyParameter)new PemReader(txtreader).ReadObject();
encryptEngine.Init(true, keyParameter);
}
var encrypted = Convert.ToBase64String(encryptEngine.ProcessBlock(bytesToEncrypt, 0, bytesToEncrypt.Length));
return encrypted;
}
public string RsaDecrypt(string base64Input
, string privateKey)
{
var bytesToDecrypt = Convert.FromBase64String(base64Input);
//get a stream from the string
AsymmetricCipherKeyPair keyPair;
var decryptEngine = new Pkcs1Encoding(new RsaEngine());
using (var txtreader = new StringReader(privateKey))
{
keyPair = (AsymmetricCipherKeyPair)new PemReader(txtreader).ReadObject();
//decryptEngine.Init(false, keyPair.Private);
decryptEngine.Init(false, keyPair.Public);
}
var decrypted = Encoding.UTF8.GetString(decryptEngine.ProcessBlock(bytesToDecrypt, 0, bytesToDecrypt.Length));
return decrypted;
}
static void Main(string[] args)
{
string _creditCardNumber = "5454545454545454";
string publicKey = System.IO.File.ReadAllText(#"C:\ThirdPartyKeys\RSAPublicKey_01.txt");
var enc = new EncryptionClass();
var encryptedWithPublic = enc.RsaEncryptWithPublic(_creditCardNumber, publicKey);
Console.WriteLine("String: " + _creditCardNumber);
Console.WriteLine("Encrypted String: " + encryptedWithPublic);
// Decrypt
var outputWithPublic = enc.RsaDecrypt(encryptedWithPublic, publicKey);
//var outputWithPrivate = enc.RsaDecrypt(encryptedWithPrivate, _privateKey);
Console.WriteLine("Decrypted String: " + outputWithPublic);
}
The encryption works, but when I try to decrypt with the same public key, it complains of
Invalid Cast Exception:
Unable to cast object of type 'Org.BouncyCastle.Crypto.Parameters.RsaKeyParameters' to type 'Org.BouncyCastle.Crypto.AsymmetricCipherKeyPair'.
at line in RsaDecrypt function:
keyPair = (AsymmetricCipherKeyPair)new PemReader(txtreader).ReadObject();
No. It's Asymmetric Encryption which means you can't decrypt it with the public key. If you could, it would defeat the purpose and anyone with the public key would be able to decrypt your secret message

RSA Decryption using BouncyCastle with private PEM file not working

I'm doing some tests with BouncyCastle in C# and I want to encrypt some data and decrypt it later with a pair of keys that I have in my computer stored as PEM files.
public static string RSABouncyEncrypt(string content)
{
var bytesToEncrypt = Encoding.UTF8.GetBytes(content);
AsymmetricKeyParameter keyPair;
using (var reader = File.OpenText(#"C:\Users\Diego\Documents\public.pem")))
keyPair = (AsymmetricKeyParameter)new org.BouncyCastle.OpenSsl.PemReader(reader).ReadObject();
var engine = new RsaEngine();
engine.Init(true, keyPair);
var encrypted = engine.ProcessBlock(bytesToEncrypt, 0, bytesToEncrypt.Length);
var cryptMessage = Convert.ToBase64String(encrypted);
Logs.Log.LogMessage("encrypted: " + cryptMessage);
System.Windows.MessageBox.Show(cryptMessage);
//Decrypt before return statement to check that it has been encrypted correctly
RSADecrypt(cryptMessage);
return cryptMessage;
}
public static void RSADecrypt(string string64)
{
var bytesToDecrypt = Convert.FromBase64String(string64); // string to decrypt, base64 encoded
AsymmetricCipherKeyPair keyPair;
using (var reader = File.OpenText(#"C:\Users\Diego\Documents\private.pem"))
keyPair = (AsymmetricCipherKeyPair)new Org.BouncyCastle.OpenSsl.PemReader(reader).ReadObject();
var decryptEngine = new RsaEngine();
decryptEngine.Init(false, keyPair.Private);
var decrypted = Encoding.UTF8.GetString(decryptEngine.ProcessBlock(bytesToDecrypt, 0, bytesToDecrypt.Length));
Logs.Log.LogMessage("decrypted: " + decrypted);
System.Windows.MessageBox.Show(decrypted);
}
The RSADecrypt function shows an error. when I show the message box after decrypting I get this:
���Z��8o>>���;;�/�Z�ב?���#�F��(͌5���o1I�,���4� S�W��)��w��x�4p�$-|А���&��Rv}�G��V�c ��&wU?
�D�� }E���O����7�n��!(e��E��$y�g9ςOأ��P�� �t�d�T�nN��K$�bQ��!�v���-�Hb���1���?����#B�y� r��Le�h=*Yr�w
�l�W|�嘟��|g��EV��#�[��M
which is definitely not what I encrypted. What am I doing wrong?
Actually the answer why it is not working is that there is no information about padding. Correct way how to instantiate RsaEngine is sth. like this
var decryptEngine = new Pkcs1Encoding(RsaEngine())
var bytesToDecrypt = Convert.FromBase64String(string64); // string to decrypt, base64 encoded
AsymmetricCipherKeyPair keyPair;
using (var reader = File.OpenText(#"C:\Users\Diego\Documents\private.pem"))
keyPair = (AsymmetricCipherKeyPair)new Org.BouncyCastle.OpenSsl.PemReader(reader).ReadObject();
var decryptEngine = new Pkcs1Encoding(RsaEngine());
decryptEngine.Init(false, keyPair.Private);
var decrypted = Encoding.UTF8.GetString(decryptEngine.ProcessBlock(bytesToDecrypt, 0, bytesToDecrypt.Length));
Logs.Log.LogMessage("decrypted: " + decrypted);
System.Windows.MessageBox.Show(decrypted);
I reproduced this problem and it happened because you used a private key and a public key that don't match. In other words the message was encrypted with a private key (let's call it private_key_1) that came from one pair (private_key_1/public_key_1) but you tried to decrypt it with a public key (let's call it publick_key_2) that came from a different pair (private_key_2/public_key_2). Try to generate a new key pair and use it in your example e.g.:
var kpgen = new RsaKeyPairGenerator();
kpgen.Init(new KeyGenerationParameters(new SecureRandom(new CryptoApiRandomGenerator()), 1024));
var keyPair = kpgen.GenerateKeyPair();
using (var writer = new StreamWriter(File.OpenWrite(#"C:\Users\Diego\Documents\private2.pem")))
{
new PemWriter(writer).WriteObject(keyPair.Private);
}
using (var writer = new StreamWriter(File.OpenWrite(#"C:\Users\Diego\Documents\public2.pem")))
{
new PemWriter(writer).WriteObject(keyPair.Public);
}

C# RSA Decrypt Using Private Key

I've been searching but I can't seem to find a simple way of decrypting using RSA.
I have generated a public and private key, they are stored in two separate files and are in the XML format. I have no problem associating the public key to the RSACryptoServiceProvider object using FromXmlString, and then encrypting a string. My confusion comes when trying to decrypt an encrypted string. I'm not sure how I associate the private key data with the RSACryptoServiceProvider so that I can use the Decrypt function.
Any help would be appreciated.
EDIT:
The format of the public and private key is XML generated by the RSACryptoServiceProvider object, which I just put into a file:
<RSAKeyValue><Modulus>vS7Y5up+6kHMx7hQjKA6sKlIVASaw ... etc ...
I load the public key using this code:
StreamReader sr = new StreamReader(HttpContext.Current.Server.MapPath("public.key"));
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
rsa.FromXmlString(sr.ReadToEnd().ToString());
I currently haven't tried anything with the private key yet, since I'm not sure where to start.
I don't know your situation but I would suggest that you store you key information in a KeyContainer. If you do this you can access the keyContainer by name and can do something like this.
// retrieves the maximum number of characters that can be decrypted at once
private int getMaxBlockSize(int keySize){
int max = ((int)(keysize/8/3) )* 4
if (keySize / 8 mod 3 != 0){
max += 4
}
return max;
}
public string decrypt(string msg, string containerName){
CspParameters params = new CspParameters();
params.KeyContainerName = containerName;
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(params);
StringBuilder decryptedMsg = new StringBuilder();
int maxDecryptSize = getMaxBlockSize(rsa.KeySize);
int iterationCount = Math.Floor(msg.length / maxDecryptSize)
for(int i=0; i<iterationCount; i++){
int start = i * maxDecryptSize;
int blkSize = Math.min(start + maxDecryptSize, msg.Length);
Byte[] msgBytes = System.Convert.FromBase64String(msg.Substring(start, blkSize));
decryptedMsg.Append(System.Text.Encoding.Unicode.GetString(RSAProvider.Decrypt(msgBytes, false));
}
return decryptedMsg.ToString();
}
I haven't tested this out so there might be a bug in here but the you get the idea.
if you have private key in text format
like given below
-----BEGIN RSA PRIVATE KEY-----
text....
-----END RSA PRIVATE KEY-----
public string RsaDecryptWithPrivate(string base64Input, string privateKey)
{
var bytesToDecrypt = Convert.FromBase64String(base64Input);
AsymmetricCipherKeyPair keyPair;
var decryptEngine = new Pkcs1Encoding(new RsaEngine());
using (var txtreader = new StringReader(privateKey))
{
keyPair = (AsymmetricCipherKeyPair)new PemReader(txtreader).ReadObject();//fetch key pair from text file
decryptEngine.Init(false, keyPair.Private);
}
var decrypted = Encoding.UTF8.GetString(decryptEngine.ProcessBlock(bytesToDecrypt, 0, bytesToDecrypt.Length));
return decrypted;
}

Encode a RSA public key to DER format

I need a RSA public key encoded into DER format.
The key-pair is generated using RSACryptoServiceProvider.
What I'm looking for is the c# equivalent to the java:
PublicKey pubKey = myPair.getPublic();
byte[] keyBytes = pubKey.getEncoded();
I have tried looking into BouncyCastle but got lost so if the solution is there any pointers are welcome.
Using Bouncy Castle:
using Org.BouncyCastle.X509;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Parameters;
...
var generator = new RsaKeyPairGenerator ();
generator.Init (new KeyGenerationParameters (new SecureRandom (), 1024));
var keyPair = generator.GenerateKeyPair ();
RsaKeyParameters keyParam = (RsaKeyParameters)keyPair.Public;
var info = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo (keyParam);
RsaBytes = info.GetEncoded ();
The last three lines are the ones who take the RSA public key and export it.
I tried to use aforementioned info.GetEncoded() but as a result, I couldn't use it to verify the signature created with the corresponding private key.
What works for me is converting it to a X509Certificate2.
private bool VerifySignature(string stringContent, string stringSignature, X509Certificate2 x509PublicKey)
{
Int16 cbSignature = 0, cbCert = 0;
int cbData = 0;
CertBasicStruct sCert = new CertBasicStruct();
var pbCert = PublicKey.RawData;
cbCert = (Int16)pbCert.Length;
byte[] pbData = Encoding.UTF8.GetBytes(stringContent);
cbData = (Int16)pbData.Length;
byte[] pbSignature = Convert.FromBase64String(Signature);
cbSignature = (Int16)pbSignature.Length;
var rtn = DllImportService.DecodeCertificate(pbCert, cbCert, ref sCert);
var rtn_1 = DllImportService.VerifySignatureByCert(ref sCert, DllImportService.CKM_SHA256_RSA_PKCS, pbData, cbData, pbSignature, cbSignature);
if (rtn_1 == 0)
{
LogWndAppendLine("Signature is valid.");
return true;
}
else
{
LogWndAppendLine("Signature is invalid");
return false;
}
}
X509Certificate2.RawData would be what you are looking for.

Categories

Resources