I am trying o create a method that constructs an AsymmetricKeyParameter from a PEM encoded public key. Unfortunately, pemReader.ReadObject() return null.
Here's a working solution for a private key: convert PEM encoded RSA private key to AsymmetricKeyParameter
What is wrong with this method?
static AsymmetricKeyParameter ReadPublicKeyFromPemEncodedString(string pemEncodedKey)
{
AsymmetricKeyParameter result = null;
using (var stringReader = new StringReader(pemEncodedKey))
{
var pemReader = new PemReader(stringReader);
var pemObject = pemReader.ReadObject(); // null!
result = ((AsymmetricCipherKeyPair)pemObject).Public;
}
return result;
}
Here is the PEM-encoded public key I am testing with. I have tried without the comment and also removing SSH2.
---- BEGIN SSH2 PUBLIC KEY ----
Comment: "rsa-key-20170608"
AAAAB3NzaC1yc2EAAAABJQAAAQEAk0AmagKx285Ufbri/olc+f3WagL1Ho+DrYdD
SbuU7cJAq+uD9xGvvP9m2JavSP4wO9i9pB/cmCFMPoIj3oGJt1/cnLb/U2juneOw
6Uo0N3F8TXdyXfZNAIPhq/jw0YfIypTFTTvFkKXfTArIwW/bQBW8/dujFR8i5CxP
jRKRDOBEy0PPOLJDD0iUr9GX/h/EO4jQ7B/GszjhPiPx+gJCilaMY+jrSczjxpsK
OXzpZEdT1NqMrzgvIZPHYhQzAiw9vQzov3vezDwKgKcRrUixZ2B8uiEQNn7Wa2Qz
WF3vL+6CGflFNYQcc0leDQBe86baYhCollouP4jfaH9KcMkYYw==
---- END SSH2 PUBLIC KEY ----
Bouncy castle just does not understand this format of public key (SSH2) (you can verify this by looking at source code of PemReader if you would like to). Unfortunately I don't know how to convert it to appropriate format in C#, but you can do that with many tools, for example with ssh-keygen (also available in gitbash for windows), or openssl. Your public key will look like this when converted to PEM:
-----BEGIN RSA PUBLIC KEY-----
MIIBCAKCAQEAk0AmagKx285Ufbri/olc+f3WagL1Ho+DrYdDSbuU7cJAq+uD9xGv
vP9m2JavSP4wO9i9pB/cmCFMPoIj3oGJt1/cnLb/U2juneOw6Uo0N3F8TXdyXfZN
AIPhq/jw0YfIypTFTTvFkKXfTArIwW/bQBW8/dujFR8i5CxPjRKRDOBEy0PPOLJD
D0iUr9GX/h/EO4jQ7B/GszjhPiPx+gJCilaMY+jrSczjxpsKOXzpZEdT1NqMrzgv
IZPHYhQzAiw9vQzov3vezDwKgKcRrUixZ2B8uiEQNn7Wa2QzWF3vL+6CGflFNYQc
c0leDQBe86baYhCollouP4jfaH9KcMkYYwIBJQ==
-----END RSA PUBLIC KEY-----
And it will be correctly handled by your current code, with a little change:
var pemReader = new PemReader(stringReader);
var pemObject = pemReader.ReadObject(); // null!
// it's already AsymmetricKeyParameter
result = ((AsymmetricKeyParameter)pemObject);
Related
I'm getting an error in Private Key conversion, I can't decrypt.
Error: System.InvalidCastException: Could not cast object of type 'Org.BouncyCastle.Crypto.AsymmetricCipherKeyPair' to type 'Org.BouncyCastle.Crypto.AsymmetricKeyParameter'.
When I convert it to AsymmetricCipherKeyPair type, the type does not match in the bottom line. I am waiting for your help.
static void Main()
{
var plainData = "plain_text";
RSA publicKeyEncryptor = getRSAPublic(#"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlYB5JrwA9fMxZxTRhG0NnKRwJizMZGJNq/xFfFxaEmKp3O6vZgsZMlFTi2kSC++yR/KriGKuGgbIYrgomn7BueoooAw5KLVO9CKKtNyQgg28vdOBbnQqljA+KID0PouAD8MqpDk9opi41zeEQPOSkAUsq5sHMptG7h9cgj0mNr2c4ffNolHAhPsrZVtGYtswhtznDkG463VOKLAmDLDeY9bASUsQXFOY+Em93GHFjStgZSTIEBof6HbUqIQf2rGjuPYCQsB/94BFma58epGz12zUPwKFMuxg89wbLOCjyAkocgS9zDnwKr7DVv08GmCUVVqI6ySzbWpKhiqWQvz4hwIDAQAB");
var plainBytes = Encoding.ASCII.GetBytes(plainData);
string encryptedPayload = System.Convert.ToBase64String(publicKeyEncryptor.Encrypt(plainBytes, RSAEncryptionPadding.Pkcs1));
RSA privateKeyDecyrpt = getRSAPrivate();
var y = privateKeyDecyrpt.Decrypt(Encoding.ASCII.GetBytes(encryptedPayload), RSAEncryptionPadding.Pkcs1);
Console.WriteLine(encryptedPayload);
}
public static RSA getRSAPublic(string publicKey)
{
string publicKeyPem = $"-----BEGIN PUBLIC KEY-----\r\n{ publicKey }\r\n-----END PUBLIC KEY-----\r\n";
var pemReader = new PemReader(new StringReader(publicKeyPem));
AsymmetricKeyParameter keyPairRaw = (AsymmetricKeyParameter)pemReader.ReadObject();
RSAParameters rsaParams = DotNetUtilities.ToRSAParameters((RsaKeyParameters)keyPairRaw);
RSA rsaObj = System.Security.Cryptography.RSA.Create();
rsaObj.ImportParameters(rsaParams);
return rsaObj;
}
public static RSA getRSAPrivate()
{
string privateKeyPem = #"-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEAlYB5JrwA9fMxZxTRhG0NnKRwJizMZGJNq/xFfFxaEmKp3O6v
ZgsZMlFTi2kSC++yR/KriGKuGgbIYrgomn7BueoooAw5KLVO9CKKtNyQgg28vdOB
bnQqljA+KID0PouAD8MqpDk9opi41zeEQPOSkAUsq5sHMptG7h9cgj0mNr2c4ffN
olHAhPsrZVtGYtswhtznDkG463VOKLAmDLDeY9bASUsQXFOY+Em93GHFjStgZSTI
EBof6HbUqIQf2rGjuPYCQsB/94BFma58epGz12zUPwKFMuxg89wbLOCjyAkocgS9
zDnwKr7DVv08GmCUVVqI6ySzbWpKhiqWQvz4hwIDAQABAoIBAEJFR+8Gqbpcykpy
bQmxubX1Io2ZkCTzepDBbB/bZEYAHGIGIBQw2UN3z3vd4JUP9Mx14tm7PIfm987i
6YTKqZ97D/UaVgAYlt4brbbMivZLlp3i8t3+ep5G1lboCtzqw6K5Fd7kTNEVt+IX
BvYvwok68flD6GXjdQa7Oiu1ZYofxXOBg84RDWu6E6edDj/XaaX12WOYaQ4p6zDn
jyYNMiYYkKoUI1a884cN50WAAIGs0n3TxgJGXZa0n/Y3CpvuphAoHPQQs7ZyJPlP
k29gaFXdotzv1TDKk0I+eTIlonMl1EyW0wDPvfMuV7EYNNfmtmqrT3OWkOpUU8fO
9eDKhukCgYEA5nlVlTKAx5LsKI3KGRQF3jiWAaQAUKoON68Sx47fSJSv317QyZqM
qbQTLz5vSytbTyHhgIEc2dtdRbl7MYkvvzaizjPDEHEJSwlTmnzt81qGYtjNGC+j
tz6Bx5MGDE8Ax3ls60Lm0uH9TMQyJZWTepZGght5WXP5NpmzJ6zG7D0CgYEApg9S
zZyMN1rNtcz6Pqaqll33hchIQd+vh2LLKkHAoQP93oWtipxyT08yr4tQ5ke3nwjj
onIt6KR0U2aSIv9OUwXw2cSwghCIqTKmtont88WXYh9zRtnZ+U0An9r3x+fr7e8y
wpk7lFPP840pzQQjyEQ2s3woMlssqOiS6SyGMBMCgYB04KVBEypxixWN/1G05A2R
wxp3XIb4YTTykisw3khnU1fZLAkvo9ufl/1+oOfps+QLPkBQXamW5YLogAZ0eYCo
NHndnixW4yv2TJWEK8Sz+31ZFV702/vnSqCf5/RSO6JGhlJxAC10VjyROJHBs5fl
u92nz2z7qy9/u/Q5s4nxdQKBgAx6qFFVS2A5ja302nVs1vL32ssN8wgoRCubbAMf
79bp0uEvEIyTFzAIlpmEka7MgusLovepNvP9r9Q4qBDDOOKaVrA2zMDpdyun58ld
8ijYl3jDPkl7w5qtg7d/oBFAx4UY7aqcE1MhPUZjPFnwzrOVFLtGQEsQePm0iJ3H
P8pLAoGAUCC6KxGF20y0uBnSLS2q3iqAqZNO57ZLnJRAscsViNEhqn4G5OZbcANa
RsyFluCwjuKFg9yW9s6ZKzjPXsAEWTyXpayICSG4gJBRfdzUol5Sjo9JweqMhiVh
5G9uBYx3+kEpYNvJDcYK89HQ3fAcfeZ1o9sXYtwccOulSQ5euAE=
-----END RSA PRIVATE KEY-----";
var pemReader = new PemReader(new StringReader(privateKeyPem));
AsymmetricKeyParameter keyPairRaw = (AsymmetricKeyParameter)pemReader.ReadObject();
RSAParameters rsaParams = DotNetUtilities.ToRSAParameters((RsaKeyParameters)keyPairRaw);
RSA rsaObj = System.Security.Cryptography.RSA.Create();
rsaObj.ImportParameters(rsaParams);
return rsaObj;
}
The code essentially contains casting-related bugs that are most easily identified during debugging by determining the object types:
I'm getting an error in Private Key conversion, I can't decrypt. Error: System.InvalidCastException: Could not cast object of type 'Org.BouncyCastle.Crypto.AsymmetricCipherKeyPair' to type 'Org.BouncyCastle.Crypto.AsymmetricKeyParameter'.
You can't import the private key 1:1 like the public key, because both have different formats. The PemReader returns a different object type in the case of the private key, namely AsymmetricCipherKeyPair, which you cannot cast into an AsymmetricKeyParameter. This is what the error message says. So it must be:
AsymmetricCipherKeyPair keyPairRaw = (AsymmetricCipherKeyPair)pemReader.ReadObject();
When I convert it to AsymmetricCipherKeyPair type, the type does not match in the bottom line.
You need to modify this line as well. Here you have to pass a keyPairRaw.Private that must be cast to RsaPrivateCrtKeyParameters:
RSAParameters rsaParams = DotNetUtilities.ToRSAParameters((RsaPrivateCrtKeyParameters)keyPairRaw.Private);
With these changes the import of the private key works.
Another bug is in the decryption. The ciphertext is Base64 encoded during encryption, therefore it must be Base64 decoded during decryption and not ASCII encoded, i.e. correct is:
var decrypted = privateKeyDecyrpt.Decrypt(Convert.FromBase64String(encryptedPayload), RSAEncryptionPadding.Pkcs1);
Keep in mind that the options for importing keys in .NET are highly dependent on the version. There are versions where you can import PEM keys out-of-the-box (e.g. as of .NET 5), so that BouncyCastle is not needed.
I have a private key that was generated by running:
openssl req -new -sha384 -x509 -days 63524 -subj "/C=CA/ST=State/L=City/O=Org/CN=Org-CA" -extensions v3_ca -keyout Org-CA.key -out Org-CA.pem -passout pass:pass1234
I need to use this private key and certificate to sign client CSRs. I used to use OpenSSL to do this, but I need to do this in C# instead.
I've found examples of what I want to do online, but those examples are using bouncy castle and an un-encrypted private key.
The key file looks like this:
-----BEGIN ENCRYPTED PRIVATE KEY-----
....
-----END ENCRYPTED PRIVATE KEY-----
And I'm trying to load it by doing this:
var privatekey = File.ReadAllBytes(#"Org-CA.key");
var rsaKeyParameters = PrivateKeyFactory.DecryptKey("pass1234".ToArray(), privatekey);
But thats not working. I get an error saying Wrong number of elements in sequence (Parameter 'seq')'
Is this how I'm supposed to be decrypting and loading the private key?
Depending on your .NET version, you may not need BouncyCastle at all. As of .NET Core 3.1 there is RSA.ImportEncryptedPkcs8PrivateKey() for DER encoded encrypted private PKCS#8 keys and as of .NET 5.0 there is even RSA.ImportFromEncryptedPem() for PEM encoded encrypted keys.
Otherwise with C#/BouncyCastle the import of an encrypted private PKCS#8 key is available e.g. with:
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.OpenSsl;
...
string encPkcs8 = #"-----BEGIN ENCRYPTED PRIVATE KEY-----
...
-----END ENCRYPTED PRIVATE KEY-----";
StringReader stringReader = new StringReader(encPkcs8);
PemReader pemReader = new PemReader(stringReader, new PasswordFinder("<your password>"));
RsaPrivateCrtKeyParameters keyParams = (RsaPrivateCrtKeyParameters)pemReader.ReadObject();
...
with the following implementation of the IPasswordFinder interface:
private class PasswordFinder : IPasswordFinder
{
private string password;
public PasswordFinder(string pwd) => password = pwd;
public char[] GetPassword() => password.ToCharArray();
}
If necessary, a conversion from keyParams to System.Security.Cryptography.RSAParameters is possible with:
using Org.BouncyCastle.Security;
...
RSAParameters rsaParameters = DotNetUtilities.ToRSAParameters(keyParams);
which can be imported directly e.g. with RSA.ImportParameters().
Edit:
After digging through the C#/BouncyCastle source code, your way is also possible (which I didn't know before).
DecryptKey() has several overloads, one of which can handle the DER encoded encrypted key as already suspected by Maarten Bodewes in his comment. The latter can be easily generated with the Org.BouncyCastle.Utilities.IO.Pem.PemReader() class.
Note that this PemReader is different from the one in the first implementation (different namespaces), which is why I use the namespace explicitly in the following code snippet. With this approach, the RsaPrivateCrtKeyParameters instance can be generated as follows:
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Crypto.Parameters;
...
StringReader stringReader = new StringReader(encPkcs8);
Org.BouncyCastle.Utilities.IO.Pem.PemReader pemReader = new Org.BouncyCastle.Utilities.IO.Pem.PemReader(stringReader);
Org.BouncyCastle.Utilities.IO.Pem.PemObject pem = pemReader.ReadPemObject();
RsaPrivateCrtKeyParameters keyParams = (RsaPrivateCrtKeyParameters)PrivateKeyFactory.DecryptKey("<your password>".ToCharArray(), pem.Content);
...
pem.Content contains the DER encoded encrypted private PKCS#8 key.
I only have a private key. i want to use it for signing data in RSA algorithm.
i know how to use certificate to sign data. but i don't any way to use private key in string format to create and use RSACryptoServiceProvider
My code looks something like this.
string privateKey = "-----BEGIN PRIVATE KEY-----\nsometext\nsometext\nsometext\.....\n-----END PRIVATE KEY-----\n"
var rasAlg = new RSACryptoServiceProvider()// how can i use my private key here.
var signature = rasAlg.SignData(bytesArray, new SHA256CryptoServiceProvider());
EDIT
if any one can help me create X509Certificate then also it is fine.
i found bouncy castle library useful. i used bouncy castle pem reader to read it from string.
AsymmetricKeyParameter keyPair;
using (var reader = new StringReader(privateKey))
keyPair = (AsymmetricKeyParameter)new PemReader(reader).ReadObject();
I am trying to create an online database application using PHP for the server and C# form application for the client.
On the server I encrypt a simple string using a public RSA key with the PHPSecLib. Then the C# application receives the string and tries to decrypt it using the corresponding private key.
The bytes are base64 encoded on the server and decoded to bytes again by C#. I created the key pair using the PHPSecLib.
This is the code I use on the client application:
public string rsa_decrypt(string encryptedText, string privateKey) {
byte[] bytesToDecrypt = Convert.FromBase64String(encryptedText);
Pkcs1Encoding decrypter = new Pkcs1Encoding(new RsaEngine());
//the error occurs on this line:
AsymmetricCipherKeyPair RSAParams = (AsymmetricCipherKeyPair)new PemReader(new StringReader(privateKey)).ReadObject();
decrypter.Init(false, RSAParams.Private);
byte[] decryptedBytes = decrypter.ProcessBlock(bytesToDecrypt, 0, bytesToDecrypt.Length);
string decryptedString = Convert.ToBase64String(decryptedBytes);
return decryptedString;
}
But, I get the following error on the line specified above^.
An unhandled exception of type 'System.IO.IOException' occurred in
BouncyCastle.Crypto.dll
Additional information: -----END RSA PRIVATE KEY not found
I believe there's nothing wrong with the key pair combo as I get an error before I even try to decrypt anything.
The privateKey parameter is currently hardcoded into the script using this format:
string privateKey = "-----BEGIN RSA PRIVATE KEY-----XXXXXXXX-----END RSA PRIVATE KEY-----";
So it seems to me the footer actually is included in the string... I have debugged and googled everywhere but I can't seem to solve it. I'm pretty new to RSA&Bouncycastle so maybe I'm just using wrong methods.
Hope you can help, thanks!
- G4A
P.S. This is my first Stackoverflow question, I just created an account, so if you could also give me some feedback on the way I formulated this question; great!
You need to add a new line between the pre/post encapsulation boundary text and the Base64 data, so:
string privateKey = "-----BEGIN RSA PRIVATE KEY-----\r\nXXX\r\n-----END RSA PRIVATE KEY-----";
This is because the pem specification allows for the existence of other textual headers between the two.
If this doesn't work
"-----BEGIN RSA PRIVATE KEY-----\r\nXXXXXXXX\r\n-----END RSA PRIVATE KEY-----"
please try this
"-----BEGIN RSA PRIVATE KEY-----
XXXXXXXX
-----END RSA PRIVATE KEY-----"
We converted the BOX Private Key to Base64 Format and stored the same in Azure Vault.
Convert key to Base64 using Base64Encode method, store in Azure Key Vault.
Retrieve the encoded string in code, decoded back using Base64Decode Method.
public static string Base64Encode(string plainText)
{
var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(plainText);
return System.Convert.ToBase64String(plainTextBytes);
}
public static string Base64Decode(string base64EncodedData)
{
var base64EncodedBytes = System.Convert.FromBase64String(base64EncodedData);
return System.Text.Encoding.UTF8.GetString(base64EncodedBytes);
}
I recommend use \x0A instead of \r\n and
.
Because only this option worked for me.
So :
"-----BEGIN RSA PRIVATE KEY-----\x0AXXXXXXXX\x0A-----END RSA PRIVATE KEY-----"
I only have a private key in a pem file and need to extract the public key from it.
At the moment I'm using openssl for this:
openssl rsa -pubout -outform DER
Ho can I do the same in c# (and Bouncycastle)?
In windows world file that contains private key is called PFX.
So you can try to import certificate to collection using .net System.Security.Cryptography infrastructure:
X509Certificate2Collection collection = new X509Certificate2Collection();
collection.Import(filePath, password, X509KeyStorageFlags.PersistKeySet);
The code block was taken from here:
How to retrieve certificates from a pfx file with c#?
If your .pem file already contains the public key it is usually between the Lines
-----BEGIN PUBLIC KEY-----
and
-----END PUBLIC KEY-----
of the pem file.
At least Openssl does it like this, but you should check it carefully.
See this link for an overview over the structure of a pem file.
So you could get it by using the Substring method. This should work:
static void ExtractPublicKeyFromPem(string path)
{
var startText = "-----BEGIN PUBLIC KEY-----";
var endText = "-----END PUBLIC KEY-----";
var pem = System.IO.File.ReadAllText(path);
var start = pem.LastIndexOf(startText);
var end = pem.IndexOf(endText);
var publicKey = pem.Substring(start, end - start);
}
Hope it helps :)