I have public key in following format:
-----BEGIN RSA PUBLIC KEY-----MIIBCgKCAQEA1pjRK+cOnEsh5L1wrt1Tmx+FvyNRj4wuAotlqIWtHS8pIqRzsIOJg+tlbUtEXYju+KOcIohZnnmfj0cq28RcQ19HIohqUXypmUbNy9np/M+Aj9NcyKIaNyuEZ+UhcU/OIStHK4eTUJt+RWL+0Q1/rl49tg7h+toB9Y6Y3SeytXvZhMx3N5qqmHHNorpfb65bvvHsicGDB7vK5kn55o+C2LhRIxfnw87nKnhPBMRbZg+BKCTXD4svz4a2xiR/uM4rxebIBgB3Sm4X1w3yoRr0F3IDhAgXmEhSZ078wm3ohPfuuwymfVhvdzavm42NwzixZ7n52SowFE2v0DSJh3IbPwIDAQAB-----END RSA PUBLIC KEY-----
How can I implement C# to verify my JWT using that string? I have found similar topic at Verifying JWT signed with the RS256 algorithm using public key in C# but none of solutions suits my case.
There is my code:
public static bool TransformKey(string fullKey)
{
try
{
var publicKey = fullKey.Replace("-----BEGIN RSA PUBLIC KEY-----", "");
publicKey = publicKey.Replace("-----END RSA PUBLIC KEY-----", "");
publicKey = publicKey.Replace("\n", "");
var keyBytes = Convert.FromBase64String(publicKey); // your key here
AsymmetricKeyParameter asymmetricKeyParameter = PublicKeyFactory.CreateKey(keyBytes);
RsaKeyParameters rsaKeyParameters = (RsaKeyParameters)asymmetricKeyParameter;
RSAParameters rsaParameters = new RSAParameters
{
Modulus = rsaKeyParameters.Modulus.ToByteArrayUnsigned(),
Exponent = rsaKeyParameters.Exponent.ToByteArrayUnsigned()
};
return true;
}
catch (Exception e)
{
Console.WriteLine(e);
return false;
}
}
static void Main(string[] args)
{
// The code provided will print ‘Hello World’ to the console.
// Press Ctrl+F5 (or go to Debug > Start Without Debugging) to run your app.
string key = "-----BEGIN RSA PUBLIC KEY-----MIIBCgKCAQEA1pjRK+cOnEsh5L1wrt1Tmx+FvyNRj4wuAotlqIWtHS8pIqRzsIOJg+tlbUtEXYju+KOcIohZnnmfj0cq28RcQ19HIohqUXypmUbNy9np/M+Aj9NcyKIaNyuEZ+UhcU/OIStHK4eTUJt+RWL+0Q1/rl49tg7h+toB9Y6Y3SeytXvZhMx3N5qqmHHNorpfb65bvvHsicGDB7vK5kn55o+C2LhRIxfnw87nKnhPBMRbZg+BKCTXD4svz4a2xiR/uM4rxebIBgB3Sm4X1w3yoRr0F3IDhAgXmEhSZ078wm3ohPfuuwymfVhvdzavm42NwzixZ7n52SowFE2v0DSJh3IbPwIDAQAB-----END RSA PUBLIC KEY-----";
bool test = TransformKey(key);
Console.ReadKey();
}
It returns exception when I try to initialize asymmetricKeyParameter object:
System.ArgumentException: Unknown object in GetInstance: Org.BouncyCastle.Asn1.DerInteger
Parameter name: obj
w Org.BouncyCastle.Asn1.Asn1Sequence.GetInstance(Object obj)
w Org.BouncyCastle.Asn1.X509.AlgorithmIdentifier.GetInstance(Object obj)
w Org.BouncyCastle.Asn1.X509.SubjectPublicKeyInfo..ctor(Asn1Sequence seq)
w Org.BouncyCastle.Asn1.X509.SubjectPublicKeyInfo.GetInstance(Object obj)
w Org.BouncyCastle.Security.PublicKeyFactory.CreateKey(Byte[] keyInfoData)
Code snippet with Modulus and Exponent in string type that can be useful:
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
rsa.ImportParameters(
new RSAParameters()
{
Modulus = FromBase64Url("w7Zdfmece8iaB0kiTY8pCtiBtzbptJmP28nSWwtdjRu0f2GFpajvWE4VhfJAjEsOcwYzay7XGN0b-X84BfC8hmCTOj2b2eHT7NsZegFPKRUQzJ9wW8ipn_aDJWMGDuB1XyqT1E7DYqjUCEOD1b4FLpy_xPn6oV_TYOfQ9fZdbE5HGxJUzekuGcOKqOQ8M7wfYHhHHLxGpQVgL0apWuP2gDDOdTtpuld4D2LK1MZK99s9gaSjRHE8JDb1Z4IGhEcEyzkxswVdPndUWzfvWBBWXWxtSUvQGBRkuy1BHOa4sP6FKjWEeeF7gm7UMs2Nm2QUgNZw6xvEDGaLk4KASdIxRQ"),
Exponent = FromBase64Url("AQAB")
});
In the case of the .NET Framework (e.g. 4.7.2), a public PKCS#1 key can be imported for example using BouncyCastle (e.g. v1.8.6.1):
using System.IO;
using System.Security.Cryptography;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.OpenSsl;
using Org.BouncyCastle.Security;
...
string publicPKCS1 = #"-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEA1pjRK+cOnEsh5L1wrt1Tmx+FvyNRj4wuAotlqIWtHS8pIqRzsIOJ
g+tlbUtEXYju+KOcIohZnnmfj0cq28RcQ19HIohqUXypmUbNy9np/M+Aj9NcyKIa
NyuEZ+UhcU/OIStHK4eTUJt+RWL+0Q1/rl49tg7h+toB9Y6Y3SeytXvZhMx3N5qq
mHHNorpfb65bvvHsicGDB7vK5kn55o+C2LhRIxfnw87nKnhPBMRbZg+BKCTXD4sv
z4a2xiR/uM4rxebIBgB3Sm4X1w3yoRr0F3IDhAgXmEhSZ078wm3ohPfuuwymfVhv
dzavm42NwzixZ7n52SowFE2v0DSJh3IbPwIDAQAB
-----END RSA PUBLIC KEY-----";
PemReader pemReader = new PemReader(new StringReader(publicPKCS1));
AsymmetricKeyParameter asymmetricKeyParameter = (AsymmetricKeyParameter)pemReader.ReadObject();
RSAParameters rsaParameters = DotNetUtilities.ToRSAParameters((RsaKeyParameters)asymmetricKeyParameter);
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
rsa.ImportParameters(rsaParameters);
...
The validation of the JWT can be done with a JWT library (e.g. System.IdentityModel.Tokens.Jwt) or with a pure cryptography API. Both are described in detail in the answers to the linked question. Regarding the former, see also here, 2nd paragraph.
I'm using self signed certificate ECDH_secP384r1 for signing token. Here is the PowerShell that I create the certificate:
$Cert = New-SelfSignedCertificate -certstorelocation cert:\localmachine\my -dnsname $Certname -NotAfter $ExpireDate -KeyAlgorithm ECDH_secP384r1
Now in my .net core application first I load the certificate:
private readonly string _certificateSubjectName;
public X509Certificate2 GetSigningCertificate()
{
using (var store = new X509Store(StoreLocation.LocalMachine))
{
store.Open(OpenFlags.ReadOnly);
var certificates = store.Certificates.Find(X509FindType.FindBySubjectName, _certificateSubjectName, false);
return certificates[0];
}
}
And also I can Get the ECDsa private key like
ECDsa privateKey = signingCertificate.GetECDsaPrivateKey();
ECDsa publicKey = signingCertificate.GetECDsaPublicKey()
But how could I have byte array of these keys?
For Rsa I could use:
public byte[] GetPrivateKey(X509Certificate2 certificate)
{
RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)certificate.PrivateKey;
MemoryStream memoryStream = new MemoryStream();
TextWriter streamWriter = new StreamWriter(memoryStream);
PemWriter pemWriter = new PemWriter(streamWriter);
AsymmetricCipherKeyPair keyPair = DotNetUtilities.GetRsaKeyPair(rsa);
pemWriter.WriteObject(keyPair.Private);
streamWriter.Flush();
byte[] byteArray = memoryStream.GetBuffer();
return byteArray;
}
But how about ECDsa? any idea?
I wrote two methods for signing using RSA and SHA256, the first one with OpenSSL library and the second one with Microsoft Cryptography library.
OpenSSL implementation:
private string PasswordHandler(bool verify, object userdata)
{
return userdata.ToString();
}
private string Sign(string signParams)
{
var privateCertPath = HttpContext.Current.Server.MapPath(#"~\certificate.pem");
string privateKey;
using (StreamReader sr = new StreamReader(privateCertPath))
{
privateKey = sr.ReadToEnd();
}
OpenSSL.Crypto.RSA rsa = OpenSSL.Crypto.RSA.FromPrivateKey(new BIO(privateKey), PasswordHandler, _password);
//hash method
MessageDigest md = MessageDigest.SHA1;
BIO b = new BIO(signParams);
CryptoKey ck = new CryptoKey(rsa);
byte[] res1 = MessageDigestContext.Sign(md, b, ck);
return Uri.EscapeDataString(System.Convert.ToBase64String(res1));
}
Cryptography implementation:
private string Sign(string data)
{
var privateCertPath = HttpContext.Current.Server.MapPath(#"~\certificate.pfx");
X509Certificate2 privateCert = new X509Certificate2(privateCertPath, _password, X509KeyStorageFlags.Exportable);
RSACryptoServiceProvider privateKey = (RSACryptoServiceProvider)privateCert.PrivateKey;
RSACryptoServiceProvider privateKey1 = new RSACryptoServiceProvider();
privateKey1.ImportParameters(privateKey.ExportParameters(true));
// Get the bytes to be signed from the string
var bytes = System.Text.Encoding.UTF8.GetBytes(data);
//const string sha256Oid = "2.16.840.1.101.3.4.2.1";
//HashAlgorithm algorithm = new SHA256CryptoServiceProvider();
//byte[] hashBytes = algorithm.ComputeHash(bytes);
//byte[] signature = privateKey1.SignHash(hashBytes, sha256Oid);
byte[] signature = privateKey1.SignData(bytes, "SHA256");
// Base 64 encode the sig so its 8-bit clean
return Convert.ToBase64String(signature);
}
Signing with OpenSSL works, generates valid digital signature but signing with Cryptography lib generates invalid signature so my question is what I implemented wrong?
I tried to use different encoding but it did not help. Certificates are generated correctly.
It might by also useful to tell basic info about the .pem certificate:
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: DES-EDE3-CBC
I'm working on some code that creates a X509certificate and a public/private key pair. The public key is added to the certificate and it is sent to an CA which signs it.
The returned certificate is then accessed through the System.Security.Cryptography.X509Certificates.X509Certificate2 class. Now I want to use this certificate to initiate a secure connection with other clients. Therefore I use the SslStream class. To start the SSL Handshake I use this method:
server.AssociatedSslStream.AuthenticateAsServer(
MyCertificate, // Client Certificate
true, // Require Certificate from connecting Peer
SslProtocols.Tls, // Use TLS 1.0
false // check Certificate revocation
);
This method requires that the private key is associated with the certificate. Of course the certificate returned by the CA does not contain a private key. But it is stored as .key file on the harddrive. The X509Certificate2 class has a property called PrivateKey which I guess will associate a private key with the certificate, but I can't find a way to set this property.
Is there any way I can associate the private key with the .net X509 class?
You can save yourself the hassle of copy-pasting all that code and store the private key next to the certificate in a pfx/pkcs#12 file:
openssl pkcs12 -export -in my.cer -inkey my.key -out mycert.pfx
You'll have to supply a password, which you have to pass to the constructor of X509Certificate2:
X509Certificate2 cert = new X509Certificate2("mycert.pfx","password");
For everyone else with the same problem, I found a neat little piece of code that let's you do exactly that:
http://www.codeproject.com/Articles/162194/Certificates-to-DB-and-Back
byte[] certBuffer = Helpers.GetBytesFromPEM(publicCert, PemStringType.Certificate);
byte[] keyBuffer = Helpers.GetBytesFromPEM(privateKey, PemStringType.RsaPrivateKey);
X509Certificate2 certificate = new X509Certificate2(certBuffer, password);
RSACryptoServiceProvider prov = Crypto.DecodeRsaPrivateKey(keyBuffer);
certificate.PrivateKey = prov;
EDIT: The code for the Helper method (which otherwise requires a codeproject login) is as follows:
public static byte[] GetBytesFromPEM(string pemString, PemStringType type)
{
string header; string footer;
switch (type)
{
case PemStringType.Certificate:
header = "-----BEGIN CERTIFICATE-----";
footer = "-----END CERTIFICATE-----";
break;
case PemStringType.RsaPrivateKey:
header = "-----BEGIN RSA PRIVATE KEY-----";
footer = "-----END RSA PRIVATE KEY-----";
break;
default:
return null;
}
int start = pemString.IndexOf(header) + header.Length;
int end = pemString.IndexOf(footer, start) - start;
return Convert.FromBase64String(pemString.Substring(start, end));
}
Update
As of .NET 5 you can simply use CreateFromPem(ReadOnlySpan, ReadOnlySpan):
Creates a new X509 certificate from the contents of an RFC 7468
PEM-encoded certificate and private key.
example:
X509Certificate2 cert = X509Certificate2.CreateFromPem(
certPem, //The text of the PEM-encoded X509 certificate.
keyPem //The text of the PEM-encoded private key.
);
Or if you have a string with both the cert and its private key, you can pass it in for both the cert arg and the key arg:
X509Certificate2 cert = X509Certificate2.CreateFromPem(
certPem, //The text of the PEM-encoded X509 certificate.
certPem// The text of the PEM-encoded private key.
);
my solution
byte[] PublicCertificate = Encoding.Unicode.GetBytes("-----BEGIN CERTIFICATE----- ... -----END CERTIFICATE-----");
var publicCertificate = new X509Certificate2(PublicCertificate );
byte[] PrivateKey = Convert.FromBase64String("MIIEvQIBA...=");
using var rsa = RSA.Create();
rsa.ImportPkcs8PrivateKey(PrivateKey, out _);
publicCertificate = publicCertificate.CopyWithPrivateKey(rsa);
publicCertificate = new X509Certificate2(publicCertificate.Export(X509ContentType.Pkcs12));
var client = new RestClient("api_url");
client.ClientCertificates = new X509Certificate2Collection();
client.ClientCertificates.Add(publicCertificate);
.NET 5+
X509Certificate2.CreateFromPemFile(certPath, keyPath);
https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.x509certificates.x509certificate2.createfrompemfile?view=net-5.0#System_Security_Cryptography_X509Certificates_X509Certificate2_CreateFromPemFile_System_String_System_String_
for .NET Framework 4.8
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.OpenSsl;
using Org.BouncyCastle.Security;
...
private X509Certificate2 GetCert(string clientCertFile, string privateKeyFile) {
var tempCertificate = new X509Certificate2(clientCertFile);
StreamReader reader = new StreamReader(privateKeyFile);
PemReader pemReader = new PemReader(reader);
AsymmetricCipherKeyPair keyPair = (AsymmetricCipherKeyPair)pemReader.ReadObject();
AsymmetricKeyParameter privateKey = keyPair.Private;
RSA rsa = DotNetUtilities.ToRSA((RsaPrivateCrtKeyParameters) privateKey);
tempCertificate = tempCertificate.CopyWithPrivateKey(rsa);
return new X509Certificate2(tempCertificate.Export(X509ContentType.Pkcs12));
}
My .net 45 solution thanks to Lee Taylor and Neano
using Org.BouncyCastle.OpenSsl;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Crypto.Parameters;
using System.IO;
using System.Security.Cryptography.X509Certificates;
private X509Certificate2 GetCert(string certPath,string keyPath)
{
X509Certificate2 cert = new X509Certificate2(certPath);
StreamReader reader = new StreamReader(keyPath);
PemReader pemReader = new PemReader(reader);
RsaPrivateCrtKeyParameters keyPair=(RsaPrivateCrtKeyParameters)pemReader.ReadObject();
RSA rsa = DotNetUtilities.ToRSA(keyPair);
cert.PrivateKey = rsa;
return new X509Certificate2(cert.Export(X509ContentType.Pfx));
}
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);
}