Public and Private Key as variables C# - c#

I'm writing a function where this function is receiving my public key as variable, and the value for this variable is the actually public key. I need two different functions apps in Azure, to encrypt and decrypt. The keys must match, but the problem is, every time I call the API the public key is different, I can encrypt without problems. But when I have to decrypt it doesn't work. I am not able to use the same key pairs for these functions. Thats why Im trying to use the keys I generated before as variables.
Example:
string publicKey = "MMMFisIDUDHfhHSANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAi7ZOKtc55v9NJuhQFR583BcFkcjflXNVMqC5/3b7t7v..."
This is the method I'm using to encrypt:
cipher.Init(true, publicKey);
My keys are being generated using Bouncy Castle.
RsaKeyPairGenerator g = new RsaKeyPairGenerator();
g.Init(new KeyGenerationParameters(new SecureRandom(), 2048));
AsymmetricCipherKeyPair keyPair = g.GenerateKeyPair();
It worked normally with the code below:
string plainText = "test data here";
byte[] plainTextToByte = Encoding.UTF8.GetBytes(plainText);
//Generating Key Pair
RsaKeyPairGenerator g = new RsaKeyPairGenerator();
g.Init(new KeyGenerationParameters(new SecureRandom(), 2048));
AsymmetricCipherKeyPair keyPair = g.GenerateKeyPair();
//Extracting the private key from pair
RsaKeyParameters privateKey = (RsaKeyParameters)keyPair.Private;
RsaKeyParameters publicKey = (RsaKeyParameters)keyPair.Public;
//Encryption proccess
IAsymmetricBlockCipher cipher = new OaepEncoding(new RsaEngine());
cipher.Init(true, publicKey);
byte[] cipherText = cipher.ProcessBlock(plainTextToByte, 0, plainTextToByte.Length);
string encryptedText = Encoding.UTF8.GetString(cipherText);
Console.WriteLine(encryptedText);
//Decryption Process
cipher.Init(false, privateKey);
byte[] decryptedText = cipher.ProcessBlock(cipherText, 0 , cipherText.Length);
string decryptedTextToString = Encoding.UTF8.GetString(decryptedText);
Console.WriteLine(decryptedTextToString);
Console.ReadLine();`
I need the keys generated above as a variable to use in a function inside a console app.
But when I try pass the key as variable, I'm getting the error below:
https://i.stack.imgur.com/vLSOL.png
I could do same procedure using core classes from C#, it was similar with the code below:
C# RSA encryption/decryption with transmission
The same logic I follow for the example above is not working for me now. I am beginner into all this.
Is there a way to do that?
This is the piece code I'm using to get the error on the screenshot. The keys were generated with the code I posted on the original post.
string plainText = "test here";
byte[] plainTextToByte = Encoding.UTF8.GetBytes(plainText);
string publicKey = "MIIBIjANBgk...DAQAB";
IAsymmetricBlockCipher cipher = new OaepEncoding(new RsaEngine());
cipher.Init(true, publicKey);
byte[] cipherText = cipher.ProcessBlock(plainTextToByte, 0, plainTextToByte.Length);
string encryptedText = Encoding.UTF8.GetString(cipherText);
Console.WriteLine(encryptedText);
return new OkObjectResult(encryptedText);`
Att.

I'm not quite clear what the problem is. But based on the last snippet posted in the question, you are trying to import a public key. And according to your penultimate comment, it is a PEM encoded public key in X.509/SPKI format exported with a PemWriter:
-----BEGIN PUBLIC KEY-----
MIIB...
...AQAB
-----END PUBLIC KEY-----
Such a key can be imported and used in Cipher#Init() as follows (let publicKeyPem be the exported PEM key):
using Org.BouncyCastle.OpenSsl;
...
PemReader pemReader = new PemReader(new StringReader(publicKeyPem));
RsaKeyParameters publicKeyReloaded = (RsaKeyParameters)pemReader.ReadObject();
...
cipher.Init(true, publicKeyReloaded);

Related

generate RSA public/private key based on a passphrase using bouncy Castel library in c#

