X509 to Create JWT - "Cannot find the requested object" - c#

I'll start by saying I know very little about cryptography. I know what public/private keys are in theory. I generated some using openSSL for Windows. My plan is to sign a JWT with the private key so it can be verified with the public key.
The lines I used to generate the keys are came from https://www.claudiobernasconi.ch/2016/04/17/creating-a-self-signed-x509-certificate-using-openssl-on-windows/
openssl genrsa 2048 > private.key
openssl req -new -x509 -nodes -sha1 -days 1000 -key private.key > public.cer
I opened the private key in notepad++ and copy/pasted the strings into a variable in C# (I tried removing line breaks, and keeping linebreaks with #"").
I try to create the x509 using the variable like so:
var x509 = new X509Certificate2( Convert.FromBase64String( privateKey ) );
However, I am getting a WindowsCryptographicException with the message "Cannot find the requested object".
I know what I want to do is possible, because we use that exact same line (with a different string) in our PROD code to read a JWT (I assume this string is my company's public key). If I pass that string into my x509 constructor, the cert generates successfully, but of course I can't sign the JWT with it because it knows it's public.
The x509 ctor also seems to be working for the author of this post: Trouble signing a JWT token with an x509 Certificate, but I can't get as far as him.
So, what am I doing wrong? What can't be found, and how do I "draw a map" to it?
==EDIT==
I thought there was enough of an example already, but I guess here you go:
using System;
using System.Security.Cryptography.X509Certificates;
namespace LocalTokenApi.JWT
{
public class JwtBuilder
{
public void GenerateJwt( string xmlUri )
{
var privateKey = #"
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDQrPEi4DwPJ65D
ZEybExHaslb2OZt+e/TRZAkO7LrlVf5aH7uiYYL42yGxhyBxQVbNDW8cAyD/r4o1
zRCCjBS1yk36YtZ+HzIc7X5c3YMmfC6k2r5GD6Ah9fkfhAbqzIiJo/GEJokCDkHS
tyniDDDnI6gteibuTRqS5qnA7YEhedqy5uOdb5TAKCLMCNJgkg9+lNTUPTg+D+Gj
94rSMJ9bpTEvU+sfta5UrDF2Owy15b9ExkELyJAWSTV/LcMrlhkZtkVQtNZM1xov
yNBPCu8+LsRnifjduS9MMh9z3RmnIE/MFl1XUnB/Ocf2HVQavP2U240kYrStVNz7
/twmPnUHAgMBAAECggEBAKw+Q9/ktM5Rk46+6FiMOg2JbSxaSpXxnReE+dEe5/nK
rGMZlFgpIuFkhwAxKD8zjoE82HyHvKIv8+YKuNj45VNUhF7rXF7IQyYLhmUC1nFa
yWl7wNi1pxjBHnu8D7WZVA5Ai2boI+jVedGDLIgQRgFTtkqrbB0A+bFNwcqkgBTv
MFYi8N5vJky3lnpivSY/hG7bOh9GacEDMqMy7aAe+15ppfHbzDcf5AUtHTwhtjc8
G/2VMdIUhozmz3b5XoSjuGeFSc/6CVsjKeLguRYr7Z4i0iaj89u60UPNwhxXTZlg
rrxfT7aZCgghSDLll/hH0Lqtwmd/ym9A2XRWxDabZtkCgYEA+Lt2P9CmoLun7/NN
i0IVnCaB4dpVaV7KgPtlWtE3oKB5wMhX18pJo5hB16UWfzBRRN3qL56iC1qJBAmW
pm/3ypcuzwm9PZrnPdb93cfWjW/GIMcK8oNUjpA2f/iLtZSO7iEua3Qtl1bJrJVv
MdVa+xdTViNR1TfhcJOnDmjH2mUCgYEA1sXaOYgi78S8m2LCyR5cJNTdW/7eBXVj
X48F1ZmpQGQ6+7258I43Gr2fHryAD9fidmi6YrRM+dDpk5FsIoVFK+Lqa4Oy79/y
oQt1HaByfvoBpDsCUY6yf/H4XhMUbZ/CF3tkceFvzBrrsAuuqh7OIsqe7hho9Sgd
/F8asP5exPsCgYA4wETtsISkPczGccPqlyxpEVwnFPLR9N/NaA6rFvtTOeots0hf
ovcETZQQSMmGQZb5WIy7Sr18S67hbfKijP+DiNUURguYh8RlFq2bsaHhaXRSPDfi
N1bOpFbbAfGWf4vRB18ZA0v3sMSZDQtu2lhE3ACWsb5VIMfeMMI4Bm47BQKBgQCP
/ljAB9D8lhepyj40HyHCI+FBg5ARctGsSLStr/c0z75n950Jdh/l0sozDkiB1sjj
gHWuJZoSR4nCwVYRku58bQekC8lVX/1JEeh0c5UwIqglFtcIHTb55x4Q3JPup5S2
r6j5XR7aZhYskriJIFwuIVEK6ty7uSjZgl3f2rtpLwKBgQC0BeuhdrQzrD8kg6cV
ZTvX12F5qJ1PFfbSpI9NwI5opqgCeGfUElEa32ig1v42taXNthWGGpFsUoSCoJG2
T2bDQ05TOItkg5/oVPJHS1ia26bxafTrXHtDoeuZ/G5oip2qULtQ62vUcazdsJ6x
zN6C1hsvJ4Kb3xPd2ZizjfDgAQ==";
var x509 = new X509Certificate2( Convert.FromBase64String( privateKey ) );
}
}
}

The data you provide to the constructor is not what the X509Certificate2 class expects. The X509Certificate2 expects the PKCS12 format.
In order to create data in the right format, the certificate including the private key as PKCS12, you missed one call to OpenSSL from the original source you linked to:
openssl pkcs12 -export -in public.cer -inkey private.key -out cert_key.p12
The data you have to pass to the X509Certificate2 constructor is then in the cert_key.p12 file. In case the private key is encrypted, you also have to specify the password as a second parameter to the X509Certificate2 constructor.

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.

Import BouncyCastle X509Certificate + Private Key (RSA) into Windows Certificate Store

I've tried about everything to import a BouncyCastle-based X509Certificate instance with the associated private key (RsaPrivateCrtKeyParameters) via a .NET X509Certificate2 + an RSACryptoServiceProvider instances and saved it into a certificate store (.NET's X509Store, My/CurrentUser).
In the Certificate Store MMC snapin, it seems like there is a private key associated with the certificate, and I've verified that a new key container is created in the appropriate place on disk, but when I try to export the certificate, I get the dreaded "Note: The associated private key cannot be found. Only the certificate can be exported" message.
If I run certutil -user -repairstore my THUMBPRINT, I get the following error:
ERROR: Certificate public key does NOT match stored keyset
From the other information it spits out, I can clearly see that the public keys differ, and that the Algorithm Parameters equals "05 00" on the Certificate Public Key, but not on the Container Public Key.
In fact, I was not aware that there was a concept of a container public key, so I'm just very confused now. Does anyone have some working code for doing this?
I found the solution in Cabadam's answer here:
https://social.msdn.microsoft.com/Forums/vstudio/en-US/ad01b2eb-1890-431a-86ae-e5da0e02b5b0/cryptographicexception-key-does-not-exist-when-attempting-to-connect-to-remote-service
RSACryptoServiceProvider tempRcsp = (RSACryptoServiceProvider)DotNetUtilities.ToRSA((RsaPrivateCrtKeyParameters)keyPair.Private);
RSACryptoServiceProvider rcsp = new RSACryptoServiceProvider(new CspParameters(1, "Microsoft Strong Cryptographic Provider", new Guid().ToString(), new CryptoKeySecurity(), null));
rcsp.ImportCspBlob(tempRcsp.ExportCspBlob(true));
dotnetCertificate2.PrivateKey = rcsp;
// Save the certificate to the X509Store

