Create RSA public key and private key - c#

Im trying to make a SHA1withRSA sign/verify function on ASP.NET core using BouncyCastle library, 1st of all I need to make a keypair, I am using this page to generate the key:
https://travistidwell.com/jsencrypt/demo/
However from BouncyCastle example code I found (C# Sign Data with RSA using BouncyCastle), from the Public and Private key text generated, I couldn't make the key files, which as I understand, would be a .CER file for public key and .PEM for private key.
So could you please suggest me a way to make the .CER and .PEM file? Also, I haven't found a complete example about signing SHA1withRSA using BouncyCastle library - or just core C#, I'd be so grateful if you could suggest me a complete example, thank you so much.

The linked website generates private keys in PKCS#1 format and public keys in X.509/SPKI format, each PEM encoded.
.NET Core only supports the import of PKCS#1 and X.509 keys as of version 3.0. For .NET Core 2.2, the easiest way is to apply BouncyCastle. For loading the PEM keys the PemReader of BouncyCastle can be used.
Option 1:
With BouncyCastle's DotNetUtilities class, RSAParameters instances can be derived and thus RSACryptoServiceProvider instances:
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.OpenSsl;
using Org.BouncyCastle.Security;
...
public static RSACryptoServiceProvider GetRSACryptoServiceProviderFromPem(string pem, bool isPrivate)
{
PemReader pemReader = new PemReader(new StringReader(pem));
object key = pemReader.ReadObject();
RSAParameters rsaParameters = isPrivate ?
DotNetUtilities.ToRSAParameters((RsaPrivateCrtKeyParameters)(((AsymmetricCipherKeyPair)key).Private)) :
DotNetUtilities.ToRSAParameters((RsaKeyParameters)key);
RSACryptoServiceProvider rsaKey = new RSACryptoServiceProvider();
rsaKey.ImportParameters(rsaParameters);
return rsaKey;
}
RSACryptoServiceProvider in turn has methods for signing/verifying:
RSACryptoServiceProvider privateCSP = GetRSACryptoServiceProviderFromPem(privateKey, true);
RSACryptoServiceProvider publicCSP = GetRSACryptoServiceProviderFromPem(publicKey, false);
byte[] dataToSign = Encoding.UTF8.GetBytes("The quick brown fox jumps over the lazy dog");
byte[] signature = privateCSP.SignData(dataToSign, HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1);
bool verified = publicCSP.VerifyData(dataToSign, signature, HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1);
Option 2:
Alternatively, the RsaPrivateCrtKeyParameters and the RsaKeyParameters classes of BouncyCastle can be used directly:
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.OpenSsl;
using Org.BouncyCastle.Security;
...
public static AsymmetricKeyParameter GetAsymmetricKeyParameterFromPem(string pem, bool isPrivate)
{
PemReader pemReader = new PemReader(new StringReader(pem));
object key = pemReader.ReadObject();
return isPrivate ? ((AsymmetricCipherKeyPair)key).Private : (AsymmetricKeyParameter)key;
}
as well as classes for signing and verifying provided by BouncyCastle's SignerUtilities:
RsaPrivateCrtKeyParameters privateKeyParameters = (RsaPrivateCrtKeyParameters)GetAsymmetricKeyParameterFromPem(privateKey, true);
RsaKeyParameters publicKeyParameters = (RsaKeyParameters)GetAsymmetricKeyParameterFromPem(publicKey, false);
ISigner signer = SignerUtilities.GetSigner("SHA1withRSA");
signer.Init(true, privateKeyParameters);
byte[] dataToSign = Encoding.UTF8.GetBytes("The quick brown fox jumps over the lazy dog");
signer.BlockUpdate(dataToSign, 0, dataToSign.Length);
byte[] signature = signer.GenerateSignature();
signer.Init(false, publicKeyParameters);
signer.BlockUpdate(dataToSign, 0, dataToSign.Length);
bool verified = signer.VerifySignature(signature);
Console.WriteLine(verified);
Both implementations are executable under .NET Core 2.2.

Related

C# - How to Decrypt an Encrypted Private Key with Bouncy Castle

I have a private key that was generated by running:
openssl req -new -sha384 -x509 -days 63524 -subj "/C=CA/ST=State/L=City/O=Org/CN=Org-CA" -extensions v3_ca -keyout Org-CA.key -out Org-CA.pem -passout pass:pass1234
I need to use this private key and certificate to sign client CSRs. I used to use OpenSSL to do this, but I need to do this in C# instead.
I've found examples of what I want to do online, but those examples are using bouncy castle and an un-encrypted private key.
The key file looks like this:
-----BEGIN ENCRYPTED PRIVATE KEY-----
....
-----END ENCRYPTED PRIVATE KEY-----
And I'm trying to load it by doing this:
var privatekey = File.ReadAllBytes(#"Org-CA.key");
var rsaKeyParameters = PrivateKeyFactory.DecryptKey("pass1234".ToArray(), privatekey);
But thats not working. I get an error saying Wrong number of elements in sequence (Parameter 'seq')'
Is this how I'm supposed to be decrypting and loading the private key?
Depending on your .NET version, you may not need BouncyCastle at all. As of .NET Core 3.1 there is RSA.ImportEncryptedPkcs8PrivateKey() for DER encoded encrypted private PKCS#8 keys and as of .NET 5.0 there is even RSA.ImportFromEncryptedPem() for PEM encoded encrypted keys.
Otherwise with C#/BouncyCastle the import of an encrypted private PKCS#8 key is available e.g. with:
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.OpenSsl;
...
string encPkcs8 = #"-----BEGIN ENCRYPTED PRIVATE KEY-----
...
-----END ENCRYPTED PRIVATE KEY-----";
StringReader stringReader = new StringReader(encPkcs8);
PemReader pemReader = new PemReader(stringReader, new PasswordFinder("<your password>"));
RsaPrivateCrtKeyParameters keyParams = (RsaPrivateCrtKeyParameters)pemReader.ReadObject();
...
with the following implementation of the IPasswordFinder interface:
private class PasswordFinder : IPasswordFinder
{
private string password;
public PasswordFinder(string pwd) => password = pwd;
public char[] GetPassword() => password.ToCharArray();
}
If necessary, a conversion from keyParams to System.Security.Cryptography.RSAParameters is possible with:
using Org.BouncyCastle.Security;
...
RSAParameters rsaParameters = DotNetUtilities.ToRSAParameters(keyParams);
which can be imported directly e.g. with RSA.ImportParameters().
Edit:
After digging through the C#/BouncyCastle source code, your way is also possible (which I didn't know before).
DecryptKey() has several overloads, one of which can handle the DER encoded encrypted key as already suspected by Maarten Bodewes in his comment. The latter can be easily generated with the Org.BouncyCastle.Utilities.IO.Pem.PemReader() class.
Note that this PemReader is different from the one in the first implementation (different namespaces), which is why I use the namespace explicitly in the following code snippet. With this approach, the RsaPrivateCrtKeyParameters instance can be generated as follows:
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Crypto.Parameters;
...
StringReader stringReader = new StringReader(encPkcs8);
Org.BouncyCastle.Utilities.IO.Pem.PemReader pemReader = new Org.BouncyCastle.Utilities.IO.Pem.PemReader(stringReader);
Org.BouncyCastle.Utilities.IO.Pem.PemObject pem = pemReader.ReadPemObject();
RsaPrivateCrtKeyParameters keyParams = (RsaPrivateCrtKeyParameters)PrivateKeyFactory.DecryptKey("<your password>".ToCharArray(), pem.Content);
...
pem.Content contains the DER encoded encrypted private PKCS#8 key.

How to create RSACryptoServiceProvide using string private key

I only have a private key. i want to use it for signing data in RSA algorithm.
i know how to use certificate to sign data. but i don't any way to use private key in string format to create and use RSACryptoServiceProvider
My code looks something like this.
string privateKey = "-----BEGIN PRIVATE KEY-----\nsometext\nsometext\nsometext\.....\n-----END PRIVATE KEY-----\n"
var rasAlg = new RSACryptoServiceProvider()// how can i use my private key here.
var signature = rasAlg.SignData(bytesArray, new SHA256CryptoServiceProvider());
EDIT
if any one can help me create X509Certificate then also it is fine.
i found bouncy castle library useful. i used bouncy castle pem reader to read it from string.
AsymmetricKeyParameter keyPair;
using (var reader = new StringReader(privateKey))
keyPair = (AsymmetricKeyParameter)new PemReader(reader).ReadObject();

Read DER private key in C# using BouncyCastle

I am trying to read an RSA private key into .Net using BouncyCastle to test data I have previously encrypted. The encrypted data is working fine using the public key and Bouncy Castle and I have also used the same private key as below (which is DER format) to successfully decrypt my data in a PHP application but I don't know why I can't create the private key in .Net to do the same thing:
byte[] privatekey = File.ReadAllBytes(#"C:\Users\Luke\privkey.der");
var rsaKeyParameters = (RsaKeyParameters)PrivateKeyFactory.CreateKey(privatekey);
The second line throws an exception:
"unknown object in factory: DerInteger\r\nParameter name: obj"
I also tried using a stream instead of a byte array and the same error occurs. The key pair was created using OpenSSL and as mentioned, decryption works in PHP using openssl_private_decrypt() and the same key as in the .Net code. I also tried a PEM format of the same key and that also didn't work (but I don't think BC supports PEM directly anyway)
Has anyone done this before? Thanks
The problem was that I had assumed PublicKeyFactory and PrivateKeyFactory were complimentary since they are in the same namespace. They are not!
To decode the private key, I needed the following alternative code:
var privKeyObj = Asn1Object.FromStream(privatekey);
var privStruct = new RsaPrivateKeyStructure((Asn1Sequence)privKeyObj);
// Conversion from BouncyCastle to .Net framework types
var rsaParameters = new RSAParameters();
rsaParameters.Modulus = privStruct.Modulus.ToByteArrayUnsigned();
rsaParameters.Exponent = privStruct.PublicExponent.ToByteArrayUnsigned();
rsaParameters.D = privStruct.PrivateExponent.ToByteArrayUnsigned();
rsaParameters.P = privStruct.Prime1.ToByteArrayUnsigned();
rsaParameters.Q = privStruct.Prime2.ToByteArrayUnsigned();
rsaParameters.DP = privStruct.Exponent1.ToByteArrayUnsigned();
rsaParameters.DQ = privStruct.Exponent2.ToByteArrayUnsigned();
rsaParameters.InverseQ = privStruct.Coefficient.ToByteArrayUnsigned();
var rsa = new RSACryptoServiceProvider();
rsa.ImportParameters(rsaParameters);
return Encoding.UTF8.GetString(rsa.Decrypt(Convert.FromBase64String(ciphertext), true));
A BIG thankyou to owlstead for their help.

Importing an Objective-C RSA public key into c# RSACryptoServiceProvider

Here is the Objective-C we are using to generate the RSA object using the following lib: https://github.com/kuapay/iOS-Certificate--Key--and-Trust-Sample-Project
BDRSACryptor *rsa = [[BDRSACryptor alloc] init];
BDRSACryptorKeyPair *RSAKeyPair = [rsa generateKeyPairWithKeyIdentifier:nil error:error];
We then pass RSAKeyPair.publicKey to our c#, where using the BouncyCastles library:
using (TextReader sr = new StringReader(pempublic))
{
var pemReader = new PemReader(sr);
var temp = (RsaKeyParameters)pemReader.ReadObject();
var RSAKeyInfo = new RSAParameters
{
Modulus = temp.Modulus.ToByteArray(),
Exponent = temp.Exponent.ToByteArray()
};
var rsaEncryptor = new RSACryptoServiceProvider();
rsaEncryptor.ImportParameters(RSAKeyInfo);
}
There are no errors, but the encryption is different. The same string encrypted in c# and obj-c are different, and we are unable to encrypt on one end and decrypt on the other.
Help!
Edit: Willing to consider any methodology of exchanging public keys between c# and obj-c. This is just the closest we have come so far.
Edit2: Contents of pempublic
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC/ugxekK+lY0VLeD8qA5nEhIn7IzBkgcrpiEM109chFxHobtvWEZbu8TqTIBtIgtISNp4idcEvahPniEyUawjmRSWB7uYmcHJ3pWaIo5/wBthmGrqS/XjedVXT6RuzaoPf9t0YXyW6YiH1kQZn4gjZF51O6iIk2+VnfkYVqeKBtQIDAQAB-----END PUBLIC KEY-----
Edit3: Regarding padding: C# and obj-c are both using OEAP padding.
Edit4: How the text is being encrypted: c#
byte[] testBytes = Encoding.UTF8.GetBytes("1234567890");
byte[] encryptedBytes = rsaEncryptor.Encrypt(testBytes, true);
string base64 = Convert.ToBase64String(encryptedBytes);
obj-c
NSString *encrypted = [rsa encrypt:#"1234567890" key:RSAKeyPair.publicKey error:error];
Final Edit:
Solved by using the Chilkat encryption library on the .NET server. We are now able to load an RSA encryptor from a public key in both XML and PEM format generated from a .NET, Java, or Objective-C Client. If anyone could explain why the .NET RSACryptoServiceProvider wouldn't work, we are all quite curious.
please check my answer to my own question
RSA C# encryption with public key to use with PHP openssl_private_decrypt(): Chilkat, BouncyCastle, RSACryptoServiceProvider
i think it may be helpful
to make it short, try using temp.Modulus.ToByteArrayUnsigned()
I wrote RSA and AES implementation using CommonCrypto, implementation is done in order to be interoperable with .NET
Check it out
https://github.com/ozgurshn/EncryptionForiOS
I used base64 encoding
.NET side could be
public string RsaDecryption(byte[] cipherText, string privateKey)
{
var cspDecryption = new RSACryptoServiceProvider();
cspDecryption.FromXmlString(privateKey);
var bytesPlainTextData = cspDecryption.Decrypt(cipherText, false);
return Encoding.UTF8.GetString(bytesPlainTextData);
}
public byte[] RsaEncryption(string plainText, string publicKey)
{
var cspEncryption = new RSACryptoServiceProvider();
cspEncryption.FromXmlString(publicKey);
var bytesPlainTextData = Encoding.UTF8.GetBytes(plainText);
var bytesCypherText = cspEncryption.Encrypt(bytesPlainTextData, false);
return bytesCypherText;
}

signing with x509certificate that has signature algorithm sha256 [duplicate]

My application will take a set of files and sign them. (I'm not trying to sign an assembly.) There is a .p12 file that I get the private key from.
This is the code I was trying to use, but I get a System.Security.Cryptography.CryptographicException "Invalid algorithm specified.".
X509Certificate pXCert = new X509Certificate2(#"keyStore.p12", "password");
RSACryptoServiceProvider csp = (RSACryptoServiceProvider)pXCert.PrivateKey;
string id = CryptoConfig.MapNameToOID("SHA256");
return csp.SignData(File.ReadAllBytes(filePath), id);
According to this answer it can't be done (the RSACryptoServiceProvider does not support SHA-256), but I was hoping that it might be possible using a different library, like Bouncy Castle.
I'm new to this stuff and I'm finding Bouncy Castle to be very confusing. I'm porting a Java app to C# and I have to use the same type of encryption to sign the files, so I am stuck with RSA + SHA256.
How can I do this using Bouncy Castle, OpenSSL.NET, Security.Cryptography, or another 3rd party library I haven't heard of? I'm assuming, if it can be done in Java then it can be done in C#.
UPDATE:
this is what I got from the link in poupou's anwser
X509Certificate2 cert = new X509Certificate2(KeyStoreFile, password");
RSACryptoServiceProvider rsacsp = (RSACryptoServiceProvider)cert.PrivateKey;
CspParameters cspParam = new CspParameters();
cspParam.KeyContainerName = rsacsp.CspKeyContainerInfo.KeyContainerName;
cspParam.KeyNumber = rsacsp.CspKeyContainerInfo.KeyNumber == KeyNumber.Exchange ? 1 : 2;
RSACryptoServiceProvider aescsp = new RSACryptoServiceProvider(cspParam);
aescsp.PersistKeyInCsp = false;
byte[] signed = aescsp.SignData(File.ReadAllBytes(file), "SHA256");
bool isValid = aescsp.VerifyData(File.ReadAllBytes(file), "SHA256", signed);
The problem is that I'm not getting the same results as I got with the original tool. As far as I can tell from reading the code the CryptoServiceProvider that does the actual signing is not using the PrivateKey from key store file. Is that Correct?
RSA + SHA256 can and will work...
Your later example may not work all the time, it should use the hash algorithm's OID, rather than it's name. As per your first example, this is obtained from a call to CryptoConfig.MapNameToOID(AlgorithmName) where AlgorithmName is what you are providing (i.e. "SHA256").
First you are going to need is the certificate with the private key. I normally read mine from the LocalMachine or CurrentUser store by using a public key file (.cer) to identify the private key, and then enumerate the certificates and match on the hash...
X509Certificate2 publicCert = new X509Certificate2(#"C:\mycertificate.cer");
//Fetch private key from the local machine store
X509Certificate2 privateCert = null;
X509Store store = new X509Store(StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);
foreach( X509Certificate2 cert in store.Certificates)
{
if (cert.GetCertHashString() == publicCert.GetCertHashString())
privateCert = cert;
}
However you get there, once you've obtained a certificate with a private key we need to reconstruct it. This may be required due to the way the certificate creates it's private key, but I'm not really sure why. Anyway, we do this by first exporting the key and then re-importing it using whatever intermediate format you like, the easiest is xml:
//Round-trip the key to XML and back, there might be a better way but this works
RSACryptoServiceProvider key = new RSACryptoServiceProvider();
key.FromXmlString(privateCert.PrivateKey.ToXmlString(true));
Once that is done we can now sign a piece of data as follows:
//Create some data to sign
byte[] data = new byte[1024];
//Sign the data
byte[] sig = key.SignData(data, CryptoConfig.MapNameToOID("SHA256"));
Lastly, the verification can be done directly with the certificate's public key without need for the reconstruction as we did with the private key:
key = (RSACryptoServiceProvider)publicCert.PublicKey.Key;
if (!key.VerifyData(data, CryptoConfig.MapNameToOID("SHA256"), sig))
throw new CryptographicException();
The use of privateKey.toXMLString(true) or privateKey.exportParameters(true) aren't usable in a secure environment, since they require your private key to be exportable, which is NOT a good practice.
A better solution is to explicitly load the "Enhanced" crypto provider as such:
// Find my openssl-generated cert from the registry
var store = new X509Store(StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);
var certificates = store.Certificates.Find(X509FindType.FindBySubjectName, "myapp.com", true);
var certificate = certificates[0];
store.Close();
// Note that this will return a Basic crypto provider, with only SHA-1 support
var privKey = (RSACryptoServiceProvider)certificate.PrivateKey;
// Force use of the Enhanced RSA and AES Cryptographic Provider with openssl-generated SHA256 keys
var enhCsp = new RSACryptoServiceProvider().CspKeyContainerInfo;
var cspparams = new CspParameters(enhCsp.ProviderType, enhCsp.ProviderName, privKey.CspKeyContainerInfo.KeyContainerName);
privKey = new RSACryptoServiceProvider(cspparams);
This is how I dealt with that problem:
X509Certificate2 privateCert = new X509Certificate2("certificate.pfx", password, X509KeyStorageFlags.Exportable);
// This instance can not sign and verify with SHA256:
RSACryptoServiceProvider privateKey = (RSACryptoServiceProvider)privateCert.PrivateKey;
// This one can:
RSACryptoServiceProvider privateKey1 = new RSACryptoServiceProvider();
privateKey1.ImportParameters(privateKey.ExportParameters(true));
byte[] data = Encoding.UTF8.GetBytes("Data to be signed");
byte[] signature = privateKey1.SignData(data, "SHA256");
bool isValid = privateKey1.VerifyData(data, "SHA256", signature);
I settled on changing the key file to specify the appropriate Crypto Service Provider, avoiding the issue in .NET altogether.
So when I create a PFX file out of a PEM private key and a CRT public certificate, I do it as follows:
openssl pkcs12 -export -aes256 -CSP "Microsoft Enhanced RSA and AES Cryptographic Provider" -inkey priv.pem -in pub.crt -out priv.pfx
The key part being -CSP "Microsoft Enhanced RSA and AES Cryptographic Provider".
(-inkey specifies the private key file and -in specifies the public certificate to incorporate.)
You may need to tweak this for the file formats you have on hand. The command line examples on this page can help with that:
https://www.sslshopper.com/ssl-converter.html
I found this solution here:
http://hintdesk.com/c-how-to-fix-invalid-algorithm-specified-when-signing-with-sha256/
Use can use this on more recent frameworks.
public byte[] GetSignature(byte[] inputData)
{
using (var rsa = this.signingCertificate.GetRSAPrivateKey())
{
return rsa.SignData(inputData, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
}
}
public bool ValidateSignature(byte[] inputData, byte[] signature)
{
using (var rsa = this.signingCertificate.GetRSAPublicKey())
{
return rsa.VerifyData(inputData, signature, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
}
}
The signingCertificate above is a X509Certificate2 with a private key. This method does not require you to import any existing keys and works in a secure environment.
When you use a certificate to get your RSACryptoServiceProvider it really matters what's the underlying CryptoAPI provider. By default, when you create a certificate with 'makecert', it's "RSA-FULL" which only supports SHA1 hashes for signature. You need the new "RSA-AES" one that supports SHA2.
So, you can create your certificate with an additional option: -sp "Microsoft Enhanced RSA and AES Cryptographic Provider" (or an equivalent -sy 24) and then your code would work without the key juggling stuff.
Here is how I signed a string without having to modify the certificate (to a Microsoft Enhanced RSA and AES Cryptographic provider).
byte[] certificate = File.ReadAllBytes(#"C:\Users\AwesomeUser\Desktop\Test\ServerCertificate.pfx");
X509Certificate2 cert2 = new X509Certificate2(certificate, string.Empty, X509KeyStorageFlags.Exportable);
string stringToBeSigned = "This is a string to be signed";
SHA256Managed shHash = new SHA256Managed();
byte[] computedHash = shHash.ComputeHash(Encoding.Default.GetBytes(stringToBeSigned));
var certifiedRSACryptoServiceProvider = cert2.PrivateKey as RSACryptoServiceProvider;
RSACryptoServiceProvider defaultRSACryptoServiceProvider = new RSACryptoServiceProvider();
defaultRSACryptoServiceProvider.ImportParameters(certifiedRSACryptoServiceProvider.ExportParameters(true));
byte[] signedHashValue = defaultRSACryptoServiceProvider.SignData(computedHash, "SHA256");
string signature = Convert.ToBase64String(signedHashValue);
Console.WriteLine("Signature : {0}", signature);
RSACryptoServiceProvider publicCertifiedRSACryptoServiceProvider = cert2.PublicKey.Key as RSACryptoServiceProvider;
bool verify = publicCertifiedRSACryptoServiceProvider.VerifyData(computedHash, "SHA256", signedHashValue);
Console.WriteLine("Verification result : {0}", verify);
According to this blog it should work with FX 3.5 (see note below). However it's important to recall that most of .NET cryptography is based on CryptoAPI (even if CNG is being more and more exposed in recent FX releases).
The key point is that CryptoAPI algorithm support depends on the Crypto Service Provider (CSP) being used and that varies a bit between Windows versions (i.e. what's working on Windows 7 might not work on Windows 2000).
Read the comments (from the blog entry) to see a possible workaround where you specify the AES CSP (instead of the default one) when creating your RSACCryptoServiceProvider instance. That seems to work for some people, YMMV.
Note: this is confusing to many people because all the released .NET frameworks includes a managed implementation of SHA256 which cannot be used by CryptoAPI. FWIW Mono does not suffer from such issues ;-)
I know this is an old thread but for those still stuck in the past and looking for an answer, the following worked for me based off #BKibler's answer. The comments stated it's not using the correct key and it's because the solution is missing a couple key settings.
// Find my openssl-generated cert from the registry
var store = new X509Store(StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);
var certificates = store.Certificates.Find(X509FindType.FindBySubjectName, "myapp.com", true);
var certificate = certificates[0];
store.Close();
// Note that this will return a Basic crypto provider, with only SHA-1 support
var privKey = (RSACryptoServiceProvider)certificate.PrivateKey;
// Force use of the Enhanced RSA and AES Cryptographic Provider with openssl-generated SHA256 keys
var enhCsp = new RSACryptoServiceProvider().CspKeyContainerInfo;
if (!Enum.TryParse<KeyNumber>(privKey.CspKeyContainerInfo.KeyNumber.ToString(), out var keyNumber))
throw new Exception($"Unknown key number {privKey.CspKeyContainerInfo.KeyNumber}");
var cspparams = new CspParameters(enhCsp.ProviderType, enhCsp.ProviderName, privKey.CspKeyContainerInfo.KeyContainerName)
{
KeyNumber = (int)keyNumber,
Flags = CspProviderFlags.UseExistingKey
};
privKey = new RSACryptoServiceProvider(cspparams);
You need to set both "KeyNumber" and "Flags" so the existing (non-exportable) key is used and you can use the public key from the certificate to verify.
I have noticed similar issues in .NET with the wrong private key being used (or was it flat-out errors? I do not recall) when the certificate I am working with is not in the user/computer certificate store. Installing it into the stored fixed the problem for my scenario and things started working as expected - perhaps you can try that.

Categories