I'd like to generate an RSA key pair with a private key encrypted with passphrase using the Bouncy Castel Library in C# and save them to separate files so that I can use them to encrypt or decrypt things whenever I need to...
I'm able to generate an RSA keypair and store them in separate PEM files. I believe it is in PKCS1 format, but I can't seem to figure out how to generate Keypair based on the password.
I looked on the official site, https://www.bouncycastle.org/csharp, but couldn't find any examples.
I also searched all the way through stack overflow with no success.
At least I figured out How to generate RSA Keypair with passphrase to encrypt the private key, using AES-256-CBC with BouncyCastle C# (1.9.2)
Note: Both the key's are in PEM format.
I got help from the following Link: https://csharp.hotexamples.com/site/file?hash=0xdc447ca00ad5a1a69649f92339871f7caeada0c6c47b8b39ccad91e46cf75d74&fullName=Certificate.cs&project=nicholaspaun/Kalkulator1
//Create Random
CryptoApiRandomGenerator randomGenerator = new CryptoApiRandomGenerator();
//RSAKeyPairGenerator generates the RSA keypair based on the random number and strength of the key required
RsaKeyPairGenerator rsaKeyPairGen = new RsaKeyPairGenerator();
rsaKeyPairGen.Init(new KeyGenerationParameters(new SecureRandom(randomGenerator), 2048));
AsymmetricCipherKeyPair keyPair = rsaKeyPairGen.GenerateKeyPair();
//Extracting the private key from the pair
RsaKeyParameters Privatekey = (RsaKeyParameters)keyPair.Private;
//Extracting the public key from the pair
RsaKeyParameters Publickey = (RsaKeyParameters)keyPair.Public;
//Creating public key in pem format\
TextWriter pubtxtWriter = new StringWriter();
PemWriter pubpemWriter = new PemWriter(pubtxtWriter);
pubpemWriter.WriteObject(Publickey);
pubpemWriter.Writer.Flush();
//now save the follwing string variable into a file. that's our public key
string print_publicKey = pubtxtWriter.ToString();
//encrypted Private Key
string password = "xxxxx"; //give desired password, with good strength
AsymmetricKeyParameter privateKey = keyPair.Private;
StringWriter sw = new StringWriter();
PemWriter pw = new PemWriter(sw);
pw.WriteObject(privateKey, "AES-256-CBC", password.ToCharArray(), new SecureRandom());
pw.Writer.Close();
pw.Writer.Flush();
//now save the follwing string variable into a file. that's our "Encrypted private key"
string encprvKey = sw.ToString();

ECDH public and private keys generation with .Net C#