Digital signature using bouncy castle and certificate private key

I am developing a feature to digital sign some content. I have valid certificate with a private key. How to digital sign using the private key and bouncy castle?
I tried the following but want some right way to achieve the same using bouncy castle:
X509Certificate2 signingCert =
CryptoHelper.FindCertificate("21A6107EC254457AAF3D4D6FD286FB79");
var rsaObj = (RSACryptoServiceProvider)signingCert.PrivateKey;
_privateKey = rsaObj.ExportParameters(true);
Thanks!
I donĀ“t know exactly what you need based on your code, but there X509 namespace/code is at
bcgit/bc-csharp - X509 and there is an utility class for conversion between System.Security.Cryptography and BouncyCastle
bcgit/bc-csharp - DotNetUtilities.cs
BouncyCastle got lots of test (and examples). Have a look at bcgit/bc-csharp - TestCertificateGen.cs too. Maybe this helps you.
EDIT: In general it should go something like this
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.OpenSsl;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.X509;
// Your loaded certificate
X509Certificate cert = null;
// Your loaded RSA key
AsymmetricKeyParameter privateKey = null;
AsymmetricKeyParameter publicKey = cert.GetPublicKey();
ISigner signer = SignerUtilities.GetSigner(cert.SigAlgName);
// Init for signing, you pass in the private key
signer.Init(true, privateKey);
// Init for verification, you pass in the public key
signer.Init(false, publicKey);
Greetings

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

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