Associate a private key with the X509Certificate2 class in .net
Solution 1
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));
}
Solution 2
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");
Solution 3
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);
PogoMips
Updated on October 02, 2021Comments
-
PogoMips over 2 years
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?
-
Dan Hastings almost 9 yearsthis saved me! created the pfx, but didnt realize i also needed to provide that password to the constructor.
-
Alejandro over 7 yearsThis is a better way than to bundle the private key and certificate together, I've used this option and find it a superior alternative.
-
sschober over 7 yearsIf you'd be so kind as to enlighten us about the nature of this superiority?
-
Poul K. Sørensen about 7 yearsdo include the code for the helper methods, annoying to have to create a user on an external site to get access.
-
Traderhut Games almost 7 yearsTrying to use this, but (of course) there is no Crypto Class (there is one in System.Web.Helpers - pretty sure not it..) Got a using missing here?
-
starmandeluxe almost 7 yearsToo short of an answer (and yes having to register to get the rest of the code is unacceptable), but this is a good starting point. And @TraderhutGames I did some digging and here's what we're looking for: stackoverflow.com/questions/1162504/…
-
Traderhut Games almost 7 yearsI ended up getting the Secret from the Azure Key Vault (where I was trying to make my certificate from) and then simply doing this: X509Certificate2 cert = new X509Certificate2(Convert.FromBase64String(value), "", X509KeyStorageFlags.MachineKeySet |X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable);
-
starmandeluxe almost 7 yearsWhat is "value" in your constructor? I'm guessing the private key? Azure Key Vault is a great option and was initially what I also wanted to use, but there is a huge limitation in that your AD application must be in the same directory as the Key Vault itself. My circumstances don't allow this unfortunately.
-
Sean over 6 yearsOutstanding answer! I've used this to avoid the hassle of registering the certificate via the MMC plugin.
-
Peter Waher almost 6 yearsI get a
PlatformNotSupportedException
thrown when trying to set the private key of a certificate in a .NET Core 2 console application. -
Nate Hammond about 5 yearsYou can avoid the password by adding "-passout pass:" to the openSSL command
-
Chris Jensen almost 5 yearsThis should be marked as the real answer, as it is the real way to do it.
-
Chad over 4 yearsAs stated above, you may want to bundle the private key and certificate together. However, if you do need to employ this solution, you may need the following enumeration to get it to work:
public enum PemStringType { Certificate = 1, RsaPrivateKey = 2 }
-
user1034912 over 3 yearswhere do i get openssl?
-
Jaans over 2 years
ImportPkcs8PrivateKey
is .NET Core - not available in .NET Framework 4.8? -
lin over 2 years
-
lukaszFD about 2 years@lin, Your solution helped to eliminate my stackoverflow.com/q/71343850/7038630 error. The only change I made was to replace "ImportPkcs8PrivateKey" with "ImportRSAPrivateKey". And here stackoverflow.com/a/70132607/7038630 is the answer why I changed "ImportPkcs8PrivateKey" to "ImportRSAPrivateKey".