X509 Certificate format - c#

When I do X509Certificate2 x509 = new X509Certificate2(certificateFile); what format does the certificate need to be in?
I have a private key:
-----BEGIN RSA PRIVATE KEY-----
......
-----END RSA PRIVATE KEY-----
and a certificate:
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
..............
Signature Algorithm: sha1WithRSAEncryption
Issuer: C=IL, ST=SS, L=...., O=....., OU=....., CN=...../emailAddress=.....
Validity
Not Before: Mar 19 14:45:09 2013 GMT
Not After : Mar 19 14:45:09 2014 GMT
Subject: C=IL, ST=SS, L=...., O=....., OU=....., CN=...../emailAddress=.....
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (2048 bit)
Modulus:
...................
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Subject Key Identifier:
.........
X509v3 Authority Key Identifier:
keyid:..........
X509v3 Basic Constraints:
CA:TRUE
Signature Algorithm: sha1WithRSAEncryption
.....
Yet I am able to load neither using the C# code above. What am I missing?

-----BEGIN RSA PRIVATE KEY----- is a header for an RSA Private Key in PKCS#1 format (unencrypted). This format is used in Private Key PEM files.
The .NET Framework does not offer any ready-made method to directly export a Private Key in this format so you have to implement it yourself.
This document can help you - there is an example of decoding a RSA Private Key. Encoding is just the reverse operation.

Related

.net core get certificate private key to work with GRPC

I create a self sign certificate. Later I create a google GRPC server who needs a certificate and key file. I try to get the infos from a loaded .net certificate. I get the cert but I have problems with the key file. I dont get the correct key format from the stored cert.
That is only a shot example. Normally I want to store the cert in at the .net cert store and when the grpc service ic created I want to read the cert and key from the store. With that I will instantiate the service.
Cert creation:
openssl req -newkey rsa:4096 -x509 -sha256 -days 3650 -nodes -out myCert.crt -keyout myCert.key -config cert.conf
openssl pkcs12 -export -in myCert.crt -inkey myCert.key -out myCert.pfx -passin pass: -passout pass:
Then I load the cert as a .net certificate (normally I will get it from the store):
var cert = new X509Certificate2("myCert.pfx", "", X509KeyStorageFlags.Exportable);
// some stringbuild before to add -----BEGIN PRIVATE KEY-----
var privateKeyFromDotNetCert = Convert.ToBase64String(x509Certificate2.GetRSAPrivateKey().ExportRSAPrivateKey());
// for reference read the original key..only to shwo the problem
var serverkey = File.ReadAllText("myCert.key");
privateKeyFromDotNetCert is different to the original server key.
later I try to
var keypair = new KeyCertificatePair(cacert, serverkey);
var sslCredentials = new SslServerCredentials(new List<KeyCertificatePair> { keypair }, cacert, false);
with serverKey it works fine but not with the extracted key from the .net certificate. Is it possible to get the correct key from the .net certificate?
Cert creation from .net cert
With the first answer I tried:
StringBuilder builder = new StringBuilder();
builder.Append("-----BEGIN PRIVATE KEY-----");
builder.AppendLine(Convert.ToBase64String(certificate.GetRSAPrivateKey().ExportPkcs8PrivateKey(),Base64FormattingOptions.InsertLineBreaks));
builder.Append("-----END PRIVATE KEY-----");
privateKeyFromDotNetCert = builder.ToString();
This was working. privateKeyFromDotNetCert is not equal to serverkey. But accepted from the grpc service.
Your comment says you're prepending -----BEGIN PRIVATE KEY----- (and presumably appending -----END PRIVATE KEY-----), but the export method you chose (ExportRSAPrivateKey) doesn't match that format. Try ExportPkcs8PrivateKey, which exports in the PKCS#8 PrivateKeyInfo format, which matches the -----BEGIN PRIVATE KEY----- header. (ExportRSAPrivateKey produces a PKCS#1 RSAPrivateKey, which is -----BEGIN RSA PRIVATE KEY-----.)
You probably also want to use the Base64FormattingOptions.InsertLineBreaks option.

Verifying the chain of a self-signed certificate when using SslStream