I am porting nodejs code to .Net and I am stuck at this part where I need to generate public and private keys.
Javascript code:
const dh = crypto.createECDH('prime256v1');
let privk = dh.getPrivateKey();
let pubk = dh.getPublicKey();
I tried the same with .Net C# with
var ecdh = new ECDiffieHellmanCng(CngKey.Create(CngAlgorithm.ECDiffieHellmanP256, null, new CngKeyCreationParameters { ExportPolicy = CngExportPolicies.AllowPlaintextExport }));
var privateKey = ecdh.Key.Export(CngKeyBlobFormat.EccPrivateBlob);
var publickey = ecdh.Key.Export(CngKeyBlobFormat.EccPublicBlob);
However when I tried to exchange those keys generated with C# with the Google FCM server, I got invalid argument error. When I copy the generated byte[] array from nodejs to .Net C# code as constants it works. It is obvious that generated keys are not meeting the requirement of the server. Since I am working with undocumented interface I can't tell why the keys are not accepted. I can see that the keys generated with nodejs are in 32 bytes in length for the private key and 65 bytes in length for the public key. The keys generated from C# are 140 and 96 bytes in length. How to generate keys in C# to match the key properties in nodejs?
I was able to solve my problem using Bouncy Castle
ECKeyPairGenerator gen = new ECKeyPairGenerator("ECDH");
SecureRandom secureRandom = new SecureRandom();
X9ECParameters ecp = NistNamedCurves.GetByName("P-256");
ECDomainParameters ecSpec = new ECDomainParameters(ecp.Curve, ecp.G, ecp.N, ecp.H, ecp.GetSeed());
ECKeyGenerationParameters ecgp = new ECKeyGenerationParameters(ecSpec, secureRandom);
gen.Init(ecgp);
AsymmetricCipherKeyPair eckp = gen.GenerateKeyPair();
ECPublicKeyParameters ecPub = (ECPublicKeyParameters)eckp.Public;
ECPrivateKeyParameters ecPri = (ECPrivateKeyParameters)eckp.Private;
byte[] publicKeyBytes = ecPub.Q.GetEncoded();
You can use ECDiffieHellman to encrypt messages. You have two options: Static-static ECDH and static-ephemeral ECDH:
For static-static ECDH the receiver will need to know the sender's public key (this might or might not be an option in your application). You should also have some data that is unique for this message (it might be a serial-number you get from somewhere else in the protocol or database-row or whatever or it might be a nonce). You then use ECDH to generate a secret key and use that to encrypt your data. This will give you your desired encrypted data length of 16 bytes, but it is not completely asymmetric: the encryptor is also able to decrypt the messages (again: this might or might not be a problem in your application).
Static-ephemeral is a bit different: here the encryptor generates a temporary (ephemeral) EC key pair. He then uses this key pair together with the receiver's public key to generate a secret key which can be used to encrypt the data. Finally, he sends the public key of the ephemeral key pair to the receiver together with the encrypted data. This might fit better into your application, but the complete encrypted data will now be 2*32+16=80 bytes using ECDH-256 and AES (as GregS notes you can save 32 bytes by only sending the x-coordinate of the public-key, but I do not believe that .NET exposes the functionality to recalculate the y-coordinate).
Here is a small class that will do static-static ECDH:
public static class StaticStaticDiffieHellman
{
private static Aes DeriveKeyAndIv(ECDiffieHellmanCng privateKey, ECDiffieHellmanPublicKey publicKey, byte[] nonce)
{
privateKey.KeyDerivationFunction = ECDiffieHellmanKeyDerivationFunction.Hash;
privateKey.HashAlgorithm = CngAlgorithm.Sha256;
privateKey.SecretAppend = nonce;
byte[] keyAndIv = privateKey.DeriveKeyMaterial(publicKey);
byte[] key = new byte[16];
Array.Copy(keyAndIv, 0, key, 0, 16);
byte[] iv = new byte[16];
Array.Copy(keyAndIv, 16, iv, 0, 16);
Aes aes = new AesManaged();
aes.Key = key;
aes.IV = iv;
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
return aes;
}
public static byte[] Encrypt(ECDiffieHellmanCng privateKey, ECDiffieHellmanPublicKey publicKey, byte[] nonce, byte[] data){
Aes aes = DeriveKeyAndIv(privateKey, publicKey, nonce);
return aes.CreateEncryptor().TransformFinalBlock(data, 0, data.Length);
}
public static byte[] Decrypt(ECDiffieHellmanCng privateKey, ECDiffieHellmanPublicKey publicKey, byte[] nonce, byte[] encryptedData){
Aes aes = DeriveKeyAndIv(privateKey, publicKey, nonce);
return aes.CreateDecryptor().TransformFinalBlock(encryptedData,0, encryptedData.Length);
}
}
// Usage:
ECDiffieHellmanCng key1 = new ECDiffieHellmanCng();
ECDiffieHellmanCng key2 = new ECDiffieHellmanCng();
byte[] data = Encoding.UTF8.GetBytes("TestTestTestTes");
byte[] nonce = Encoding.UTF8.GetBytes("whatever");
byte[] encryptedData = StaticStaticDiffieHellman.Encrypt(key1, key2.PublicKey, nonce, data);
Console.WriteLine(encryptedData.Length); // 16
byte[] decryptedData = StaticStaticDiffieHellman.Decrypt(key2, key1.PublicKey, nonce, encryptedData);
Console.WriteLine(Encoding.UTF8.GetString(decryptedData));

