C# - How to Decrypt an Encrypted Private Key with Bouncy Castle - c#

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.

Related

Create RSA public key and private key

Im trying to make a SHA1withRSA sign/verify function on ASP.NET core using BouncyCastle library, 1st of all I need to make a keypair, I am using this page to generate the key:
https://travistidwell.com/jsencrypt/demo/
However from BouncyCastle example code I found (C# Sign Data with RSA using BouncyCastle), from the Public and Private key text generated, I couldn't make the key files, which as I understand, would be a .CER file for public key and .PEM for private key.
So could you please suggest me a way to make the .CER and .PEM file? Also, I haven't found a complete example about signing SHA1withRSA using BouncyCastle library - or just core C#, I'd be so grateful if you could suggest me a complete example, thank you so much.
The linked website generates private keys in PKCS#1 format and public keys in X.509/SPKI format, each PEM encoded.
.NET Core only supports the import of PKCS#1 and X.509 keys as of version 3.0. For .NET Core 2.2, the easiest way is to apply BouncyCastle. For loading the PEM keys the PemReader of BouncyCastle can be used.
Option 1:
With BouncyCastle's DotNetUtilities class, RSAParameters instances can be derived and thus RSACryptoServiceProvider instances:
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.OpenSsl;
using Org.BouncyCastle.Security;
...
public static RSACryptoServiceProvider GetRSACryptoServiceProviderFromPem(string pem, bool isPrivate)
{
PemReader pemReader = new PemReader(new StringReader(pem));
object key = pemReader.ReadObject();
RSAParameters rsaParameters = isPrivate ?
DotNetUtilities.ToRSAParameters((RsaPrivateCrtKeyParameters)(((AsymmetricCipherKeyPair)key).Private)) :
DotNetUtilities.ToRSAParameters((RsaKeyParameters)key);
RSACryptoServiceProvider rsaKey = new RSACryptoServiceProvider();
rsaKey.ImportParameters(rsaParameters);
return rsaKey;
}
RSACryptoServiceProvider in turn has methods for signing/verifying:
RSACryptoServiceProvider privateCSP = GetRSACryptoServiceProviderFromPem(privateKey, true);
RSACryptoServiceProvider publicCSP = GetRSACryptoServiceProviderFromPem(publicKey, false);
byte[] dataToSign = Encoding.UTF8.GetBytes("The quick brown fox jumps over the lazy dog");
byte[] signature = privateCSP.SignData(dataToSign, HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1);
bool verified = publicCSP.VerifyData(dataToSign, signature, HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1);
Option 2:
Alternatively, the RsaPrivateCrtKeyParameters and the RsaKeyParameters classes of BouncyCastle can be used directly:
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.OpenSsl;
using Org.BouncyCastle.Security;
...
public static AsymmetricKeyParameter GetAsymmetricKeyParameterFromPem(string pem, bool isPrivate)
{
PemReader pemReader = new PemReader(new StringReader(pem));
object key = pemReader.ReadObject();
return isPrivate ? ((AsymmetricCipherKeyPair)key).Private : (AsymmetricKeyParameter)key;
}
as well as classes for signing and verifying provided by BouncyCastle's SignerUtilities:
RsaPrivateCrtKeyParameters privateKeyParameters = (RsaPrivateCrtKeyParameters)GetAsymmetricKeyParameterFromPem(privateKey, true);
RsaKeyParameters publicKeyParameters = (RsaKeyParameters)GetAsymmetricKeyParameterFromPem(publicKey, false);
ISigner signer = SignerUtilities.GetSigner("SHA1withRSA");
signer.Init(true, privateKeyParameters);
byte[] dataToSign = Encoding.UTF8.GetBytes("The quick brown fox jumps over the lazy dog");
signer.BlockUpdate(dataToSign, 0, dataToSign.Length);
byte[] signature = signer.GenerateSignature();
signer.Init(false, publicKeyParameters);
signer.BlockUpdate(dataToSign, 0, dataToSign.Length);
bool verified = signer.VerifySignature(signature);
Console.WriteLine(verified);
Both implementations are executable under .NET Core 2.2.

Failed to import RSA private key: "Bad Version of provider"

I have a random private key ("C:\tmp\private.key"):
-----BEGIN RSA PRIVATE KEY-----
MIICXgIBAAKBgQDHikastc8+I81zCg/qWW8dMr8mqvXQ3qbPAmu0RjxoZVI47tvs
kYlFAXOf0sPrhO2nUuooJngnHV0639iTTEYG1vckNaW2R6U5QTdQ5Rq5u+uV3pMk
7w7Vs4n3urQ6jnqt2rTXbC1DNa/PFeAZatbf7ffBBy0IGO0zc128IshYcwIDAQAB
AoGBALTNl2JxTvq4SDW/3VH0fZkQXWH1MM10oeMbB2qO5beWb11FGaOO77nGKfWc
bYgfp5Ogrql4yhBvLAXnxH8bcqqwORtFhlyV68U1y4R+8WxDNh0aevxH8hRS/1X5
031DJm1JlU0E+vStiktN0tC3ebH5hE+1OxbIHSZ+WOWLYX7JAkEA5uigRgKp8ScG
auUijvdOLZIhHWq7y5Wz+nOHUuDw8P7wOTKU34QJAoWEe771p9Pf/GTA/kr0BQnP
QvWUDxGzJwJBAN05C6krwPeryFKrKtjOGJIniIoY72wRnoNcdEEs3HDRhf48YWFo
riRbZylzzzNFy/gmzT6XJQTfktGqq+FZD9UCQGIJaGrxHJgfmpDuAhMzGsUsYtTr
iRox0D1Iqa7dhE693t5aBG010OF6MLqdZA1CXrn5SRtuVVaCSLZEL/2J5UcCQQDA
d3MXucNnN4NPuS/L9HMYJWD7lPoosaORcgyK77bSSNgk+u9WSjbH1uYIAIPSffUZ
bti+jc1dUg5wb+aeZlgJAkEAurrpmpqj5vg087ZngKfFGR5rozDiTsK5DceTV97K
a3Y+Nzl+XWTxDBWk4YPh2ZlKv402hZEfWBYxUDn5ZkH/bw==
-----END RSA PRIVATE KEY-----
I tried to use the RSACryptoServiceProvider.ImportCspBlob to import it but it failed with the error:
System.Security.Cryptography.CryptographicException: Bad Version of
provider.
Full stack trace:
Failed: System.Security.Cryptography.CryptographicException: Bad Version of provider.
at System.Security.Cryptography.CryptographicException.ThrowCryptographicException(Int32 hr)
at System.Security.Cryptography.Utils._ImportCspBlob(Byte[] keyBlob, SafeProvHandle hProv, CspProviderFlags flags, SafeKeyHandle& hKey)
at System.Security.Cryptography.Utils.ImportCspBlobHelper(CspAlgorithmType keyType, Byte[] keyBlob, Boolean publicOnly, CspParameters& parameters, Boolean randomKeyContainer, SafeProvHandle& safeProvHandle, SafeKeyHandle& safeKeyHandle)
at System.Security.Cryptography.RSACryptoServiceProvider.ImportCspBlob(Byte[] keyBlob)
at ConsoleApplication3.Program.DecodeRSA(Byte[] data, Int32 c_data) in C:\Users\myuser\Documents\Visual Studio 2015\Projects\myproj\ConsoleApplication3\Program.cs:line 28
at ConsoleApplication3.Program.Main(String[] args) in C:\Users\myuser\Documents\Visual Studio 2015\Projects\myproj\ConsoleApplication3\Program.cs:line 14
Press any key to continue . . .
Any idea what I am doing wrong?
This is my code:
using System;
using System.Security.Cryptography;
namespace ConsoleApplication3
{
class Program
{
static public byte[] privateKey;
static void Main(string[] args)
{
try
{
privateKey = System.IO.File.ReadAllBytes(#"C:\tmp\private.key");
DecodeRSA(privateKey);
}
catch(Exception e)
{
Console.WriteLine("Failed: {0}", e);
}
}
static public void DecodeRSA(byte[] data)
{
using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
{
rsa.ImportCspBlob(Program.privateKey);
}
}
}
}
Your private key has a PKCS1-PEM-Format. Private key BLOBs have another format. As already mentioned in the comments, the formats are very different and cannot be easily converted (see e.g. here). Of course you can use a PKCS1-PEM-key, but it is not that easy. Here are some options:
Possibility 1:
If you use .NET Core 3.0 there is a direct support for reading a PKCS1-key (see also here):
byte[] privateKeyPkcs1PEM = File.ReadAllBytes(#"C:\tmp\private.key"); // PKCS1 - PEM
byte[] privateKeyPkcs1DER = ConvertPKCS1PemToDer(Encoding.UTF8.GetString(privateKeyPkcs1PEM));
RSA rsa = RSA.Create();
rsa.ImportRSAPrivateKey(privateKeyPkcs1DER, out _);
// use e.g. rsa.Decrypt(...)
However, the ImportRSAPrivateKey-method can only process the DER-format which is essentially the binary format of the PEM-format (for more details see here or here). Thus, you have to convert the PEM-format into the DER-format with something like
private static byte[] ConvertPKCS1PemToDer(string pemContents)
{
return Convert.FromBase64String(pemContents
.TrimStart("-----BEGIN RSA PRIVATE KEY-----".ToCharArray())
.TrimEnd("-----END RSA PRIVATE KEY-----".ToCharArray())
.Replace("\r\n",""));
}
Alternatively, OpenSSL can also be used for the conversion:
openssl rsa -inform PEM -outform DER -in C:\tmp\private.key -out C:\tmp\private.der
Possibility 2:
You can convert your PKCS1-PEM-key into an PKCS8-DER-key using OpenSSL. The appropriate command is:
openssl pkcs8 -topk8 -inform pem -in C:\tmp\private.key -outform der -nocrypt -out C:\tmp\privatepkcs8.der
The difference between PKCS1- and PKCS8-format is explained here. Then you can import the key with built-in .NET-methods (see also here, section PKCS#8 PrivateKeyInfo):
byte[] privateKeyPkcs8DER = File.ReadAllBytes(#"C:\tmp\privatepkcs8.der"); // PKCS8 - DER
CngKey cngKey = CngKey.Import(privateKeyPkcs8DER, CngKeyBlobFormat.Pkcs8PrivateBlob);
RSA rsa = new RSACng(cngKey);
// use e.g. rsa.Decrypt(...)
Possibility 3:
If third-party-libraries may be used, BouncyCastle is also a possibility:
StreamReader reader = File.OpenText(#"C:\tmp\private.key"); // PKCS1 - PEM
AsymmetricCipherKeyPair keyPair = (AsymmetricCipherKeyPair)new PemReader(reader).ReadObject();
Pkcs1Encoding pkcs1Encoding = new Pkcs1Encoding(new RsaEngine());
// E.g. decryption
pkcs1Encoding.Init(false, keyPair.Private);
byte[] decrypted = pkcs1Encoding.ProcessBlock(encrypted, 0, encrypted.Length);
Possibility 4:
Another possibility is the use of the DecodeRSAPrivateKey-method from JavaScience which can process a PKCS1-key. However, the DecodeRSAPrivateKey-method can only process the DER-format. Thus, you must first manually convert the PEM-format into the DER-format using e.g. ConvertPKCS1PemToDer or OpenSSL.
Thanks to the #Topaco answer, I understood that I need to find the CSP blob and like he mentioned, it is undocumented.
So I just used byte[] a = rsa.ExportCspBlob(true); to get it.
I converted the bytes to base64 and this is a correct key format in base64 that worked for me:
BwIAAACkAABSU0EyAAQAAAEAAQD99dvdVctFcYP6fGCvz/8QcoJqjpfKMPxCIsVRAZSCaKTB6Dl0DbEQBcaLNe8Cm31lzMYyf/2vh6gM+GUHmKcBYo2Z7JvauTGXFXEyv02ai8RINlvAGAicZwWoyGJb5h4sM881Q5+BuDTcoyefk+b7x7KBQjMD/wNuPCWijZ0lsP+Gt1tPryE757QDDl95jQk04ZS+70vGOAO836f+RCyeA6c0ZEA1eYzsM/PVsv+nLwh7pTj7KLFSha5CM304SdcDnyOnt1ARyv1BQsRhyN3IAOH/Se00OfWhcc0sZCjg+xvDebKuoODHDhUfHJPchOmyvhSxjyNACJuxg1uGh3XRmaPoceXXFCuNhFGheK5cQrfUGHpWeJKrpWM/+f3XcrYob0jQCloBIicXfvhhPnkPojiOquxmjy0rA8/JRjHov3+znJY+pQgFC5cUmvGWxhWygm+qDwYco6yCSRkkaIp/K39uJXQ2pQf9XapqjtAJipRo5xX0o/itiDyF1qPT7TumZROMUhU3znXGnxPelZ2bA7SgPiu6BBKADfqG1XJE1K50ydaEfyXYceYHIs7UAMLw9aTptqHbPPGp1hDL2xpWBR6hvqkPqouiVJ7VgPHkjxwT/hgXBvJbHOm/ghq/xA/1oTtXLJHXCASVdylt+nwauOp5qR0Dfdbz7IQGjChYzBHuqDuKorpmfHhZl+bDTHpJ1PjWrojoBfAt2v5zlBnw/ipjkD9MXKrNlPqbgeYXUAeAzfFKQhF2kr3zlmExIS8=
It is important to mentioned that I just need that this function will pass successfully because I am working on a blackbox application and I can't change the code, I need to provide it with the right input.

Different answers on python and .net for encrypting with a single public key

I have this code in .net:
RSACryptoServiceProvider cipher = new RSACryptoServiceProvider();
cipher.FromXmlString(publicKey);
byte[] data = Encoding.UTF8.GetBytes(input);
byte[] cipherText = cipher.Encrypt(data, false);
result = Convert.ToBase64String(cipherText);
and this code in python:
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5
from base64 import b64encode
key = open(public_key_loc, "r").read()
rsakey = RSA.importKey(key)
encrypted_data = rsakey.encrypt(input, 32)[0]
result = b64encode(encrypted_data)
When I run this codes with a public key, I get different answers on the same input!
I've searched and found out that both Crypto (python) and RSACryptoServiceProvider (.net) are using the RSA algorithm.
EDIT:
The public key used in .net is created by removing -----BEGIN PUBLIC KEY----- and -----END PUBLIC KEY----- from the key and converting it to XML.
Example:
PublicKey:
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCETtzC9pZ+dnQ0z0pXL6pNrkn4vGdbLTf3fhH5
MsVYsFIPuuaUSC9EnbTa8G9p1AIKNsjQaBbzfkvgdu5Tz8qEXZfYQV2bnSCtl/87M7Xn0raAmGTr
jSliTdsxMyJHObzAPkamjHemAxHd9VkwXfZOPAh00ueag+buTAkbzL1MlQIDAQAB
-----END PUBLIC KEY-----
Input:
"6000306"
.net output: SpDXp/KCea8DlIuhow6k8+uyfGFe93r9+w39ROoSRAggF9dBU3boK5zRareOQo2//7LyMZZVVklvDCFPo/irJtgbxjn6c0C7gHrL7ubKRG7iVaa9iSF1u13gdRZvLGy/MKOxiz9G+FKLZfJYtkiOSLkJHXXMWTGSNedQsdraJZc=
Python output:
Q3H0NTJYZzymWhWDtMCSzcqZ0D/Nvumq3VqvFCvQRlib82pth48DbVcKwjrmSjT0F/ipi7mnSq8M7BLX/7wo4tQFqul9+avyI/eAW5LKbuFZiiD8eP0GMwEZZyHGurFKhxu+1Qa0dftCIeiIMVJsVaHcUD254BSkYQC04Fflvfc=
What is the problem? Am I missing something?
For the python code, you have neglected to use the PKCS1_v1_5 module you imported. As a result, you're getting plain vanilla RSA encryption with no padding. This is not what you want. If instead your code is:
key = open(public_key_loc, "r").read()
rsakey = RSA.importKey(key)
cipher = PKCS1_v1_5.new(rsakey)
encrypted_data = cipher.encrypt(input)
result = b64encode(encrypted_data)
Then you'll see that the cipher changes every time, even with the same input, just like the C# side does. PKCS1 version 1.5 type 2 padding includes a random component, so the output should never be the same.
The PKCS1_OAEP module is the more modern choice, and is supported by .NET simply by change the false to true in the RSACryptoServiceProvider.Encrypt() method.

How do I extract the public key from private key in c#?

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 :)

Using C# to get the Public Key from my cert for Java

Without BounceyCastle.
I have my cert, and the GetPublicKey() value is not what the Java side of the house needs.
The cert if an X509Certificate2 object, using DSA encryption. Created using makecert
Convert.ToBase64String(cert.GetPublicKey()) returns
AoGAeaKLPS4ktxULg3YQL0ePphF08tKsddZtv3SDERa8b8go5h3AxmWjuDd8y9dIzZFe8KDjY9Lg
JU4JOA27snO3fCsPAVkmJ0O2pbxn+wzT7oij2FOLcCAjnFNNsoaWrtMv+I4XXl18DyDQLFkZiPx9
2UyuDzoQTGxgCrPccQPjUgY=
Convert.ToBase64String(cert.RawData) returns
MIICxjCCAoagAwIBAgIQbdIpaaU9rZdA+wJKA+mUfDAJBgcqhkjOOAQDMBYxFDASBgNVBAMTC0RT
QSBSb290IENBMB4XDTEzMDExMDE3MTAzNVoXDTM5MTIzMTIzNTk1OVowFDESMBAGA1UEAxMJVXNl
ciBOYW1lMIIBtzCCASwGByqGSM44BAEwggEfAoGBALWn4Iyvn7LFkV9ULoZtwJ8J1c+ibsbhjPiw
+xUgRW2LAZV2/Lv89W1jCprNkf87tN/ogMT/1VSIOo7ff/tqVRTWPVJ1ZMrR9VOnF2k/Sorg8Cmr
sAClsSWrACKIwK2XKJGWTU4oMLxvYcu85+yQ4nWLofgA/+WARrJ/rk2aUSZ3AhUAlqPLNh6JZkpD
G/OXKzhsZFUiDrkCgYEAoICjHltWOgN8/2uyAaMTNrBuJfi/HM9AWe5B8m9HDfl1K6Qx2Ni6tbYP
uFtvHdGnoqqn46l7eY+xjpi5GEydvkPtAQKmTDGcSh6vtnTeNV15Hafg5pXUKw1OisIr/bpx/KIk
cgCtSo6qC5IhDzeZXnfJYcE+8U+O6hEr5dwByN4DgYQAAoGAeaKLPS4ktxULg3YQL0ePphF08tKs
ddZtv3SDERa8b8go5h3AxmWjuDd8y9dIzZFe8KDjY9LgJU4JOA27snO3fCsPAVkmJ0O2pbxn+wzT
7oij2FOLcCAjnFNNsoaWrtMv+I4XXl18DyDQLFkZiPx92UyuDzoQTGxgCrPccQPjUgajWTBXMAwG
A1UdEwEB/wQCMAAwRwYDVR0BBEAwPoAQmhMLkJ/cPXGitvGMB81tZaEYMBYxFDASBgNVBAMTC0RT
QSBSb290IENBghDCpMJ75zgZokJVlZmNq/LTMAkGByqGSM44BAMDLwAwLAIUYUALM9WhgwzRMj1y
MSdoparmYvICFFxLgFr2ow3NGTkqWvHIXtjO9R0G
However, when my Java counterpart gets the public key, using the same cert file, gets
$ cat david-509.cer | openssl x509 -pubkey
-----BEGIN PUBLIC KEY-----
MIIBtzCCASwGByqGSM44BAEwggEfAoGBALWn4Iyvn7LFkV9ULoZtwJ8J1c+ibsbh
jPiw+xUgRW2LAZV2/Lv89W1jCprNkf87tN/ogMT/1VSIOo7ff/tqVRTWPVJ1ZMrR
9VOnF2k/Sorg8CmrsAClsSWrACKIwK2XKJGWTU4oMLxvYcu85+yQ4nWLofgA/+WA
RrJ/rk2aUSZ3AhUAlqPLNh6JZkpDG/OXKzhsZFUiDrkCgYEAoICjHltWOgN8/2uy
AaMTNrBuJfi/HM9AWe5B8m9HDfl1K6Qx2Ni6tbYPuFtvHdGnoqqn46l7eY+xjpi5
GEydvkPtAQKmTDGcSh6vtnTeNV15Hafg5pXUKw1OisIr/bpx/KIkcgCtSo6qC5Ih
DzeZXnfJYcE+8U+O6hEr5dwByN4DgYQAAoGAeaKLPS4ktxULg3YQL0ePphF08tKs
ddZtv3SDERa8b8go5h3AxmWjuDd8y9dIzZFe8KDjY9LgJU4JOA27snO3fCsPAVkm
J0O2pbxn+wzT7oij2FOLcCAjnFNNsoaWrtMv+I4XXl18DyDQLFkZiPx92UyuDzoQ
TGxgCrPccQPjUgY=
-----END PUBLIC KEY-----
And thus my problem. How do I get this value from my cert?
Thanks!
You should use cert.PublicKey.EncodedKeyValue instead of cert.GetPublicKey().
EncodedKeyValue provides ASN1 encoded value, not raw key data as GetPublicKey().
So you can use this code
void ExportPublicKey(X509Certificate2 cert, string filePath)
{
byte[] encodedPublicKey = cert.PublicKey.EncodedKeyValue.RawData;
File.WriteAllLines(filePath, new[] {
"-----BEGIN PUBLIC KEY-----",
Convert.ToBase64String(encodedPublicKey, Base64FormattingOptions.InsertLineBreaks),
"-----END PUBLIC KEY-----",
});
}
See Ian Boyd's answer. It just provides all the answer that you're looking for about encoding. Note that it's related to RSA and not DSA, but it gaves you all the informations about PEM/DER/ASN.1 encoding, which is your problem here.

Categories