I have a chain.pem
-----BEGIN CERTIFICATE-----
// My server cert signed by intemediate CA
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
// My intermediate cert signed by root CA
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
// My self signed root cert
-----END CERTIFICATE-----
as well as a server.key.pem
-----BEGIN RSA PRIVATE KEY-----
// Private key for server cert
-----END RSA PRIVATE KEY-----
From there, I generate a pfx file - which has the server cert with its private key along with the rest of the chain.
openssl pkcs12 -export -out certificate.pfx -inkey server.key.pem -in chain.pem
I leave the export password blank
Next I host an TcpListener with an SslStream
namespace fun_with_ssl
{
internal class Program
{
public static int Main(string[] args)
{
var serverCertificate = new X509Certificate2("certificate.pfx");
var listener = new TcpListener(IPAddress.Any, 1443);
listener.Start();
while (true)
{
using (var client = listener.AcceptTcpClient())
using (var sslStream = new SslStream(client.GetStream(), false))
{
sslStream.AuthenticateAsServer(serverCertificate, false, SslProtocols.Tls12, false);
//send/receive from the sslStream
}
}
}
}
}
But when I try to check the chain from openssl, it fails
openssl s_client -connect 127.0.0.1:1443 -CAfile ca.cert.pem
CONNECTED(00000005)
depth=0 CN = SERVER
verify error:num=20:unable to get local issuer certificate
verify return:1
depth=0 CN = SERVER
verify error:num=21:unable to verify the first certificate
verify return:1
---
Certificate chain
0 s:CN = SERVER
i:CN = Intermediate
---
Server certificate
-----BEGIN CERTIFICATE-----
// My Server certificate
-----END CERTIFICATE-----
subject=CN = SERVER
issuer=CN = Intermediate
---
No client certificate CA names sent
Client Certificate Types: RSA sign, DSA sign, ECDSA sign
Requested Signature Algorithms: RSA+SHA256:RSA+SHA384:RSA+SHA1:ECDSA+SHA256:ECDSA+SHA384:ECDSA+SHA1:DSA+SHA1:RSA+SHA512:ECDSA+SHA512
Shared Requested Signature Algorithms: RSA+SHA256:RSA+SHA384:RSA+SHA1:ECDSA+SHA256:ECDSA+SHA384:ECDSA+SHA1:DSA+SHA1:RSA+SHA512:ECDSA+SHA512
Peer signing digest: SHA256
Peer signature type: RSA
Server Temp Key: ECDH, P-384, 384 bits
---
SSL handshake has read 1439 bytes and written 481 bytes
Verification error: unable to verify the first certificate
---
New, TLSv1.2, Cipher is ECDHE-RSA-AES256-GCM-SHA384
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
Protocol : TLSv1.2
Cipher : ECDHE-RSA-AES256-GCM-SHA384
Session-ID: E82C0000B86186D0051CFE6290C12F0D62C4D376B7E40437029B8B85687C4B18
Session-ID-ctx:
Master-Key: 13681EAE940F241726072A4586A96A9FEEEF29B8309B9122FA2F07AC7C9F949128CB66D0F9C430E1D2480E61E287C578
PSK identity: None
PSK identity hint: None
SRP username: None
Start Time: 1566533377
Timeout : 7200 (sec)
Verify return code: 21 (unable to verify the first certificate)
Extended master secret: yes
---
140266287337920:error:14094416:SSL routines:ssl3_read_bytes:sslv3 alert certificate unknown:../ssl/record/rec_layer_s3.c:1528:SSL alert number 46
I doesn't seem as though it is presenting the intermediate or the root certificate so that it can verify the chain. What am I missing here?
In my scenario, the client would have the root certificate public key.
Even if the PFX contained the entire chain, using the single-certificate constructor makes it only load the cert which had a private key, and the rest are discarded.
Even if the load-the-PFX-as-a-collection method is used, SslStream only uses a collection to find an acceptable server certificate (has a private key and the proper EKU value), then ignores the rest.
The intermediate certificate is only sent when it can be found, via system ambient context, by X509Chain. If your certificate is not public-trust then the best answer for your scenario is to add the intermediate (and, optionally, the root) to the CurrentUser\CA (X509StoreName.CertificateAuthority) certificate store. The "CA" store doesn't provide trust, it's just a grab bag of all the intermediate issuer CAs the system has seen, and the system uses it as a cache when building new chains.
You can do this programmatically at startup by
X509Certificate2 serverCertificate = null;
using (X509Store store = new X509Store(StoreName.CertificateAuthority, StoreLocation.CurrentUser))
{
store.Open(OpenFlags.ReadWrite);
X509Certificate2Collection coll = new X509Certificate2Collection();
coll.Import("certificate.pfx");
foreach (X509Certificate2 cert in coll)
{
if (cert.HasPrivateKey)
{
// Maybe apply more complex logic if you really expect multiple private-key certs.
if (serverCertificate == null)
{
serverCertificate = cert;
}
else
{
cert.Dispose();
}
}
else
{
// This handles duplicates (as long as no custom properties have been applied using MMC)
store.Add(cert);
cert.Dispose();
}
}
}
// tcpListener, et al.
Other options: Feed the whole collection into X509Chain.ChainPolicy.ExtraStore, call X509Chain.Build on serverCert, only add the certs after the first one (and optionally not the last one)... just depends on how much extra stuff is expected to be in the PFX.

