C# Decrypt emails by reading keys on smart card - c#

One of my client needs a tool to decrypt their emails.
They are using smart card for encryption/decryption.
As of now they are using a tool which asks them to enter their PUBLIC KEY. After giving a valid PUBLIC KEY, combination of PUBLIC and PRIVATE KEYS on SMART CARD will help them to decrypt their emails.
We are working on a tool which could be a windows service(which runs in the background) and decrypt all the emails with our prompting the user for public key again and again.
We are using C# to develop this tool and from the blogs I got to know that we need to Microsoft CSP to achieve this functionality. I think the below code will be able to use the smart card container and read the private key. But I am not sure how to provide the public key to this and decrypt the email.
CspParameters csp = new CspParameters(1, "Microsoft Base Smart Card Crypto Provider");
// CspParameters csp = new CspParameters(1, "Microsoft Strong Cryptographic Provider");
csp.Flags = CspProviderFlags.UseDefaultKeyContainer;
// Initialize an RSACryptoServiceProvider object using
// the CspParameters object.
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(csp);
Can anybody correct me if I am in a wrong direction? And also please shed some light to proceed further.

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.

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

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.

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.

How to read credentials from a SmartCard in c#

In my organization, users must use SmartCard for interactive login to a Windows stations (95,Vista and 7). almost daily, we need to read the credentials stored in the SmartCard and compaire them with the ActiveDirectory, without implementing a custom credentials manager. The fields we compare are: userPrincialName and sAMAccountName.
Can you please show me a code that demonstrates how to read the credentials from the SmartCard or guide me to an article / code on the internet?
A search over internet suggeted implementing credentials manager or using other languages (like C, C++).
Also, I came across this article : http://www.codeproject.com/Articles/17013/Smart-Card-Framework-for-NET written by orouit, which is a framework for working with SmartCards - but I think this too much for my simple task. What do you think?
Well if developing under windows, once you insert smart card windows will fetch all certificates from the smart card place them to the My certificate store.
var smartCardCerts = new List<X509Certificate2>();
var myStore = new X509Store(StoreName.My, StoreLocation.CurrentUser);
myStore.Open(OpenFlags.ReadOnly);
foreach(X509Certificate2 cert in myStore.Certificates)
{
if( !cert.HasPrivateKey ) continue; // not smartcard for sure
var rsa = cert.PrivateKey as RSACryptoServiceProvider;
if( rsa==null ) continue; // not smart card cert again
if( rsa.CspKeyContainerInfo.HardwareDevice ) // sure - smartcard
{
// inspect rsa.CspKeyContainerInfo.KeyContainerName Property
// or rsa.CspKeyContainerInfo.ProviderName (your smartcard provider, such as
// "Schlumberger Cryptographic Service Provider" for Schlumberger Cryptoflex 4K
// card, etc
var name = cert.Name;
rsa.SignData(); // to confirm presence of private key - to finally authenticate
}
}
basically a lot of crypto API is available via .NET nowdays. But you could also use API directly Crypto API
for example you could access smart card directly via
CryptAcquireContext(&hProv,"\\.\<Reader Name>\<Container Name>",...)
where reader name is card reader name and container name is whatever rsa.KeyContainerName in code snippet above. There are multiple ways to access information like that and Crypto API is not very consistent or straightforward. as a hint .NET version of CryptAcquireContext is RSACryptoServiceProvider with CspParameters where you can specify container name if needed.
Well finding user in ActiveDirectory may be done via System.DirectoryServices.DirectoyEntry and System.DirectoryServices.DirectorySearcher, but do not forget System.DirectoryServices.ActiveDirectory.Forest and related API which makes some things a lot easier to figure out.
You would be able to get

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