I am using the following c# code to encrypt and decrypt using rsa with PEM files:
public string encrypt(string elementToEncrypt, string pathPrivateKey) {
string pem = System.IO.File.ReadAllText(pathPrivateKey);
byte[] Buffer = getBytesFromPEMFile(pem, "PUBLIC KEY");
System.Security.Cryptography.RSACryptoServiceProvider rsa = new System.Security.Cryptography.RSACryptoServiceProvider();
System.Security.Cryptography.RSAParameters rsaParam = rsa.ExportParameters(false);
rsaParam.Modulus = Buffer;
rsa.ImportParameters(rsaParam);
byte[] encryptedMessageByte = rsa.Encrypt(Convert.FromBase64String(elementToEncrypt),false);
return Convert.ToBase64String(encryptedMessageByte);
}
public string decrypt(string elementToDesencrypt, string pathPublicKey)
{
string pem = System.IO.File.ReadAllText(pathPublicKey);
byte[] Buffer = getBytesFromPEMFile(pem, "RSA PRIVATE KEY");
System.Security.Cryptography.RSACryptoServiceProvider rsa = new System.Security.Cryptography.RSACryptoServiceProvider();
System.Security.Cryptography.RSAParameters rsaParam = rsa.ExportParameters(false);
rsaParam.Modulus = Buffer;
rsa.ImportParameters(rsaParam);
byte[] encryptedMessageByte = rsa.Decrypt(Convert.FromBase64String(elementToDesencrypt), false);
return Convert.ToBase64String(encryptedMessageByte);
}
public byte[] getBytesFromPEMFile(string pemString, string headerPEM) {
string header = String.Format("-----BEGIN {0}-----", headerPEM);
string footer = String.Format("-----END {0}-----", headerPEM);
int start = pemString.IndexOf(header, StringComparison.Ordinal) + header.Length;
int end = pemString.IndexOf(footer, start, StringComparison.Ordinal) - start;
if (start < 0 || end < 0)
{
return null;
}
return Convert.FromBase64String(pemString.Substring(start, end));
}
But the problem is when i want to decrypt in the line:
byte[] encryptedMessageByte = rsa.Decrypt(Convert.FromBase64String(elementToDesencrypt), false);
and the error that i am getting is that the key does not exist.
My pem files are:
key.pem
-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQDLPKI8p+ANRabCTdLvJjuT0wx1kt2voJ0+BtdTRBqhJQbRgM2P
dtHilmaVSyiVtD5l1mTl+h8mFRBttiH0VgW3KuyvFk2mrjF78MrsXlYoHVizGgeh
UWVUsNh7EhdgF/hM7miZMXsHoa/MEQwgytPwjpDbOXXECZz8CpHiyNOftwIDAQAB
AoGAUrmXgAEFHeHgAu8SkO2LCpy5UZI6UiaaWokGVIpAHJ+pqtU21tKSlByMHPC+
0FDRpTojT8kDrMieK0obgA0TvcUaARVPGZsLjB4WZLKh7e8LPaUTvAS9dTmKd7xB
4YGFKY+AJb38VdDU9CoQMsiPtIIiPWz09lgGvYRGzXmTBwECQQDsEtLRyOijXISK
iFhtdpBI4yAmnTYyYLrsPXgS7asa80h7vnTmOlUpuqsxZtWNVGcpNiYG4y8OpJU5
Jr8IkNnXAkEA3GRC63+SEbEo5wXcrHF+tzxfFmk3yzS38w5jtGik3yrp6psyjaQ8
Q+D3RaKjGYtjTH3pmljRH2OGEvrNwvFtIQJAFkLgJnAvn9gFl5qr3AamLHleesWw
aqe8eLKDNCW9UNlIKIMZOuydQ0YbBpmP4bfn0ncMtvGNanASskT5FrGyGQJASE7k
3dsnE4LqhpGXy0QZbQjzsain05XiXG52K/TBUy8DPCPbPDmMREEFH+WyWWkwFSKi
iC9nvUKr9IIxDCqlwQJBAIDwEg6yVGdVCQry+OEGtsiaGPveX+lAx/kULba0wfRq
KaQAstQrT7p+ONtC8x8NHDE/ayjz6GlEZ7svR/LZO7w=
-----END RSA PRIVATE KEY-----
and pubkey.pem
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDLPKI8p+ANRabCTdLvJjuT0wx1
kt2voJ0+BtdTRBqhJQbRgM2PdtHilmaVSyiVtD5l1mTl+h8mFRBttiH0VgW3Kuyv
Fk2mrjF78MrsXlYoHVizGgehUWVUsNh7EhdgF/hM7miZMXsHoa/MEQwgytPwjpDb
OXXECZz8CpHiyNOftwIDAQAB
-----END PUBLIC KEY-----
I have read that if I want to decrypt, I should have both keys into one pem file, but if I do it, I would not know what to put on
rsaParam.Modulus = Buffer;
I mean, do I have to mix both buffers (private and public) into one? if so, how should I do that?.
Thanks for the help.
.NET does not have built-in support for reading "bare key" files.
If you paste the Base64 component of your public key file to https://lapo.it/asn1js/ you'll see that it can be decomposed to
To import it into .NET you need to copy the bytes (not the decimal value) of the first integer (1024-bit in this example) into the RSAParameters.Modulus value, and the bytes of the second integer into the RSAParameters.Exponent value. In both cases if there's a leading 00 byte you need to leave that off.
For your current key:
More generically, you'd need to parse the public key file as an ASN.1 DER blob, then consume the Modulus and Exponent values out of the payload.
The easiest workaround if you need to do it more than once is to use OpenSSL to make a self-signed certificate for the key file, since .NET can use keys from certificates (
cert.GetRSAPublicKey()
).Adding support for bare keys is on the .NET Core roadmap (https://github.com/dotnet/corefx/issues/20414), and one can reasonably well assume that after it gets added to .NET Core it will make it to .NET Framework.
For decrypting you need the private key. Again, the easiest answer is to make a self-signed certificate from the key, bundle the cert and key together into a PFX/PKCS#12 file, and use
cert.GetRSAPrivateKey()
. But, for the hard way:Pasting the private key blob we see that it looks like
In https://tools.ietf.org/html/rfc8017#appendix-A.1.2 we see that the
RSAPrivateKey
structure looks likeAnd that version == 0 means that there are only two primes. Now we run into some quirks. In .NET, the
D
value must have the same size asModulus
. IfD
happened to come out to a "1016-bit" number (or smaller) you'd need to insert leading0x00
values. .NET also requires thatP
be exactly half the size ofModulus
(round up if it matters), and thatQ
,DP
,DQ
, andInverseQ
all have the same size asP
.So, again, for your key:
I've created a library for reading and writing PEM / ASN.1 encoded files. See https://github.com/huysentruitw/pem-utils
Can be installed from NuGet:
Usage