RSA integration between c# and php

I want to send encrypted data to php as part of a c# project. The php page should decrypt the data and perform some operation on it ,then encrypt it again, and return it back to the c# application.
There is an issue with how the kyes look:
rsa private key:
uGUskwU/GVS1HjBVBa9ECPDRH04fH............
php private key:
-----BEGIN RSA PRIVATE KEY----- MIICXAIBAAKBgQDARufsW5Z9I+P96RdfDEq8r5XYaC ......
How can I integrate the two, or is there another class in C# I can use besides cryptography which would work?
Try using: http://phpseclib.sourceforge.net/
Generate keys:
openssl genrsa -out private.pem 1024
openssl rsa -in private.pem -out public.pem -outform PEM -pubout
Copy to php:
<?php
include('Crypt/RSA.php');
$privateKey="-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQDr5KvAc6JX22BdpsfQbWam8rjkwJzTC954XQzhLOPeGiAGfkPm
CLYhBrmxZN6NxiseivpN9yzrRjYeccr+s3A/8q1EbdhmmM4+AxdNzATEsUxL//MS
VXCaoJhGOfXswokFT8HQod9M6VMmuyDaw9iEorLavV6J/nCmq2HIutcbbwIDAQAB
AoGBAMt2dgnN8zXSW2ELrIcDBapz6rQrylhigWXR5H0IKhb087CGEeyEkQsVcO8o
MKmaRrDWcKT00IKyEZXa0pszjFmD6f90H6TrV6BwZKEynAUVqaX4CCn6x9wgppMA
hWg3eYgoUME3Xgr1Iugb4rqtocEL7Vww/NoUgLXhzHH4RNz5AkEA+mZN2189Te3m
LmXFIJhV+GxrDmYrgJ0csjoEFHAxKJ52lsXTe1VODWbGWAv59jOrbUKe/aQaj4N1
IcTinF3jBQJBAPErTwBKvHznU9dC0eY+JRpwm4Xb9zCNqaG/Ir5N8Gc1U0YLJh9D
vRhtyT9+shwRPznklkc8DKwFaEU7HSiw9uMCQGLK9FLmG6jggN2zd3gpwlmWZRK0
StueoVDMRQnPTgXpp35LJOpXOMle0EiyyPdTYYJlM5d9JKGUyyT8qi/pdwUCQQCE
jp9vs6SCqsukh+/DM+lE7RwuqUbnAxZ39wzruP8oW2EYIMylZqArKzzwj3zqfHha
I9CN+u2kJ5y2YNWYJhytAkA53cDUSL9Wo9pq5hw1C1SWNcimteeQmfQ8f0SMegDW
fZ4rCjGczF82UpB12Cqo8SiK8xrCZAxfnSmSI248oCWe
-----END RSA PRIVATE KEY-----";
$publicKey="-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDr5KvAc6JX22BdpsfQbWam8rjk
wJzTC954XQzhLOPeGiAGfkPmCLYhBrmxZN6NxiseivpN9yzrRjYeccr+s3A/8q1E
bdhmmM4+AxdNzATEsUxL//MSVXCaoJhGOfXswokFT8HQod9M6VMmuyDaw9iEorLa
vV6J/nCmq2HIutcbbwIDAQAB
-----END PUBLIC KEY-----";
$rsa = new Crypt_RSA();
$rsa->loadKey($publicKey); // public key
$plaintext = 'I am a secret.. shhhhhh.';
$rsa->setEncryptionMode(CRYPT_RSA_ENCRYPTION_PKCS1);
$ciphertext = $rsa->encrypt($plaintext);
$rsa->loadKey($privateKey); // private key
echo $rsa->decrypt($ciphertext);
?>
You can use different keys, but the php code must encrypt data using the C# public key and the C# code must use the php public key to encrypt data posted to the php code.
You can also have only two keys, one private and one public and use on both sides.
Take a look at this answer of doing this on the C# side: https://stackoverflow.com/a/1162519/2715306
And this: https://stackoverflow.com/a/251757/2715306

