Asp.net C# JWT Decode with private key (.p12) - c#

I need to decode the encryptedToken with private key file .p12..
For private key, I already assign in X509Certificate2 and calling with
RSA privateKey = _x509private.GetRSAPrivateKey();
I am not using
RSACryptoServiceProvider privateKey = _x509private.PublicKey.Key as RSACryptoServiceProvider;
because that function is already deprecated.
But when I want to use this function to decode the encryptedToken
string plainToken = Jose.JWT.Decode(encryptedToken, privateKey);
What I expected as output is token that I will verify token with public key. But, Unfortunately I get the error message of
Algorithm 'AesGcm' is not supported on this platform.
I was thinking the error message was arise because I didn't use RSACryptoServiceProvider as Private key.
I already search how to convert RSA to RSACryptoServiceProvider, but can't find anything...
I also considering not using the Jose.JWT.Decode(), but no luck...
Is there anyone that can help me?
PS: I am using .NET6 and Visual Studio Mac 2019 V.8

As bartonjs said in this comment, it only affects macOS.
.NET on macOS can’t use Apple’s AES-GCM implementation and requires OpenSSL to be installed to provide the algorithm. How to use .Net Core with Openssl3 on Mac OS? may help.

Related

Problems signing an XML file, using C# [duplicate]

