In C#, I'm retrieving an RSA public key from a HTTP request and it gives me the key encoded in base64.
WebClient webClient = new WebClient();
string rsaPublicKeyBase64 = webClient.DownloadString("http://localhost:8000/getkey");
// rsaPublicKeyBase64 = LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlHZk1BMEdDU3FHU0liM0RRRUJBUVVBQTRHTkFEQ0JpUUtCZ1FEdDAwcXQ2Zi9UUXdMQmVsVExRdVlXb05xSQoxbmRkcFpaOGh0WWs4d0NLbmFuRFJpWkJ1NVo5NnBNT01yNi84RS9JUzB0amV4WGdsVjh0WFlKK0NKc1lDUHhoCnBDUkduUW9rYkE2MnpOODVXNEROVUNMQ0cyMXlXcndscFhjSmxLYkY2dFhxdmd3TGRQb2RwZzUwY3RrWkI4R0UKbDBLS3VOV3JHZXRad045V0NRSURBUUFCCi0tLS0tRU5EIFBVQkxJQyBLRVktLS0tLQo=
I then decode the base 64 RSA public key.
byte[] rsaPublicKey = Convert.FromBase64String(rsaPublicKeyBase64);
/*
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDt00qt6f/TQwLBelTLQuYWoNqI
1nddpZZ8htYk8wCKnanDRiZBu5Z96pMOMr6/8E/IS0tjexXglV8tXYJ+CJsYCPxh
pCRGnQokbA62zN85W4DNUCLCG21yWrwlpXcJlKbF6tXqvgwLdPodpg50ctkZB8GE
l0KKuNWrGetZwN9WCQIDAQAB
-----END PUBLIC KEY-----
*/
My next step is to convert this byte[] containing my RSA public key certificate into type RSACryptoServiceProvider. I've found answers online, but none seem to work for me.
Here's what I currently have (which does not work).
string rsaPublicKeyFile = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
X509Certificate2 cert = null;
try {
File.WriteAllBytes(rsaPublicKeyFile, rsaPublicKey);
cert = new X509Certificate2(rsaPublicKeyFile);
} finally {
File.Delete(rsaPublicKeyFile);
}
I receive an unhandled exception error shown in the screenshot below.
System.Security.Cryptography.CryptographicException: 'Cannot find the requested object.
Thanks to #Crypt32, I managed to solve it by referencing the PublicKey Class documentation
I wrote a function GetCertificateFromBytes(byte[] cert) which writes to a temporary file in order to read the certificate:
public static X509Certificate2 GetCertificateFromBytes(byte[] cert) {
string certFile = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
try {
File.WriteAllBytes(certFile, cert);
X509Store store = new X509Store(StoreLocation.CurrentUser);
try {
store.Open(OpenFlags.ReadOnly);
X509Certificate2Collection certCollection = store.Certificates;
return certCollection[0];
} finally {
store.Close();
}
} finally {
File.Delete(certFile);
}
}
Then to Encrypt:
X509Certificate2 cert = GetCertificateFromBytes(rsaPublicKey);
RSACryptoServiceProvider publicKeyProvider = (RSACryptoServiceProvider)cert.PublicKey.Key;
byte[] encrypted = publicKeyProvider.Encrypt(data, false);
Related
public string SignSHA256RSA(string data,string privateKey)
{
using (var rsa = RSA.Create())
{
byte[] privateKeyBytes = Convert.FromBase64String(privateKey);
rsa.ImportPkcs8PrivateKey(privateKeyBytes, out _);
var dataToSign = Encoding.UTF8.GetBytes(data);
var signature = rsa.SignData(dataToSign, HashAlgorithmName.SHA256, RSASignaturePadding.Pss);
return Convert.ToBase64String(signature);
}
}
Using the above code, I attempted to generate an RSA signature using a certain private key. It runs smoothly locally, however when I publish it to a server, the following error is generated:
Internal.Cryptography.CryptoThrowHelper+WindowsCryptographicException: The system cannot find the file specified.
at System.Security.Cryptography.CngKeyLite.ImportKeyBlob(String blobType, ReadOnlySpan keyBlob, Boolean encrypted, ReadOnlySpan password)
at System.Security.Cryptography.CngPkcs8.ImportPkcs8(ReadOnlySpan keyBlob)
at System.Security.Cryptography.CngPkcs8.ImportPkcs8PrivateKey(ReadOnlySpan source, Int32& bytesRead)
at System.Security.Cryptography.RSAImplementation.RSACng.ImportPkcs8PrivateKey(ReadOnlySpan source, Int32& bytesRead)
I look for answers online, but I can't discover anything helpful.
and it works perfectly in production when I comment out rsa.ImportPkcs8PrivateKey(privateKeyBytes, out _)
public static string SignTool(string data, string privateKey)
{
using (var rsa = RSA.Create())
{
//byte[] privateKeyBytes = Convert.FromBase64String(privateKey);
rsa.ImportFromPem(Config.Config.private_key.ToCharArray());
/*
make sure that your private key is in the format of
-----BEGIN PRIVATE KEY-----
your private key goes here
-----END PRIVATE KEY-----
*/
var dataToSign = Encoding.UTF8.GetBytes(data);
var signature = rsa.SignData(dataToSign, HashAlgorithmName.SHA256, RSASignaturePadding.Pss);
return Convert.ToBase64String(signature);
}
}
I need to generate the RSA certificate(self signed certificate) with help of C# code. I have used the below code to create the certificate.
public bool CreateRSACertificate()
{
RSA rsaKey = RSA.Create();
CertificateRequest certRequest = new CertificateRequest("cn=MyApplication", rsaKey, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
certRequest.CertificateExtensions.Add(new X509BasicConstraintsExtension(true, false, 0, true));
certRequest.CertificateExtensions.Add(new X509SubjectKeyIdentifierExtension(certRequest.PublicKey, false));
X509Certificate2 certificate = certRequest.CreateSelfSigned(DateTimeOffset.Now, DateTimeOffset.Now.AddYears(5));
byte[] certData = certificate.Export(X509ContentType.Pfx, "TestPassword");
X509Certificate2 cert = new X509Certificate2(certData, "TestPassword", X509KeyStorageFlags.Exportable);
File.WriteAllBytes("MyCertificate.pfx", cert.Export(X509ContentType.Pfx, "TestPassword"));
return true;
}
And, after that I try to encrypt the data using the the certificate file which I created using the below code.
public bool EncryptAndDecryptFile()
{
string data = "{data: 'mydate123#gmail.com'}";
X509Certificate2 certificate = new X509Certificate2("MyCertificate.pfx", "TestPassword", X509KeyStorageFlags.Exportable);
if (certificate.HasPrivateKey) {
Console.WriteLine("Private key available "); // It's says like the private key was available
}
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
rsa.FromXmlString(certificate.PublicKey.Key.ToXmlString(false));
byte[] bytes = Encoding.ASCII.GetBytes(data);
var encryptedData = rsa.Encrypt(bytes, false); //It seems the data encrypted. I'm not sure.But, I can able to see some encrypted data.
using (certificate.GetRSAPrivateKey()) {
RSACryptoServiceProvider drsa = new RSACryptoServiceProvider();
drsa.FromXmlString(certificate.PrivateKey.ToXmlString(false));
var decdata = drsa.Decrypt(encryptedData, false); // Here, I see some exception.
someString = Encoding.ASCII.GetString(decdata);
}
Console.WriteLine("someString someString ::: " + someString);
return true;
}
While, running the above code I see the below error.
Unhandled exception. Internal.Cryptography.CryptoThrowHelper+WindowsCryptographicException: Key not valid for use in specified state.
at Internal.NativeCrypto.CapiHelper.ExportKeyBlob(Boolean includePrivateParameters, SafeKeyHandle safeKeyHandle)
at System.Security.Cryptography.RSACryptoServiceProvider.ExportParameters(Boolean includePrivateParameters)
at ConfigUtility.X509Certificate.ReadRSACertificate()
To verify the certificate I have the below command,
certutil -dump MyCertificate.pfx
So, the above utility gave me the below output,
================ Certificate 0 ================
================ Begin Nesting Level 1 ================
Element 0:
Serial Number: 054834637a713ecf
Issuer: CN=MyApplication
NotBefore: 29-05-2020 13:49
NotAfter: 29-05-2025 13:49
Subject: CN=MyApplication
Signature matches Public Key
Root Certificate: Subject matches Issuer
Cert Hash(sha1): 16e83e8a92a38b948adad03a86768e27115851d4
---------------- End Nesting Level 1 ----------------
Provider = Microsoft Software Key Storage Provider
Private key is NOT plain text exportable
Encryption test passed
CertUtil: -dump command completed successfully.
In fact, you get exception in this line:
drsa.FromXmlString(certificate.PrivateKey.ToXmlString(false));
the whole encryption and decryption code pieces are incorrect, you are messing things with old an deprecated RSACryptoServiceProvider and doing unnecessary operations. Here is how the method should look like:
public bool EncryptAndDecryptFile() {
string data = "{data: 'mydate123#gmail.com'}";
X509Certificate2 certificate = new X509Certificate2("MyCertificate.pfx", "TestPassword", X509KeyStorageFlags.Exportable);
if (certificate.HasPrivateKey) {
Console.WriteLine("Private key available "); // It's says like the private key was available
}
Byte[] encryptedData = new Byte[0];
using (RSA pubKey = certificate.GetRSAPublicKey()) {
byte[] bytes = Encoding.ASCII.GetBytes(data);
encryptedData = rsa.Encrypt(bytes, RSAEncryptionPadding.OaepSHA256);
}
// assuming, encryptedData is not null
String someString = String.Empty;
using (RSA prKey = certificate.GetRSAPrivateKey()) {
Byte[] decdata = prKey.Decrypt(encryptedData, RSAEncryptionPadding.OaepSHA256);
someString = Encoding.ASCII.GetString(decdata);
}
return data.Equals(someString);
}
I have an certificate which I can read using the X509Certificate2 class like this:
X509Certificate2 certificate = new X509Certificate2(#"certificate.pem");
But I also have an EC private key. This are it's file contents.
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIKpAuZ/Wwp7FTSCNJ56fFM4Y/rf8ltXp3xnrooPxNc1UoAoGCCqGSM49
AwEHoUQDQgAEqiRaEw3ItPsRAqdDjJCyqxhfm8y3tVrxLBAGhPM0pVhHuqmPoQFA
zR5FA3IJZaWcopieEX5uZ4KMtDhLFu/FHw==
-----END EC PRIVATE KEY-----
How do I 'feed' this private key to the certificate and eventually to my HttpClient so that it will become usable as a client certificate?
This is the rest of my code:
X509Certificate2 certificate = new X509Certificate2(#"certificate.pem");
//certificate.PrivateKey = something;
httpClientHandler.ClientCertificates.Clear();
httpClientHandler.ClientCertificates.Add(certificate);
httpClientHandler.SslProtocols = SslProtocols.Tls12;
httpClientHandler.ClientCertificateOptions = ClientCertificateOption.Manual;
HttpClient httpClient = new HttpClient(httpClientHandler);
HttpResponseMessage result = httpClient.GetAsync("https://server.cryptomix.com/secure/").Result;
string str = result.Content.ReadAsStringAsync().Result;
Combining the certificate and key with openssl, and feeding that into the x509certificate class works:
openssl pkcs12 -export -in certificate.pem -inkey privatekey.pem -out cert-and-key.pfx
And then using this to obtain a class with assigned private key:
X509Certificate2 certificate = new X509Certificate2("cert-and-key.pfx", "password");
And then the code in my question works.
I think I've got it... This uses the BouncyCastle NuGet package.
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.OpenSsl;
using Org.BouncyCastle.Pkcs;
using Org.BouncyCastle.Security;
using System.Security.Cryptography.X509Certificates;
using System;
using System.IO;
string pemKey = #"-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIKpAuZ/Wwp7FTSCNJ56fFM4Y/rf8ltXp3xnrooPxNc1UoAoGCCqGSM49
AwEHoUQDQgAEqiRaEw3ItPsRAqdDjJCyqxhfm8y3tVrxLBAGhPM0pVhHuqmPoQFA
zR5FA3IJZaWcopieEX5uZ4KMtDhLFu/FHw==
-----END EC PRIVATE KEY-----";
string pemCert = #"-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----";
var keyPair = (AsymmetricKeyParameter)new PemReader(new StringReader(pemKey)).ReadObject();
var cert = (Org.BouncyCastle.X509.X509Certificate)new PemReader(new StringReader(pemCert)).ReadObject();
var builder = new Pkcs12StoreBuilder();
builder.SetUseDerEncoding(true);
var store = builder.Build();
var certEntry = new X509CertificateEntry(cert);
store.SetCertificateEntry("", certEntry);
store.SetKeyEntry("", new AsymmetricKeyEntry(keyPair.Private), new[] { certEntry });
byte[] data;
using (var ms = new MemoryStream())
{
store.Save(ms, Array.Empty<char>(), new SecureRandom());
data = ms.ToArray();
}
var x509Cert = new X509Certificate2(data);
The trick seems to be to combine the cert and key together into a pkcs12 container, then feed that into X509Certificate2 in one go.
You can simply use the method X509Certificate2.CreateFromPemFile.
Here is the link to the Microsoft documentation!
var httpClientHandler = new HttpClientHandler();
var cert = X509Certificate2.CreateFromPemFile(certificateFile, privateKeyFile);
httpClientHandler.ClientCertificates.Add(cert);
var httpClient = new HttpClient(httpClientHandler);
HttpResponseMessage result = httpClient.GetAsync("https://server.cryptomix.com/secure/").Result;
string str = result.Content.ReadAsStringAsync().Result;
where certificateFile and privateKeyFile are file paths to your PEM files,
`I have imported the .cer certificate through IIS manager->Server Certificates-> import I want to extract the public key from a .cer certificate (link) and encrypt a string using the public key(RSA 2048 bit) extracted from the certificate,the method used should be RSA with ECB and PKCS1Padding
edit:added the below code
public static void SettingRSAParameters(){
X509Certificate cert = X509Certificate.CreateFromCertFile("D:\\cer.cer");
byte[] publicKey = cert.GetPublicKey();
int keyLength = publicKey.Length;
byte[] ExponentData = new byte[3];
byte[] ModulusData = new byte[256];
Array.Copy(publicKey, publicKey.Length - ExponentData.Length, ExponentData, 0, ExponentData.Length);
Array.Copy(publicKey, publicKey.Length - ExponentData.Length - 2 - ModulusData.Length, ModulusData, 0, ModulusData.Length);
ExponentData1 = ExponentData;
ModulusData1 = ModulusData;}
Extracting the modulus and exponent data in this fashion and plugging it in through the below code
static public byte[] Encryption(byte[] Data, RSAParameters RSAKey, bool DoOAEPPadding)
{
SettingRSAParameters();
RSAKey.Modulus = ModulusData1;
RSAKey.Exponent = ExponentData1;
try
{
byte[] encryptedData;
using (RSACryptoServiceProvider RSA = new RSACryptoServiceProvider(2048))
{
RSA.ImportParameters(RSAKey);
encryptedData = RSA.Encrypt(Data, DoOAEPPadding);
return encryptedData;
}
}
catch (CryptographicException e)
{
Console.WriteLine(e.Message);
return null;
}
}
The code doesn't encrypt properly please check if the code is correct and also suggest as where to add the ciphermode ECB
I've got an RSA private key in PEM format, is there a straight forward way to read that from .NET and instantiate an RSACryptoServiceProvider to decrypt data encrypted with the corresponding public key?
Update 03/03/2021
.NET 5 now supports this out of the box.
To try the code snippet below, generate a keypair and encrypt some text at http://travistidwell.com/jsencrypt/demo/
var privateKey = #"-----BEGIN RSA PRIVATE KEY-----
{ the full PEM private key }
-----END RSA PRIVATE KEY-----";
var rsa = RSA.Create();
rsa.ImportFromPem(privateKey.ToCharArray());
var decryptedBytes = rsa.Decrypt(
Convert.FromBase64String("{ base64-encoded encrypted string }"),
RSAEncryptionPadding.Pkcs1
);
// this will print the original unencrypted string
Console.WriteLine(Encoding.UTF8.GetString(decryptedBytes));
Original answer
I solved, thanks. In case anyone's interested, bouncycastle did the trick, just took me some time due to lack of knowledge from on my side and documentation. This is the code:
var bytesToDecrypt = Convert.FromBase64String("la0Cz.....D43g=="); // string to decrypt, base64 encoded
AsymmetricCipherKeyPair keyPair;
using (var reader = File.OpenText(#"c:\myprivatekey.pem")) // file containing RSA PKCS1 private key
keyPair = (AsymmetricCipherKeyPair) new PemReader(reader).ReadObject();
var decryptEngine = new Pkcs1Encoding(new RsaEngine());
decryptEngine.Init(false, keyPair.Private);
var decrypted = Encoding.UTF8.GetString(decryptEngine.ProcessBlock(bytesToDecrypt, 0, bytesToDecrypt.Length));
With respect to easily importing the RSA private key, without using 3rd party code such as BouncyCastle, I think the answer is "No, not with a PEM of the private key alone."
However, as alluded to above by Simone, you can simply combine the PEM of the private key (*.key) and the certificate file using that key (*.crt) into a *.pfx file which can then be easily imported.
To generate the PFX file from the command line:
openssl pkcs12 -in a.crt -inkey a.key -export -out a.pfx
Then use normally with the .NET certificate class such as:
using System.Security.Cryptography.X509Certificates;
X509Certificate2 combinedCertificate = new X509Certificate2(#"C:\path\to\file.pfx");
Now you can follow the example from MSDN for encrypting and decrypting via RSACryptoServiceProvider:
I left out that for decrypting you would need to import using the PFX password and the Exportable flag. (see: BouncyCastle RSAPrivateKey to .NET RSAPrivateKey)
X509KeyStorageFlags flags = X509KeyStorageFlags.Exportable;
X509Certificate2 cert = new X509Certificate2("my.pfx", "somepass", flags);
RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)cert.PrivateKey;
RSAParameters rsaParam = rsa.ExportParameters(true);
You might take a look at JavaScience's source for OpenSSLKey
There's code in there that does exactly what you want to do.
In fact, they have a lot of crypto source code available here.
Source code snippet:
//------- Parses binary ans.1 RSA private key; returns RSACryptoServiceProvider ---
public static RSACryptoServiceProvider DecodeRSAPrivateKey(byte[] privkey)
{
byte[] MODULUS, E, D, P, Q, DP, DQ, IQ ;
// --------- Set up stream to decode the asn.1 encoded RSA private key ------
MemoryStream mem = new MemoryStream(privkey) ;
BinaryReader binr = new BinaryReader(mem) ; //wrap Memory Stream with BinaryReader for easy reading
byte bt = 0;
ushort twobytes = 0;
int elems = 0;
try {
twobytes = binr.ReadUInt16();
if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81)
binr.ReadByte(); //advance 1 byte
else if (twobytes == 0x8230)
binr.ReadInt16(); //advance 2 bytes
else
return null;
twobytes = binr.ReadUInt16();
if (twobytes != 0x0102) //version number
return null;
bt = binr.ReadByte();
if (bt !=0x00)
return null;
//------ all private key components are Integer sequences ----
elems = GetIntegerSize(binr);
MODULUS = binr.ReadBytes(elems);
elems = GetIntegerSize(binr);
E = binr.ReadBytes(elems) ;
elems = GetIntegerSize(binr);
D = binr.ReadBytes(elems) ;
elems = GetIntegerSize(binr);
P = binr.ReadBytes(elems) ;
elems = GetIntegerSize(binr);
Q = binr.ReadBytes(elems) ;
elems = GetIntegerSize(binr);
DP = binr.ReadBytes(elems) ;
elems = GetIntegerSize(binr);
DQ = binr.ReadBytes(elems) ;
elems = GetIntegerSize(binr);
IQ = binr.ReadBytes(elems) ;
Console.WriteLine("showing components ..");
if (verbose) {
showBytes("\nModulus", MODULUS) ;
showBytes("\nExponent", E);
showBytes("\nD", D);
showBytes("\nP", P);
showBytes("\nQ", Q);
showBytes("\nDP", DP);
showBytes("\nDQ", DQ);
showBytes("\nIQ", IQ);
}
// ------- create RSACryptoServiceProvider instance and initialize with public key -----
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
RSAParameters RSAparams = new RSAParameters();
RSAparams.Modulus =MODULUS;
RSAparams.Exponent = E;
RSAparams.D = D;
RSAparams.P = P;
RSAparams.Q = Q;
RSAparams.DP = DP;
RSAparams.DQ = DQ;
RSAparams.InverseQ = IQ;
RSA.ImportParameters(RSAparams);
return RSA;
}
catch (Exception) {
return null;
}
finally {
binr.Close();
}
}
The stuff between the
-----BEGIN RSA PRIVATE KEY----
and
-----END RSA PRIVATE KEY-----
is the base64 encoding of a PKCS#8 PrivateKeyInfo (unless it says RSA ENCRYPTED PRIVATE KEY in which case it is a EncryptedPrivateKeyInfo).
It is not that hard to decode manually, but otherwise your best bet is to P/Invoke to CryptImportPKCS8.
Update: The CryptImportPKCS8 function is no longer available for use as of Windows Server 2008 and Windows Vista. Instead, use the PFXImportCertStore function.
ok, Im using mac to generate my self signed keys. Here is the working method I used.
I created a shell script to speed up my key generation.
genkey.sh
#/bin/sh
ssh-keygen -f host.key
openssl req -new -key host.key -out request.csr
openssl x509 -req -days 99999 -in request.csr -signkey host.key -out server.crt
openssl pkcs12 -export -inkey host.key -in server.crt -out private_public.p12 -name "SslCert"
openssl base64 -in private_public.p12 -out Base64.key
add the +x execute flag to the script
chmod +x genkey.sh
then call genkey.sh
./genkey.sh
I enter a password (important to include a password at least for the export at the end)
Enter pass phrase for host.key:
Enter Export Password: {Important to enter a password here}
Verifying - Enter Export Password: { Same password here }
I then take everything in Base64.Key and put it into a string named sslKey
private string sslKey = "MIIJiAIBA...................................." +
"......................ETC...................." +
"......................ETC...................." +
"......................ETC...................." +
".............ugICCAA=";
I then used a lazy load Property getter to get my X509 Cert with a private key.
X509Certificate2 _serverCertificate = null;
X509Certificate2 serverCertificate{
get
{
if (_serverCertificate == null){
string pass = "Your Export Password Here";
_serverCertificate = new X509Certificate(Convert.FromBase64String(sslKey), pass, X509KeyStorageFlags.Exportable);
}
return _serverCertificate;
}
}
I wanted to go this route because I am using .net 2.0 and Mono on mac and I wanted to use vanilla Framework code with no compiled libraries or dependencies.
My final use for this was the SslStream to secure TCP communication to my app
SslStream sslStream = new SslStream(serverCertificate, false, SslProtocols.Tls, true);
I hope this helps other people.
NOTE
Without a password I was unable to correctly unlock the private key for export.
I've tried the accepted answer for PEM-encoded PKCS#8 RSA private key and it resulted in PemException with malformed sequence in RSA private key message. The reason is that Org.BouncyCastle.OpenSsl.PemReader seems to only support PKCS#1 private keys.
I was able to get the private key by switching to Org.BouncyCastle.Utilities.IO.Pem.PemReader (note that type names match!) like this
private static RSAParameters GetRsaParameters(string rsaPrivateKey)
{
var byteArray = Encoding.ASCII.GetBytes(rsaPrivateKey);
using (var ms = new MemoryStream(byteArray))
{
using (var sr = new StreamReader(ms))
{
var pemReader = new Org.BouncyCastle.Utilities.IO.Pem.PemReader(sr);
var pem = pemReader.ReadPemObject();
var privateKey = PrivateKeyFactory.CreateKey(pem.Content);
return DotNetUtilities.ToRSAParameters(privateKey as RsaPrivateCrtKeyParameters);
}
}
}
I've created the PemUtils library that does exactly that. The code is available on GitHub and can be installed from NuGet:
PM> Install-Package PemUtils
or if you only want a DER converter:
PM> Install-Package DerConverter
Usage for reading a RSA key from PEM data:
using (var stream = File.OpenRead(path))
using (var reader = new PemReader(stream))
{
var rsaParameters = reader.ReadRsaKey();
// ...
}
For people who don't want to use Bouncy, and are trying some of the code included in other answers, I've found that the code works MOST of the time, but trips up on some RSA private strings, such as the one I've included below. By looking at the bouncy code, I tweaked the code provided by wprl to
RSAparams.D = ConvertRSAParametersField(D, MODULUS.Length);
RSAparams.DP = ConvertRSAParametersField(DP, P.Length);
RSAparams.DQ = ConvertRSAParametersField(DQ, Q.Length);
RSAparams.InverseQ = ConvertRSAParametersField(IQ, Q.Length);
private static byte[] ConvertRSAParametersField(byte[] bs, int size)
{
if (bs.Length == size)
return bs;
if (bs.Length > size)
throw new ArgumentException("Specified size too small", "size");
byte[] padded = new byte[size];
Array.Copy(bs, 0, padded, size - bs.Length, bs.Length);
return padded;
}
-----BEGIN RSA PRIVATE KEY-----
MIIEoQIBAAKCAQEAxCgWAYJtfKBVa6Px1Blrj+3Wq7LVXDzx+MiQFrLCHnou2Fvb
fxuDeRmd6ERhDWnsY6dxxm981vTlXukvYKpIZQYpiSzL5pyUutoi3yh0+/dVlsHZ
UHheVGZjSMgUagUCLX1p/augXltAjgblUsj8GFBoKJBr3TMKuR5TwF7lBNYZlaiR
k9MDZTROk6MBGiHEgD5RaPKA/ot02j3CnSGbGNNubN2tyXXAgk8/wBmZ4avT0U4y
5oiO9iwCF/Hj9gK/S/8Q2lRsSppgUSsCioSg1CpdleYzIlCB0li1T0flB51zRIpg
JhWRfmK1uTLklU33xfzR8zO2kkfaXoPTHSdOGQIDAQABAoIBAAkhfzoSwttKRgT8
sgUYKdRJU0oqyO5s59aXf3LkX0+L4HexzvCGbK2hGPihi42poJdYSV4zUlxZ31N2
XKjjRFDE41S/Vmklthv8i3hX1G+Q09XGBZekAsAVrrQfRtP957FhD83/GeKf3MwV
Bhe/GKezwSV3k43NvRy2N1p9EFa+i7eq1e5i7MyDxgKmja5YgADHb8izGLx8Smdd
+v8EhWkFOcaPnQRj/LhSi30v/CjYh9MkxHMdi0pHMMCXleiUK0Du6tnsB8ewoHR3
oBzL4F5WKyNHPvesYplgTlpMiT0uUuN8+9Pq6qsdUiXs0wdFYbs693mUMekLQ4a+
1FOWvQECgYEA7R+uI1r4oP82sTCOCPqPi+fXMTIOGkN0x/1vyMXUVvTH5zbwPp9E
0lG6XmJ95alMRhjvFGMiCONQiSNOQ9Pec5TZfVn3M/w7QTMZ6QcWd6mjghc+dGGE
URmCx8xaJb847vACir7M08AhPEt+s2C7ZokafPCoGe0qw/OD1fLt3NMCgYEA08WK
S+G7dbCvFMrBP8SlmrnK4f5CRE3pV4VGneWp/EqJgNnWwaBCvUTIegDlqS955yVp
q7nVpolAJCmlUVmwDt4gHJsWXSQLMXy3pwQ25vdnoPe97y3xXsi0KQqEuRjD1vmw
K7SXoQqQeSf4z74pFal4CP38U3pivvoE4MQmJeMCfyJFceWqQEUEneL+IYkqrZSK
7Y8urNse5MIC3yUlcose1cWVKyPh4RCEv2rk0U1gKqX29Jb9vO2L7RflAmrLNFuA
J+72EcRxsB68RAJqA9VHr1oeAejQL0+JYF2AK4dJG/FsvvFOokv4eNU+FBHY6Tzo
k+t63NDidkvb5jIF6lsCgYEAlnQ08f5Y8Z9qdCosq8JpKYkwM+kxaVe1HUIJzqpZ
X24RTOL3aa8TW2afy9YRVGbvg6IX9jJcMSo30Llpw2cl5xo21Dv24ot2DF2gGN+s
peFF1Z3Naj1Iy99p5/KaIusOUBAq8pImW/qmc/1LD0T56XLyXekcuK4ts6Lrjkit
FaMCgYAusOLTsRgKdgdDNI8nMQB9iSliwHAG1TqzB56S11pl+fdv9Mkbo8vrx6g0
NM4DluCGNEqLZb3IkasXXdok9e8kmX1en1lb5GjyPbc/zFda6eZrwIqMX9Y68eNR
IWDUM3ckwpw3rcuFXjFfa+w44JZVIsgdoGHiXAdrhtlG/i98Rw==
-----END RSA PRIVATE KEY-----
Check http://msdn.microsoft.com/en-us/library/dd203099.aspx
under Cryptography Application Block.
Don't know if you will get your answer, but it's worth a try.
Edit after Comment.
Ok then check this code.
using System.Security.Cryptography;
public static string DecryptEncryptedData(stringBase64EncryptedData, stringPathToPrivateKeyFile) {
X509Certificate2 myCertificate;
try{
myCertificate = new X509Certificate2(PathToPrivateKeyFile);
} catch{
throw new CryptographicException("Unable to open key file.");
}
RSACryptoServiceProvider rsaObj;
if(myCertificate.HasPrivateKey) {
rsaObj = (RSACryptoServiceProvider)myCertificate.PrivateKey;
} else
throw new CryptographicException("Private key not contained within certificate.");
if(rsaObj == null)
return String.Empty;
byte[] decryptedBytes;
try{
decryptedBytes = rsaObj.Decrypt(Convert.FromBase64String(Base64EncryptedData), false);
} catch {
throw new CryptographicException("Unable to decrypt data.");
}
// Check to make sure we decrpyted the string
if(decryptedBytes.Length == 0)
return String.Empty;
else
return System.Text.Encoding.UTF8.GetString(decryptedBytes);
}