Internal.Cryptography.CryptoThrowHelper.WindowsCryptographicException when Decrypting RSA in C#

I'm testing RSA in C# dotnet core. I create two RSA objects, one for encrypting and the other for decrypting. I export the public key from the first rsa object and import it for the other object. When the second one decrypt the cipher array, it throws Internal.Cryptography.CryptoThrowHelper.WindowsCryptographicException.
The code is below:
String plainstr = "Hello World";
RSA rsa1 = RSA.Create();
RSA rsa2 = RSA.Create();
rsa1.KeySize = 1024;
rsa2.KeySize = 1024;
byte[] cipherbytes = rsa1.Encrypt(Encoding.ASCII.GetBytes(plainstr), RSAEncryptionPadding.Pkcs1);
//If the parameter is true, it works well. But when I use it in an actual project, I won't pass the private key.
RSAParameters parameters = rsa1.ExportParameters(false);
rsa2.ImportParameters(parameters);
//Exception is here.
byte[] plaintbytes = rsa2.Decrypt(cipherbytes, RSAEncryptionPadding.Pkcs1);
Console.WriteLine(Encoding.ASCII.GetString(plaintbytes));
Console.ReadKey();
This is how RSA Encryption works. You can Encrypt with the public key but you can only Decrypt with the private key.
In your example you are encrypting the string with the private key of the rsa1 object, you are copying the public parameters of it to rsa2 and you are trying to decrypt with it.
Maybe you want to do the opposite?

Initializing RSA from String

I am trying to decrypt some text that is encrypted with RSA, I have the public key to do this
`
-----BEGIN RSA PUBLIC KEY-----
MIGWAoGBAMqfGO9sPz+kxaRh/qVKsZQGul7NdG1gonSS3KPXTjtcHTFfexA4MkGA
mwKeu9XeTRFgMMxX99WmyaFvNzuxSlCFI/foCkx0TZCFZjpKFHLXryxWrkG1Bl9+
+gKTvTJ4rWk1RvnxYhm3n/Rxo2NoJM/822Oo7YBZ5rmk8NuJU4HLAhAYcJLaZFTO
sYU+aRX4RmoF
-----END RSA PUBLIC KEY-----
`
How can I load this into RSACryptoServiceProvider because this can only load from XMLString and I do not know how to convert this to Xml format
The key size is 128
I tried to initialize it using the following code
public byte[] Decrypt128(byte[] input)
{
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(128);
rsa.ImportCspBlob(Encoding.ASCII.GetBytes(_longKey));
return rsa.Decrypt(input, true);
}
_longKey is the content between BEGIN and END and also including the BEGIN and END, bot Bad Version of provider.
This is not a duplicate question of How do you convert Byte Array to Hexadecimal String, and vice versa?
I already know how to convert byte to hex and hex to byte, but that in any way does not help me initializing RSACryptoServiceProvider maybe give me example how that would help but at this point it doesn't
You could use BouncyCastle which has a PemReader allowing you to extract the modulus and exponent for the key:
using (var reader = File.OpenText("mykey.key"))
{
var pem = new PemReader(reader);
var o = (RsaKeyParameters)pem.ReadObject();
using (var rsa = new RSACryptoServiceProvider())
{
var parameters = new RSAParameters();
parameters.Modulus = o.Modulus.ToByteArray();
parameters.Exponent = o.Exponent.ToByteArray();
rsa.ImportParameters(parameters);
// Do what you need to do with the RSACryptoServiceProvider instance
}
}
If you don't want to have a dependency on BouncyCastle in your project, once loaded the public key into the RSACryptoServiceProvider using this method you could export it to XML for future use:
string xml = rsa.ToXmlString(false);
File.WriteAllText("mykey.xml", xml);

RSA encryption by supplying modulus and exponent