Here is my code.
X509Certificate pXCert = new X509Certificate2(#"keyStore.p12", "password");
RSACryptoServiceProvider csp = (RSACryptoServiceProvider)pXCert.PrivateKey;
string id = CryptoConfig.MapNameToOID("SHA256");
return csp.SignData(File.ReadAllBytes(filePath), id);
On the last line I'm getting the exception:
System.Security.Cryptography.CryptographicException "Invalid algorithm specified."
What am I doing wrong?
UPDATE:
id = 2.16.840.1.101.3.4.2.1
For dot net framework 4.7.0 or higher is not taking the sha1 so configure the below in application start. it's worked fine for me.
AppContext.SetSwitch("Switch.System.Security.Cryptography.Xml.UseInsecureHashAlgorithms", true);
AppContext.SetSwitch("Switch.System.Security.Cryptography.Pkcs.UseInsecureHashAlgorithms", true);
There is no issue with .NET code or the CSP code you provided.
Your problem is that CSP just doesn’t support SHA 256. You can get further information here
You might have come here while you are migrating your application from .NET Framework 4.7 and earlier versions to 4.7.1 or later versions.
If you are getting the exception System.Security.Cryptography.CryptographicException: Invalid algorithm specified., the reason is that default SignedXML and SignedXMS algorithms changed to SHA256 for applications that target the .NET Framework 4.7.1 and later versions (from Microsoft .NET migration guide)
In that guide you'll also find the solution:
For applications that target the .NET Framework 4.7.1 and later versions, if the use of SHA256 is undesirable, you can restore the default to SHA1 by adding the following configuration switch to the runtime section of your app config file:
<AppContextSwitchOverrides value="Switch.System.Security.Cryptography.Xml.UseInsecureHashAlgorithms=true;
Switch.System.Security.Cryptography.Pkcs.UseInsecureHashAlgorithms=true" />
But this may not always work, especially for web applications, as you can read in this blog post, fortunately along with the answer as well. It is only necessary to add some lines in the Application_Start
protected void Application_Start(object sender, EventArgs e)
{
[...]
AppContext.SetSwitch("Switch.System.Security.Cryptography.Xml.UseInsecureHashAlgorithms", true);
AppContext.SetSwitch("Switch.System.Security.Cryptography.Pkcs.UseInsecureHashAlgorithms", true);
}
Note that I use SHA512 but SHA256 will work with the below examples:
"Invalid algorithm specified" Took me forever to figure out and I tried practically everything.
Step 1 - the certificate has to be SHA512 and use a CSP (Cryptographic Service Provider) that is SHA512 Capable. Here is a list of CSPs and their capabilities. If you look for SHA512 you'll find the "Microsoft Enhanced RSA and AES Cryptographic Provider". By default generating certificates don't use this (at least in Windows) so you have to specify it when you create the certificate.
If you create the certificate with openssl, you can use the option -CSP below to set the correct CSP that will make it work. If you have an existing pfx, you can convert it to a PEM file with openssl, and then back to a pfx to add the option.
Create private key and certificate - this step will ask you questions, state, region etc etc.
openssl req -x509 -nodes -sha512 -newkey rsa:2048 -keyout 512key.pem -out 512cert.pem -days 3650
Create PFX file to import into your certificate store using the Microsoft Enhanced RSA and AES Cryptographic Provider:
openssl pkcs12 –export –in 512cert.pem –inkey 512key.pem –CSP “Microsoft Enhanced RSA and AES Cryptographic Provider” –out 512pfx.pfx
Step 2 : Props to Gonzalo Gallotti for posting the link to the piece of code that helped me. I commented up my code to show what each step is doing. NOTE: this code won't work without a properly generated certificate as described in step 1
public void GetCertificate() {
// Get the Machine Cert Store
var store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
string alg = CryptoConfig.MapNameToOID("SHA512");
// Open the cert store
store.Open(OpenFlags.ReadWrite);
// Loop through each certificate within the store
foreach (X509Certificate2 myCert in store.Certificates)
{
// Get the certificate we are looking for
if (myCert.IssuerName.Name.Contains("CN=YourSite"))
{
// Check if the certificate has a private key
if (myCert.HasPrivateKey)
{
// Get your custom signature as a string
string mySignature = GetSignatureString();
// Convert signature to byte array
byte[] originalData = Encoding.UTF8.GetBytes(mySignature);
// Create RSA provider from private key
RSACryptoServiceProvider rsaProvider = (RSACryptoServiceProvider)myCert.PrivateKey;
// Sign the signature with SHA512
byte[] signedSignature = signedSignature = rsaProvider.SignData(originalData, alg);
if (rsaProvider.VerifyData(originalData, alg, signedSignature))
{
// Signature is verified Do Stuff
}
else
{
throw new Exception("The data does not match the signature.");
}
}
}
}
}
Which CSP your certificate is using can be checked with certutil tool (on windows)
certutil yourCertificate.p12
Examples:
Microsoft Enhanced Cryptographic Provider v1.0 => throws error with C# 4.7 and above
Microsoft Enhanced RSA and AES Cryptographic Provider => works
Having a similar issue but just resolved it. If you are not using X509 but just the plain RSACryptoServiceProvider to get the keys, then only SHA1 is supported.
You can set the AppContext switches in a web config via appSettings:
<appSettings>
<add key="AppContext.SetSwitch:Switch.System.Security.Cryptography.Xml.UseInsecureHashAlgorithms" value="true" />
<add key="AppContext.SetSwitch:Switch.System.Security.Cryptography.Pkcs.UseInsecureHashAlgorithms" value="true" />
</appSettings>
I fixed the issue by upgrading my dependencies.
Instead of relying on the GAC version I was previously using for years, I switched to the latest NuGet packages (v16.8.0) of:
Microsoft.Build.Tasks.Core
Microsoft.Build.Utilities.Core
Microsoft.Build.Framework
This fixed this issue for us.

Unable to cast error using Box-created RSA key with Box C# SDK project

Our organization recently adopted Box to replace Windows shared folders. The small development group I'm a part of will need to upload and download files from at least one Box share and so we are just getting started with the API.
Our group currently uses Visual Studio 2013 to develop in C# with a preferred target of .NET 4.0. The current version of the SDK (2.15.6) isn't compatible with our platform but looking at prior versions it appears we should be able to leverage version 2.12.1 with our existing platform. The SDK includes a solution with C# methods to access the API calls and a solution with sample code.
Presently I'm trying to get the sample code to consume my credentials and authenticate to the server.
Within the Box developer tools I have created an application and used the built-in Box configuration option to generate a Public/Private keypair which is presented as a JSON file.
Where I'm getting hung up is when I try to use the keypair with the Box.V2.Samples.JWTAuth project. I copied the private key out of the JSON file, replaced the \n's with actual carriage returns, and pasted it into the private_key.pem file:
Private Key
I placed the other values in app.config and started the console app, which produces an Unable to cast error:
Error message
How can I get from a BouncyCastle RsaPrivateCrtKeyParamaters object to a BouncyCastle AsymmetricCipherKeyPair object?
Edit
I was able to track down the problematic code block. From SDK\Box.V2.JWTAuth\BoxJWTAuth.cs:
var pwf = new PEMPasswordFinder(this.boxConfig.JWTPrivateKeyPassword);
AsymmetricCipherKeyPair key;
using (var reader = new StringReader(this.boxConfig.JWTPrivateKey))
{
key = (AsymmetricCipherKeyPair)new PemReader(reader, pwf).ReadObject();
}
var rsa = DotNetUtilities.ToRSA((RsaPrivateCrtKeyParameters)key.Private);
this.credentials = new SigningCredentials(new RsaSecurityKey(rsa), SecurityAlgorithms.RsaSha256Signature, SecurityAlgorithms.Sha256Digest);
From my reading it seems like the code is anticipating having to extract the Private key from a Public/Private pair which isn't what Box is supplying (at least, not currently) and what I'm supplying appears to already be of the RsaPrivateCrtKeyParameters type so this cast may (no longer be) necessary.
It's not clear from your question whether you are trying to write code to do this, or trying to configure a tool, but programmatically, it would be:
RSAPrivateCrtKeyParameters priv = ...;
RSAKeyParameters pub = new RSAKeyParameters(false, priv.getModulus(), priv.getPublicExponent());
AsymmetricCipherKeyPair kp = new AsymmetricCipherKeyPair(pub, priv);

Veryifying RSA signature using openssl_public_decrypt failing for .NET signed string

I am attempting to use X509Certificate2 and RSACryptoServiceProvider together with PHP OpenSSL commands to implement two-way mutual security.
It all seems to be fine sending a message from PHP to C#, RSACryptoServiceProvider on the .NET server can decrypt the message using it's own private key and verify the signature using the PHP server's public key.
But going the other way, .NET to PHP, is causing trouble using 'openssl_public_decrypt' to verify the signature of the .NET server. It just returns false. 'openssl_private_decrypt' works fine to decrypt the encrypted data with the PHP server's private key.
I created the RSA (2048)self-signed certificates for both servers using openssl (.key and .crt) and then created a .pfx to use as private key in .NET code.
I am wondering what is the problem here. I am using the default settings for the encryption/decryption on both the .NET and PHP servers. Are the encryption/decryption mechanisms not the same on the case when you have encrypted something with a .pfx? Or does openssl_public_decrypt expect a different encryption from what RSACryptoServiceProvider gives you?
Any help would be great, as I'm not really sure where the issue lies.
openssl_public_decrypt is pretty anal about the format of the public key and doesn't support as many padding schemes as openssl_private_decrypt does.
My recommendation: Use phpseclib, a pure PHP RSA implementation. An example:
<?php
include('Crypt/RSA.php');
$rsa = new Crypt_RSA();
$rsa->loadKey('...'); // public key
$plaintext = '...';
$rsa->setEncryptionMode(CRYPT_RSA_ENCRYPTION_PKCS1);
$ciphertext = $rsa->encrypt($plaintext);
$rsa->loadKey('...'); // private key
echo $rsa->decrypt($ciphertext);
?>
It supports a ton more key formats than OpenSSL does, from XML Signature formatted keys, to PuTTY keys, etc.

X509 Certificate with Subject UID

I am loading a certificate from string like this:
public static void Test()
{
byte[] arrayCertificate;
arrayCertificate = Convert.FromBase64String("CERT_STRING");
X509Certificate2 clientCertificateFromXml = new X509Certificate2(arrayCertificate);
Console.Write(clientCertificateFromXml);
Console.ReadKey();
}
But this certificate doesn't have a "Subject Unique Identifier"
Take a look at this:
http://en.wikipedia.org/wiki/X.509 (The part of Structure of a certificate)
And I want to know how can I read that value from my .NET code (I looked that I can get SerialNumber, Thumbprints and others but there is no Subject UID anywhere).
Also, I will really appreciate If anyone can share an openssl command to include this UID for the certificate :-) (pfx one)
And I want to know how can I read that value from my .NET code
IIRC this is not exposed in the .NET BCL, either from X509Certificate or the newer (better but still incomplete) X509Certificate2.
But you can use Mono.Security assembly (or just the code you want from it), from the Mono project. It's open source, MIT.X11 licensed and it includes it's own X509Certificate.
This version expose just about everything in X.509 certificates, including a SubjectUniqueIdentifier property.
I will really appreciate If anyone can share an openssl command to include this UID for the certificate
I do not recall for openssl... but you can use the X509CertificateBuilder from Mono.Security to create your own certificates. See Mono's makecert tool source code for an example.
Disclaimer: I wrote the code :-)

