I want functions for import/export PEM/DER strings in C#. I'm using RSA on python and C# but i had no solution who worked fine beetween both language.
After many many tries and many research, i finally found an solution who working for read PEM public key. The following code works :
// pubkey is the PEM file without header/footer and encoded base64
Asn1Object obj = Asn1Object.FromByteArray(pubkey_bytes);
DerSequence publicKeySequence = (DerSequence)obj;
DerSequence publicKey = (DerSequence)Asn1Object.FromByteArray(publicKeySequence.GetEncoded());
DerInteger modulus = (DerInteger)publicKey[0];
DerInteger exponent = (DerInteger)publicKey[1];
RsaKeyParameters keyParameters = new RsaKeyParameters(false, modulus.PositiveValue, exponent.PositiveValue);
RSAParameters parameters = DotNetUtilities.ToRSAParameters(keyParameters);
this code is inspired of a solution i found on StackOverflow. But when i'm trying to use the same solution for private key i have an exception "Spécified size too small". This error seems appears on the "InverseQ" line. Below the code i'm using :
// Private key encode base64 without header/footer
Asn1Object obj = Asn1Object.FromByteArray(privkey_bytes);
DerSequence privateKeySequence = (DerSequence)obj;
DerSequence privateKey = (DerSequence)Asn1Object.FromByteArray(privateKeySequence.GetEncoded());
DerInteger M = (DerInteger)privateKey[1];
DerInteger E = (DerInteger)privateKey[2];
DerInteger D = (DerInteger)privateKey[3];
DerInteger P = (DerInteger)privateKey[4];
DerInteger Q = (DerInteger)privateKey[5];
DerInteger DP = (DerInteger)privateKey[6];
DerInteger DQ = (DerInteger)privateKey[7];
DerInteger IQ = (DerInteger)privateKey[8];
var keyParameters = new RsaPrivateCrtKeyParameters(M.PositiveValue,
E.PositiveValue, D.PositiveValue, P.PositiveValue, Q.PositiveValue,
DP.PositiveValue, DQ.PositiveValue, IQ.PositiveValue);
RSAParameters parameters = DotNetUtilities.ToRSAParameters(keyParameters); // Exception here !
I tried also this code from another StackOverflow post, for fix padding issues, but it's not my problem here :
RSAParameters parameters = new RSAParameters();
parameters.Modulus = M.PositiveValue.ToByteArrayUnsigned();
parameters.Exponent = E.PositiveValue.ToByteArrayUnsigned();
parameters.P = P.PositiveValue.ToByteArrayUnsigned();
parameters.Q = Q.PositiveValue.ToByteArrayUnsigned();Modulus.Length);
parameters.DP = ConvertRSAParametersField(DP.PositiveValue, parameters.P.Length);
parameters.DQ = ConvertRSAParametersField(DQ.PositiveValue, parameters.Q.Length);
parameters.InverseQ = ConvertRSAParametersField(IQ.PositiveValue, parameters.Q.Length);
public static RSAParameters ToRSAParameters(RsaPrivateCrtKeyParameters privKey)
{
RSAParameters rp = new RSAParameters();
rp.Modulus = privKey.Modulus.ToByteArrayUnsigned();
rp.Exponent = privKey.PublicExponent.ToByteArrayUnsigned();
rp.P = privKey.P.ToByteArrayUnsigned();
rp.Q = privKey.Q.ToByteArrayUnsigned();
rp.D = ConvertRSAParametersField(privKey.Exponent, rp.Modulus.Length);
rp.DP = ConvertRSAParametersField(privKey.DP, rp.P.Length);
rp.DQ = ConvertRSAParametersField(privKey.DQ, rp.Q.Length);
rp.InverseQ = ConvertRSAParametersField(privKey.QInv, rp.Q.Length); // Exception HERE
return rp;
}
private static byte[] ConvertRSAParametersField(BigInteger n, int size)
{
byte[] bs = n.ToByteArrayUnsigned();
if (bs.Length == size)
return bs;
if (bs.Length > size) // HERE
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;
}
My private key is generated by python and i think it a normal key :
-----BEGIN RSA PRIVATE KEY-----
MIICYAIBAAKBgQCUkUk/vvyTI3begsdjz7eQVchjMTYMTyqt8HHHSXYXY2GrQCZS
VfBVxFLmx19brgMdLrKO93CjJWt+ACXAaLHmuGCsvNgGjCFqgg4XxM4Xamt9PeOc
F8HhMH7iS3OcgybQuu9GrekZB0yL2L9GVpyVrXpVlVHi4gBVjUr80e5LUwIDAQAB
AoGAGgK9wk1bxx8EZrya0BzD1J9QMB2jitApdr6MDQoNhNa/eM4IZ43oP/vZT9JE
Hbb/kJJmbKVhsQ6SHUNFO6qJA0FWYNqEA1xsTtat3RXAB/WCExxW9GwDG6pEXjK8
OrJFbKyIOhmqy3sBib9V76ROYsMi7Gioih8vtYKz8NFBiZECRQCmOHzCqEbsWJnK
5JUsUG8DyR3wg8mzi98m1qYGz3hsuBJMCW2M/QBSgyWGdDxExbQMcTb9lmTelq4W
sHbZLQ3FVxJsbwI9AOTP4ZCLDnzblroQjiUCW1vbVE6YnoO0YTb67Dj/g9CNOZpz
E0lJ3boUlg6lOEpDdwKxSswXhQU53OMJXQJFAKC+BaB0/Uk4EVnNHZkiG4lsp2Bd
AeR4wg8cCqiRYCK7Cy6u+1sZm4Mvwk05AMN88TYLEiO/mcJLswTMF9LDqAqLvoxP
AjxDmlPXo+4k37AZyzhkIN0jN5siGZ+D5DBw0RQoBv5ICOHDC0rgdW2IQ/rN2uzV
rDcmWYFy6WQI1j636ZUCRHWmzCi6ymkcUnqwlYXsjbZSVM7njOc3sJyVhaOgqs+o
1Zjbtpqq7DYEBHPFizh/nu6kf4lT3w5+vIKGs8r4k2IUMcJi
-----END RSA PRIVATE KEY-----
I have a doubt about the order of the parameters returned by "Asn1Object.FromByteArray()". I noticied the [0] was just a "0", that means empty for me. The length is 9 so it seems to be the good way, i'm just not sure about in which order they are returned. Tried to find some documentation but i found nothing.
Thanks in advance for people gonna help me
EDIT
First, i want to say my goal is to get a RSAParameters object.
As suggered by someone who deleted his post, i changed
rp.InverseQ = ConvertRSAParametersField(privKey.QInv, rp.Q.Length);
to
rp.InverseQ = ConvertRSAParametersField(privKey.QInv, rp.P.Length);
but after doing it, i got an exception System.Security.Cryptography.CryptographicException who telling me "Incorrect data." on the line
var csp = new RSACryptoServiceProvider(); // my csp object is already defined somewhere else but it change nothing
csp.ImportParameters(parameters); // Exception here, he doesn't like my object who doesn't seems valid for him.
The real problem seems to be "why my object is rejected by RSACryptoServiceProvider.ImportParameters()
Related
First off, I'm still new to crypto/signing so bear with any misuse of terms please.
I need to create a signature in C# that is getting verified by a Python library. In Python, it's a simple chunk of code to decode/verify the signature:
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding
public_key.verify(signature, payload_contents,
padding.PSS(mgf=padding.MGF1(hashes.SHA256()),
salt_length=padding.PSS.MAX_LENGTH,
), hashes.SHA256(), )
My current C# code looks like this:
private static string CreateSignature(byte[] data, string privateKeyFileLocation)
{
var cert = new X509Certificate2(privateKeyFileLocation);
byte[] signedBytes;
using (var rsa = cert.GetRSAPrivateKey())
{
signedBytes = rsa.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pss);
}
var finalString = Convert.ToBase64String(signedBytes);
return finalString;
}
However, the signature is failing the verification check in the Python code. It looks like PSS padding in the .Net libraries defaults to using MGF1. However, I believe I'm having issues due to the mask generation function (MGF1) using a 256-bit hash in the Python code, but defaulting to SHA1 in C#. I've waded through the .Net C# documentation and it looks like there is no way to override this. I looked into Bouncy Castle's C# documentation and am just having trouble finding any kind of similar example of how to set padding with custom params. Does anyone have experience in this area and can lend a few hints?
Just for anyone else dealing with this. This is the code I ended up with.
private static string GenerateSignatureForData(string data, string privateKeyFileLocation)
{
var cert = new X509Certificate2(privateKeyFileLocation, "12345", X509KeyStorageFlags.Exportable);
var bcCert = TransformRSAPrivateKey(cert.PrivateKey);
var keyLen = (int)Math.Ceiling((cert.GetRSAPrivateKey().KeySize - 1) / 8.0);
byte[] signedBytes = CreateSignature(Encoding.ASCII.GetBytes(data), bcCert, keyLen);
return Convert.ToBase64String(signedBytes);
}
private static AsymmetricKeyParameter TransformRSAPrivateKey(AsymmetricAlgorithm privateKey)
{
RSACryptoServiceProvider prov = privateKey as RSACryptoServiceProvider;
RSAParameters parameters = prov.ExportParameters(true);
return new RsaPrivateCrtKeyParameters(
new BigInteger(1, parameters.Modulus),
new BigInteger(1, parameters.Exponent),
new BigInteger(1, parameters.D),
new BigInteger(1, parameters.P),
new BigInteger(1, parameters.Q),
new BigInteger(1, parameters.DP),
new BigInteger(1, parameters.DQ),
new BigInteger(1, parameters.InverseQ));
}
private static byte[] CreateSignature(byte[] data, AsymmetricKeyParameter privateKey, int keyLength)
{
var digest = new Sha256Digest();
var saltLength = keyLength - digest.GetDigestSize() - 2;
PssSigner signer = new PssSigner(new RsaEngine(), new Sha256Digest(), digest, saltLength);
signer.Init(true, new ParametersWithRandom((RsaPrivateCrtKeyParameters)privateKey));
signer.BlockUpdate(data, 0, data.Length);
return signer.GenerateSignature();
}
There's some extra in there because the salt length also had to be calculated. I had to look at the source code for the crypto library to calculate the salt MAX_LENGTH indicated in the python code here:
salt_length=padding.PSS.MAX_LENGTH
Here is strictly the code required to do that:
var cert = new X509Certificate2(privateKeyFileLocation, "12345", X509KeyStorageFlags.Exportable);
var bcCert = TransformRSAPrivateKey(cert.PrivateKey);
var keyLen = (int)Math.Ceiling((cert.GetRSAPrivateKey().KeySize - 1) / 8.0);
var digest = new Sha256Digest();
var saltLength = keyLength - digest.GetDigestSize() - 2;
Similarly, converting the .Net crypto lib AsymmetricAlgorithm to a Bouncy Castle AsymmetricKeyParameter required the conversion function TransformRSAPrivateKey seen above.
We need to take Modulus and exponent from RSA keys. I have created my pubilc key using following methodology. Please let me know how can we take modulus and exponent part from it. I have already read this post.
NSData* tag = [#"com.x.x.x" dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary* attributes =
#{ (id)kSecAttrKeyType: (id)kSecAttrKeyTypeRSA,
(id)kSecAttrKeySizeInBits: #1024,
(id)kSecPrivateKeyAttrs:
#{ (id)kSecAttrIsPermanent: #YES,
(id)kSecAttrApplicationTag: tag,
},
};
CFErrorRef error = NULL;
SecKeyRef privateKey = SecKeyCreateRandomKey((__bridge CFDictionaryRef)attributes,
&error);
if (!privateKey) {
NSError *err = CFBridgingRelease(error);
// Handle the error. . .
}
SecKeyRef publicKey = SecKeyCopyPublicKey(privateKey);
// Now I want modulus and exponent form this publicKey
EDITED :-
I have also send base64 string to server but there we are facing quite a issue to find public key ref from base64 string. if someone has done it in c#, you can also help us with this
c# code snippet
const string pKey = "-----key-----"
byte[] publicKeyBytes = Convert.FromBase64String(pKey);
var stream = new MemoryStream(publicKeyBytes);
Asn1Object asn1Object = Asn1Object.FromStream(stream);
Now we need public key component which we are unable to parse. Any help would be great
On C# you can achieve the encryption using this way,
const string publicKey = "MIGJAoGBAMIt95f4xaP7vYV/+Hdyb4DK0oKvw495PrRYG3nsYgVP7zlBE/rTN6Nmt69W9d0nGefuRlJFIr9TA8vlJmqTus6uXEasBuEjzH7vM7HQeAK6i8qEbVy0T+Uuq+16yy059NL7i/VWljVE6rqTntDUELmbIwNBwj6oBuL1z3SnFoMjAgMBAAE="; //generated on iOS
byte[] publicKeyBytes = Convert.FromBase64String(pKey);
var stream = new MemoryStream(publicKeyBytes);
Asn1Object asn1Object = Asn1Object.FromStream(stream);
Asn1Encodable asn1Sequence = asn1Object;
AlgorithmIdentifier algorithmIdentifier = new
AlgorithmIdentifier(PkcsObjectIdentifiers.IdRsaesOaep);
SubjectPublicKeyInfo subjectPublicKeyInfo = new
SubjectPublicKeyInfo(algorithmIdentifier, asn1Sequence);
AsymmetricKeyParameter asymmetricKeyParameter2 =
PublicKeyFactory.CreateKey(subjectPublicKeyInfo);
RsaKeyParameters rsaKeyParameters =
(RsaKeyParameters)asymmetricKeyParameter2;
RSAParameters rsaParameters = new RSAParameters();
rsaParameters.Modulus = rsaKeyParameters.Modulus.ToByteArrayUnsigned();
rsaParameters.Exponent = rsaKeyParameters.Exponent.ToByteArrayUnsigned();
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
rsa.ImportParameters(rsaParameters);
string test = "John snow is the true king";
byte[] encbyte = rsa.Encrypt(Encoding.UTF8.GetBytes(test), RSAEncryptionPadding.Pkcs1);
string encrt = Convert.ToBase64String(encbyte);
the next unit test export a privatekey and save it in bytes arrays using the rsa instance then encrypt the "hi" message, all is fine here, but the problem occur when It make rsa2 instance and import the previous privatekey in RSAParameter, then message can be decrypt after import privatekey, but It throw exception when you try to export privatekey of rsa2.
Please could you tell me why It can't extract Imported Private key
[TestMethod]
public void TestRsa()
{
var rsa = new RSACng(2048);
///Export private key to arrays
var rsaParam = rsa.ExportParameters(true);
byte[] yQ = new byte[rsaParam.Q.Length];
byte[] yP = new byte[rsaParam.P.Length];
byte[] yInverseQ = new byte[rsaParam.InverseQ.Length];
byte[] yDP = new byte[rsaParam.DP.Length];
byte[] yDQ = new byte[rsaParam.DQ.Length];
//Public Part Key
byte[] yPm = new byte[rsaParam.Modulus.Length];
byte[] yPe = new byte[rsaParam.Exponent.Length];
byte[] yD = new byte[rsaParam.D.Length];
rsaParam.Q.CopyTo(yQ, 0);
rsaParam.P.CopyTo(yP, 0);
rsaParam.InverseQ.CopyTo(yInverseQ, 0);
rsaParam.DP.CopyTo(yDP, 0);
rsaParam.DQ.CopyTo(yDQ, 0);
rsaParam.Modulus.CopyTo(yPm, 0);
rsaParam.Exponent.CopyTo(yPe, 0);
rsaParam.D.CopyTo(yD, 0);
var encrypt = rsa.Encrypt(Encoding.UTF8.GetBytes("hi"), RSAEncryptionPadding.Pkcs1);
///Importing private key in another instance of RSACng
var rsa2 = new RSACng(2048);
RSAParameters rsaParameters = new RSAParameters()
{
Q = yQ,
P = yP,
InverseQ = yInverseQ,
DP = yDP,
D = yD,
DQ = yDQ,
Exponent = yPe,
Modulus = yPm
};
rsa2.ImportParameters(rsaParameters);
var decryptData = rsa2.Decrypt(encrypt, RSAEncryptionPadding.Pkcs1);
Assert.AreEqual(Encoding.UTF8.GetString(decryptData), "hi");
rsa2.ExportParameters(true);///How can I prevent exception here
}
Thanks all!
In .NET Core the RSACng object should be in an exportable state when you use ImportParameters, and it should be the case in .NET Framework 4.7.2 as well.
You can put into an exportable state as long as you change the export policy before using the key (by trying to call Export or doing a sign/decrypt/encrypt/verify operation). For example, this works:
using (RSA rsa1 = new RSACng(2048))
using (RSACng rsa2 = new RSACng())
{
rsa2.ImportParameters(rsa1.ExportParameters(true));
rsa2.Key.SetProperty(
new CngProperty(
"Export Policy",
BitConverter.GetBytes((int)CngExportPolicies.AllowPlaintextExport),
CngPropertyOptions.Persist));
RSAParameters params2 = rsa2.ExportParameters(true);
Console.WriteLine(params2.D.Length);
}
Using the NCRYPT_EXPORT_POLICY_PROPERTY described at https://msdn.microsoft.com/en-us/library/windows/desktop/aa376242(v=vs.85).aspx.
I generate an AsymmetricCipherKeyPair as follows:
string curveName = "P-521";
X9ECParameters ecP = NistNamedCurves.GetByName(curveName);
ECDomainParameters ecSpec = new ECDomainParameters(ecP.Curve, ecP.G, ecP.N, ecP.H, ecP.GetSeed());
IAsymmetricCipherKeyPairGenerator g = GeneratorUtilities.GetKeyPairGenerator("ECDH");
g.Init(new ECKeyGenerationParameters(ecSpec, new SecureRandom()));
AsymmetricCipherKeyPair aKeyPair = g.GenerateKeyPair();
My intention was to extract the public and private keys and then rebuild the keys later. I first extracted the keys as follows:
byte[] privateKey = ((ECPrivateKeyParameters)aKeyPair.Private).D.ToByteArray();
byte[] publicKey = ((ECPublicKeyParameters)aKeyPair.Public).Q.GetEncoded();
How do I recreate the public and private key parameters so that I can use them? In this example, I do recreate the private key and then sign the data byte array.
public static byte[] SignData(byte[] data, byte[] privateKey)
{
string curveName = "P-521";
X9ECParameters ecP = NistNamedCurves.GetByName(curveName);
ECDomainParameters ecSpec = new ECDomainParameters(ecP.Curve, ecP.G, ecP.N, ecP.H, ecP.GetSeed());
ISigner signer = SignerUtilities.GetSigner("SHA-256withECDSA");
BigInteger biPrivateKey = new BigInteger(privateKey);
ECPrivateKeyParameters keyParameters = new ECPrivateKeyParameters(biPrivateKey, ecSpec);
signer.Init(true, keyParameters);
signer.BlockUpdate(data, 0, data.Length);
return signer.GenerateSignature();
}
Although it feels like a real hack, it works just fine. How can I do this with the Public Key? I set the variable xxx to (ECPublicKeyParameters)aKeyPair.Public and I can use the code below to verify the signature. Note that I could use xxx directly, but the point is to serialize xxx out and then back in, so, this code actually does convert the xxx variable and creates a new one, which is stored in xx. I then use xx to verify (which shows that I can round trip the key).
var xx = PublicKeyFactory.CreateKey(Org.BouncyCastle.X509.SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(xxx).GetDerEncoded());
ISigner signer = SignerUtilities.GetSigner("SHA-256withECDSA");
signer.Init(false, xx);
signer.BlockUpdate(data, 0, data.Length);
return signer.VerifySignature(signature);
I had hoped that I could create the key (xx) from Q.GetEncoded() similar to how I did it for the private key.
Is there a better way to rebuild the private key? also using an ASN.1 encoding? If so, perhaps I should use that instead.
I can do this as follows:
string curveName = "P-521";
X9ECParameters ecP = NistNamedCurves.GetByName(curveName);
FpCurve c = (FpCurve)ecP.Curve;
ECFieldElement x = new FpFieldElement(c.Q, xxx.Q.X.ToBigInteger());
ECFieldElement y = new FpFieldElement(c.Q, xxx.Q.Y.ToBigInteger());
ECPoint q = new FpPoint(c, x, y);
ECPublicKeyParameters xxpk = new ECPublicKeyParameters("ECDH", q, SecObjectIdentifiers.SecP521r1);
Then, I can use xxpk to verify the signature.
Disclaimer: I do not claim that this is the best way to do this, just that it works!
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);
}