How can I add a private key to X509Certificate2 after I have already added the public key?

My steps are:
Create X509Certificate2 with public key:
X509Certificate2 clientCertificate = new X509Certificate2("public key certificate blob as byte[]");
How do I want to load the private key blob to clientCertificate?
It looks like you can not import RSA private key with just .net framework tools.
Check out this thread How to read a PEM RSA private key from .NET.
Just in case anyone like me and up looking at this old post when searching for how to generate X509Certificate2 from pem fil/private key:
The .Net 5.0 framework has a very simplified approach to this:
var certPem = File.ReadAllText("cert.pem");
var eccPem = File.ReadAllText("ecc.pem");
var cert = X509Certificate2.CreateFromPem(certPem, eccPem);
(source: https://www.scottbrady91.com/C-Sharp/PEM-Loading-in-dotnet-core-and-dotnet)
Also see: How to import PKCS#8 RSA privateKey (created by OpenSSL) in C#
It includes a link to Mono's source code which can read PKCS#8 / PEM files and return an RSA instance from it.

Decrypt with PrivateKey X.509 Certificate

I have a problem to decrypt a message usgin X.509 Certificate.
I generate my certificate with makecert with this options:
makecert -r -pe -n "CN=MyCertificate" -ss CA -sr CurrentUser -a sha1 -sky signature -cy authority -sv CA.pvk CA.cer
And the PrivateKey was "mypassword".
My problem is when I want to decrypt a message encrypt with previous certificate in c#.
I found this class http://blog.shutupandcode.net/?p=660, but in the X509Decrypt method allways the PrivateKey is null.
public static byte[] X509Decrypt(byte[] data, string certificateFile, string password)
{
// load the certificate and decrypt the specified data
using (var ss = new System.Security.SecureString())
{
foreach (var keyChar in password.ToCharArray())
ss.AppendChar(keyChar);
// load the password protected certificate file
X509Certificate2 cert = new X509Certificate2(certificateFile, ss);
using (RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)cert.PrivateKey)
{
return rsa.Decrypt(data, true);
}
}
}
I tried passing the certificate file (.cer)
X509DecryptString(token, #"c:\CA.cer", "mypassword");
And passing the pvk file (.pvk)
X509DecryptString(token, #"c:\CA.pvk", "mypassword");
But allways have that the PrivateKey property is null.
Can anyone guide me to decrypt the message using the pvk file?
Thanks,
Jose
The certificate itself only contains the public key (+ some data), but not the private key. (It's very unlikely that the RSA private key is "mypassword". The password that protects your private key may be "mypassword", but the private key itself (more specifically the private exponent, in RSA) will be a rather long number.)
As a result (because CA.cer only contains the certificate), X509DecryptString(token, #"c:\CA.cer", "mypassword") will almost certainly not work.
X509DecryptString(token, #"c:\CA.pvk", "mypassword"); could work in principle, but you're creating a X509Certificate2 object from it, and it still needs the certificate and the private key. You should be able to load that from a PKCS#12 container (.p12/.pfx).
To create this container, you can use pvk2pfx:
pvk2pfx -spc CA.cer -pvk CA.pvk -pfx CA.pfx
(If you don't specify -pfx CA.pfx, it will launch the interactive interface, in which case you need to tick the box to export the private key.)
Then, try to decrypt using that pfx/p12 file instead.
I think you should be using "-sky exchange" to generate a public/private key pair.

Categories