Why am I getting "Invalid algorithm specified" exception

Here is my code.
X509Certificate pXCert = new X509Certificate2(#"keyStore.p12", "password");
RSACryptoServiceProvider csp = (RSACryptoServiceProvider)pXCert.PrivateKey;
string id = CryptoConfig.MapNameToOID("SHA256");
return csp.SignData(File.ReadAllBytes(filePath), id);
On the last line I'm getting the exception:
System.Security.Cryptography.CryptographicException "Invalid algorithm specified."
What am I doing wrong?
UPDATE:
id = 2.16.840.1.101.3.4.2.1
For dot net framework 4.7.0 or higher is not taking the sha1 so configure the below in application start. it's worked fine for me.
AppContext.SetSwitch("Switch.System.Security.Cryptography.Xml.UseInsecureHashAlgorithms", true);
AppContext.SetSwitch("Switch.System.Security.Cryptography.Pkcs.UseInsecureHashAlgorithms", true);
There is no issue with .NET code or the CSP code you provided.
Your problem is that CSP just doesn’t support SHA 256. You can get further information here
You might have come here while you are migrating your application from .NET Framework 4.7 and earlier versions to 4.7.1 or later versions.
If you are getting the exception System.Security.Cryptography.CryptographicException: Invalid algorithm specified., the reason is that default SignedXML and SignedXMS algorithms changed to SHA256 for applications that target the .NET Framework 4.7.1 and later versions (from Microsoft .NET migration guide)
In that guide you'll also find the solution:
For applications that target the .NET Framework 4.7.1 and later versions, if the use of SHA256 is undesirable, you can restore the default to SHA1 by adding the following configuration switch to the runtime section of your app config file:
<AppContextSwitchOverrides value="Switch.System.Security.Cryptography.Xml.UseInsecureHashAlgorithms=true;
Switch.System.Security.Cryptography.Pkcs.UseInsecureHashAlgorithms=true" />
But this may not always work, especially for web applications, as you can read in this blog post, fortunately along with the answer as well. It is only necessary to add some lines in the Application_Start
protected void Application_Start(object sender, EventArgs e)
{
[...]
AppContext.SetSwitch("Switch.System.Security.Cryptography.Xml.UseInsecureHashAlgorithms", true);
AppContext.SetSwitch("Switch.System.Security.Cryptography.Pkcs.UseInsecureHashAlgorithms", true);
}
Note that I use SHA512 but SHA256 will work with the below examples:
"Invalid algorithm specified" Took me forever to figure out and I tried practically everything.
Step 1 - the certificate has to be SHA512 and use a CSP (Cryptographic Service Provider) that is SHA512 Capable. Here is a list of CSPs and their capabilities. If you look for SHA512 you'll find the "Microsoft Enhanced RSA and AES Cryptographic Provider". By default generating certificates don't use this (at least in Windows) so you have to specify it when you create the certificate.
If you create the certificate with openssl, you can use the option -CSP below to set the correct CSP that will make it work. If you have an existing pfx, you can convert it to a PEM file with openssl, and then back to a pfx to add the option.
Create private key and certificate - this step will ask you questions, state, region etc etc.
openssl req -x509 -nodes -sha512 -newkey rsa:2048 -keyout 512key.pem -out 512cert.pem -days 3650
Create PFX file to import into your certificate store using the Microsoft Enhanced RSA and AES Cryptographic Provider:
openssl pkcs12 –export –in 512cert.pem –inkey 512key.pem –CSP “Microsoft Enhanced RSA and AES Cryptographic Provider” –out 512pfx.pfx
Step 2 : Props to Gonzalo Gallotti for posting the link to the piece of code that helped me. I commented up my code to show what each step is doing. NOTE: this code won't work without a properly generated certificate as described in step 1
public void GetCertificate() {
// Get the Machine Cert Store
var store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
string alg = CryptoConfig.MapNameToOID("SHA512");
// Open the cert store
store.Open(OpenFlags.ReadWrite);
// Loop through each certificate within the store
foreach (X509Certificate2 myCert in store.Certificates)
{
// Get the certificate we are looking for
if (myCert.IssuerName.Name.Contains("CN=YourSite"))
{
// Check if the certificate has a private key
if (myCert.HasPrivateKey)
{
// Get your custom signature as a string
string mySignature = GetSignatureString();
// Convert signature to byte array
byte[] originalData = Encoding.UTF8.GetBytes(mySignature);
// Create RSA provider from private key
RSACryptoServiceProvider rsaProvider = (RSACryptoServiceProvider)myCert.PrivateKey;
// Sign the signature with SHA512
byte[] signedSignature = signedSignature = rsaProvider.SignData(originalData, alg);
if (rsaProvider.VerifyData(originalData, alg, signedSignature))
{
// Signature is verified Do Stuff
}
else
{
throw new Exception("The data does not match the signature.");
}
}
}
}
}
Which CSP your certificate is using can be checked with certutil tool (on windows)
certutil yourCertificate.p12
Examples:
Microsoft Enhanced Cryptographic Provider v1.0 => throws error with C# 4.7 and above
Microsoft Enhanced RSA and AES Cryptographic Provider => works
Having a similar issue but just resolved it. If you are not using X509 but just the plain RSACryptoServiceProvider to get the keys, then only SHA1 is supported.
You can set the AppContext switches in a web config via appSettings:
<appSettings>
<add key="AppContext.SetSwitch:Switch.System.Security.Cryptography.Xml.UseInsecureHashAlgorithms" value="true" />
<add key="AppContext.SetSwitch:Switch.System.Security.Cryptography.Pkcs.UseInsecureHashAlgorithms" value="true" />
</appSettings>
I fixed the issue by upgrading my dependencies.
Instead of relying on the GAC version I was previously using for years, I switched to the latest NuGet packages (v16.8.0) of:
Microsoft.Build.Tasks.Core
Microsoft.Build.Utilities.Core
Microsoft.Build.Framework
This fixed this issue for us.

Categories

Resources