Adding a private key to X509Certificate2 - C# - c#

I'm new to 2WaySSL.
What I'm trying to do:
1. Retrieve a Client certificate which is stored in a certificate store,
I'm managing to do that by such code:
certificates = store.Certificates.Find(X509FindType.FindBySerialNumber, serialNumber, false);
When attaching the certificate to the request, the authentication by the server fails,
since I the certificate object does not contain the private key.
I'm trying to find a way of associating the private key to the certificate (in c#),
Once I'll retrieve the key from where it's stored.
something like:
certificate.PrivateKey = key;
But I found no easy way of either initiating the key object, or assigning it to the certificate without getting some exception, even when the key is null, I'm getting an access denied exception.
Any help , especially followed by a code sample, would be appreciated.

AFter .NET 4.7.2, If you have the certificate with only public key and an RSA private key handy you can perform the below code
certificate= certificate.CopyWithPrivateKey(key);
you have to import System.Security.Cryptography.X509Certificates;

You have 2 easy options
import certificate with private key (usually a pfx file) to certificate store. Then store.Certificates.Find will retrieve the certificate with private key.
Load pfx file into X509Certificate2 istance with one of its constructors new X509Certificate2(pfx_filename, password_to_pfx)

Related

Certificate private key permissions in .NET 6

I'm trying to import a certificate with private key into the Windows Certificate Store. I can successfully import the certificate using the below
X509Certificate2 certificate = new(certByteArray, certPassword, X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet);
X509Store store = new(StoreName.TrustedPeople, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadWrite);
store.Add(certificate);
But the problem I've got is, how to give a user access to the private key programmatically.
I've found these links helpful:
https://www.pkisolutions.com/accessing-and-using-certificate-private-keys-in-net-framework-net-core/
CngKey Assign permission to machine key
Set Certificate PrivateKey Permissions in .NET 5
I can grant access via the UI with certlm.msc > Drag certificate to Personal store > Right click certificate > All Tasks > Manage private keys > Add the user and permission
But I need to do this programmatically
There are changes from .NET Full Framework which is where the examples come from. I've spent more than a day on it, tried multiple certificates, certificate is definitely marked as exportable and running VS as administrator. I'm happy with a Windows only solution
This is about as close as I've got
const string NCRYPT_SECURITY_DESCR_PROPERTY = "Security Descr";
const CngPropertyOptions DACL_SECURITY_INFORMATION = (CngPropertyOptions)4;
X509Store trustedPeopleStore = new(StoreName.TrustedPeople, StoreLocation.LocalMachine);
trustedPeopleStore.Open(OpenFlags.ReadWrite);
var certificates = trustedPeopleStore.Certificates.Find(X509FindType.FindByThumbprint, "xxxxxxxxxxxxxxxxxxxxxx", false);
RSA rsa = certificates[0].GetRSAPrivateKey();
RSACng rsaCng = rsa as RSACng;
CngProperty prop = rsaCng.Key.GetProperty(NCRYPT_SECURITY_DESCR_PROPERTY, DACL_SECURITY_INFORMATION);
I can see the rsaCng.Key present in debug, but it fails on the next line (it definitely is exportable) getting the property with
Internal.Cryptography.CryptoThrowHelper.WindowsCryptographicException: 'Key not valid for use in specified state.'
I've also read comments that you shouldn't try setting the acl directly on the file, but not sure if that is correct or not
See this code project post for some example code that grants access programmatically (specifically look at the "AddAccessToCertificate" method).
Check this for more info: Programmatically adding certificate to personal store

Unable to set the private key in .NET Core

We have stored our Digi certificates having a private key in HashiCorp Vault. It contains thee things: certificate, name, private key.
I am trying to read the data and return new x509 certificate using a .NET Core standard project:
var crypto = new RSACryptoServiceProvider(parms);
crypto.FromXmlString(Encoding.UTF8.GetString(Convert.FromBase64String(certificationvalue.Data.PrivateKey)));
x509.PrivateKey = crypto; // getting error that operation is not allowed on this platform.
How can I fix this error?

Does GetECDsaPrivateKey return handle to private key in TPM?