I am creating a C# Winforms application which POSTs data to a server over HTTPS.
The login mechanism is supposed to be like this:
I send the username to the server, it responds with rsa-modulus and rsa-exponent
I encrypt the password using these given parameters and send username + password to the server for authentication
I have tried the RSACryptoServiceProvider class, but I cannot find samples or anything said on how we can do the encryption using a given modulus and exponent?.
I think that without specifying any values, its doing default encryption parameters..
So if anybody has done this before, can they give me some hints please? thanks
UPDATE: according to the suggestion by Mr. Carsten Konig, . I have tried to do it with RSAParameters and RSA.ImportParameters, but it returns a "BAD DATA" error with cryptographic exception. My code is given below.
I have also tried RSA.FromXmlString(mykey); (where mykey contains an xml string with modulus and exp) but I also get a "BAD DATA" errror with cryptographic exception... any idea anybody? or if its some microsoft bug, can anyone suggest some other decent library to do this easily?
RSAParameters rsaparam = new RSAParameters();
rsaparam.Modulus = modbytes;
rsaparam.Exponent = expbytes;
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider() ;
RSA.ImportParameters(rsaparam);
byte[] encryptedData = RSA.Encrypt(dataToEncrypt, false)
You can do this by using the RSACryptoServiceProvider.Encrypt method. You will also need to use the RSACryptoServiceProvider.ImportParameters method and pass it an RSAParameters structure (this is where you set the exponent, modulus, etc).
Please have a look at the documentation in the link for the RSAParameters - it's very well documented what parameter you have to pass for what structure-field - should be no problem if you now the algorithm.
EDIT: here is the example straight from the MSDN-site:
class RSACSPSample
{
static void Main()
{
try
{ //initialze the byte arrays to the public key information.
byte[] PublicKey = {214,46,220,83,160,73,40,39,201,155,19,202,3,11,191,178,56,
74,90,36,248,103,18,144,170,163,145,87,54,61,34,220,222,
207,137,149,173,14,92,120,206,222,158,28,40,24,30,16,175,
108,128,35,230,118,40,121,113,125,216,130,11,24,90,48,194,
240,105,44,76,34,57,249,228,125,80,38,9,136,29,117,207,139,
168,181,85,137,126,10,126,242,120,247,121,8,100,12,201,171,
38,226,193,180,190,117,177,87,143,242,213,11,44,180,113,93,
106,99,179,68,175,211,164,116,64,148,226,254,172,147};
byte[] Exponent = {1,0,1};
//Values to store encrypted symmetric keys.
byte[] EncryptedSymmetricKey;
byte[] EncryptedSymmetricIV;
//Create a new instance of RSACryptoServiceProvider.
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
//Create a new instance of RSAParameters.
RSAParameters RSAKeyInfo = new RSAParameters();
//Set RSAKeyInfo to the public key values.
RSAKeyInfo.Modulus = PublicKey;
RSAKeyInfo.Exponent = Exponent;
//Import key parameters into RSA.
RSA.ImportParameters(RSAKeyInfo);
//Create a new instance of the RijndaelManaged class.
RijndaelManaged RM = new RijndaelManaged();
//Encrypt the symmetric key and IV.
EncryptedSymmetricKey = RSA.Encrypt(RM.Key, false);
EncryptedSymmetricIV = RSA.Encrypt(RM.IV, false);
Console.WriteLine("RijndaelManaged Key and IV have been encrypted with RSACryptoServiceProvider.");
}
//Catch and display a CryptographicException
//to the console.
catch(CryptographicException e)
{
Console.WriteLine(e.Message);
}
}
}
Please note that only the key/iv gets encrypted - not arbitrary bytes - the length of those bytes is important too!
The allowed length is described in MSDN an depends on the OS!
If you are using RSACryptoServiceProvider.ToXmlString to export the modulus and exponent that the server sends, you need to use Convert.FromBase64String.
public RSAParameters SetPublicKey(string modulus, string exponent)
{
RSAParameters result = new RSAParameters();
result.Modulus = Convert.FromBase64String(modulus);
result.Exponent = Convert.FromBase64String(exponent);
return result;
}
One additional hint that was very useful for me:
In this line,
//Set RSAKeyInfo to the public key values.
SAKeyInfo.Modulus = PublicKey;
PublicKey can also be a direct, straightforward, array of bytes that you can get from the "Public Key" field of a X509 Certificate (directly).

Categories

Resources