We are generating certificates that are supposed to have an ECDSA key stored on the TPM. My understanding is that the GetECDsaPrivateKey extension method will load a handle to the algorithm for the private key in the TPM. Is this true? The certs we are testing with keep returning null and I need to know if they are simply being created incorrectly or if the test code is bad. The MSDN method description isn't very helpful in this case.
Yes, if it was told to look there.
On Windows, .NET's X509Certificate2 is backed by the Windows certificate APIs. Windows certificates have properties (in addition to their in-certificate attributes), and one of the properties identifies where the private key lives (in effect, the arguments to CngKey.Open) -- if the property is missing (and the "I have already opened the private key [here]" property is also missing) then the cert "has no private key" (cert.HasPrivateKey == false).
If your certificate is part of a Windows Certificate Store, and viewing that store via MMC shows the private key icon, then GetECDsaPrivateKey() can open it (assuming the key wasn't deleted after the fact, and that it's an ECDsa-capable key).
If you load the certificate from a .cer file, or some other means, then the cert doesn't know that there's a private key available, and it never searches for it. If you know how to find the private key and open it into an ECDsa object (e.g. ECDsaCng) then you can (in-memory) associate them by X509Certificate2 certWithKey = cert.CopyWithPrivateKey(key);. At that point, having been told where the private key lives, the new object's GetECDsaPrivateKey() works as expected.
That GetECDsaPrivateKey only gets the key if it's part of the certificate. To use a key from the TPM, you need to use the CngKey.Open() method with the name of the key and the storage provider.

Identityserver fails to load selfsigned certificate

I'm trying to set a Certificate for identityserver and it keeps failing with a "no access to private key error".
Taking it out of identityserver, the following code throws an access denied error
static X509Certificate2 GetCertificateFromDisk()
{
using (var stream = File.Open(#"patht-to-pfx", FileMode.Open))
{
var cert = new X509Certificate2(ReadStream(stream), "password", X509KeyStorageFlags.MachineKeySet);
return cert;
}
}
When running the code as administrator it works fine, not when running it under my own account. Eventually I want to run it as localsystem.
I even added 'Everyone' under the certificates private key permissions in my local computer certificate store,
screenprint here
... still I get the exception.
What is wrong here? Going Crazy about it
Update
Great tips from CryptoGuy in the answer below. Important note: Opening the file is not correct only Identityserver3 still failed when getting the certificate from the store. What made it work was to regenerate the certificate using Keith Sparkjoy's tool SerfCert. My previous certificate was generated using powershell. So keep in mind that powershell certificates have issues with accessibility of private key. Thanks to Keith for the tool!
There are few things to consider.
1) you are performing write access to Local Machine store. X509KeyStorageFlags.MachineKeySet attempts to save private key to Local Machine store. Therefore, you need administrator permissions to write there. You should remove this flag to perform read-only access
2)
Documentation says that adding permissions in MMC (manage private key-option on a certificate) should allow this, but it doesn't seem to work
it works on an already saved private keys.
What you really should do is to import certificate and private key to Local Machine store and then configure your application to reference installed certificate.
3) if your application runs under unpriveleged account and the key don't need to be shared, then you should use Current User store.

Cannot import certificate pfx with private key to store from .net c# 3.5 on windows server 2012

I am attempting to load a certificate with private key from a pfx file and import it to the LocalMachine/My (Personal) certificate store. My code works fine except that when I view the certificate in the store it says
"The associated private key cannot be found"
and further, certutil says
"Cannot find the certificate and private key for decryption"
The strange part is that my code works fine on Windows 7 development box but not on Windows Server 2008 R2 or 2012. Also strange that if I manually import the pfx file using mmc, the private key seems to persist properly.
Here is the code I am using to load the file and import :
// load the certificate from pfx file
X509Certificate2 cert = new X509Certificate2(filePath, pfxPassword, X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet);
// import to the store
X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadWrite); //tried MaxAllowed as well
store.Add(caCert);
store.Close();
Any ideas? For background info, I am also generating the certificate in code at an earlier step using BouncyCastle. I ran into some problems persisting the private key during that step but was solved with the answer from this question. Also the code that is attempting the import is running as administrator.
A little time away from this problem helped me refine my investigation. I tracked down the private key file for the imported certificate in C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys
and found that the file had no owner and no permissions set (not even for Administrators or System). Modifying these permissions manually caused the "associated private key" message to go away when viewing the cert in the store.
That led me to modify my code to set permissions on the private key before attempting to import it into the store :
https://stackoverflow.com/a/4902009/332610
hooray!

Categories

Resources