Why does X509Certificate2 sometimes fail to create from a blob? - c#

I have an ASP.NET web service which is receiving a byte array representing the contents of a .pfx file containing an X.509 certificate. The server-side code is using the System.Security.Cryptography.X509Certificate2 constructor to load the certificate from the bytes:
X509Certificate2 native_cert = new X509Certificate2(
pkcs12_buf /*byte array*/,
password,
X509KeyStorageFlags.PersistKeySet |
X509KeyStorageFlags.Exportable
);
Depending on who my service process is running as, this call will either succeed, or fail with an "internal error" exception. The last call on the exception stack is to X509Utils._LoadCertFromBlob, which is unmanaged code in mscore.dll.
This code succeeds when run from a console application in an interactive login using the service account's credentials. It fails when running under w3wp.exe in an application pool that uses the service account's credentials. Changing the app pool identity to an administrator fixes the problem, so it must be a privilege issue, but I have no idea what privilege could be necessary for this. The code does not touch either the filesystem or the Windows certificate stores.
[UPDATE: More Info]
This error appears in the Windows Event Log:
*Cryptographic Parameters:*
**Provider Name:** Microsoft Software Key Storage Provider
**Algorithm Name:** Not Available.
**Key Name:** {E182E13B-166D-472A-A24A-CBEF0808E9ED}
**Key Type:** User key.
*Cryptographic Operation:*
**Operation:** Open Key.
**Return Code:** 0x2
Any ideas?

I just found the solution to this one myself:
X509KeyStorageFlags flags = X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.MachineKeySet;
X509Certificate2 cert = new X509Certificate2(pkcs12_buf, password, flags);
The trick here is to use the local key store MachineKeySet flag instead of the user profile key store, which is the default if you don't specify an alternative location. Because the ASP.NET process identity doesn't load the user profile store, you can't access the store when importing a certificate programmatically, but you can access the machine store.
I think PersistKeySet just keeps the private key loaded, but I'm not sure exactly what it does - it's required if you need to access the private key for some reason though.

Try granting the ASP.NET account permissions to the following folder: C:\Documents And Settings\All Users\Microsoft\Crypto\RSA\MachineKeys\ (may vary according to your environment)

Related

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.

Can't Get Current User Certificate From X.509 Store

I am Developing Asp.net application in Which I Want to Read Current user Certificate From x509Store...Its Working Fine For my Local Machine .. When I Deploy application on IIS...than I Can't Pick Current user Certificate which I will Use for application authentication...
This Is Following Code for Working Fine for Local System..
public X509Certificate2 selectCert(StoreName store, StoreLocation Location , string windowTitle, string windowMsg)
{
X509Certificate2 certSelected = null;
X509Store x509Store = new X509Store(store);
x509Store.Open(OpenFlags.ReadWrite);
X509Certificate2Collection col = x509Store.Certificates;
X509Certificate2Collection sel = X509Certificate2UI.SelectFromCollection(col, windowTitle, windowMsg, X509SelectionFlag.SingleSelection);
if (sel.Count > 0)
{
X509Certificate2Enumerator en = sel.GetEnumerator();
en.MoveNext();
certSelected = en.Current;
}
x509Store.Close();
return certSelected;
}
Here x509Store.Certificates Return Some Collection of Current User Certificate..
But when I Run Deployed application In ISS. x509Store.Certificates Give Empty Collection at Sever...For Current User & I've checked my personal store (via certmgr.mmc) and I'm sure that I have the certificates...Crucial Problem Please Solve Out...I can't Understand .. What is happened...
Thanx
Your IIS app must be running under the ASP .net built in account in IIS, which does not have permission to access the certificate stores.
One way to solve it is to change the user to some other wihch has permissions, but it will lead to security issues.
A better approach but more complex is to have a service executed with elevated privileges which accepts incomming TCP/UDP/pipes connections from local machine, retrieves the certificates and pass them out, in this way you can call safely that service from your asp.net page without giving it elevated permissions
If you have installed the certificate as CurrentUser, only services running under your username will have access.
In order to access the certificate for all usernames in a machine, you will have to install the certificate as LocalMachine and retrieve the same using LocalMachine
for more info read

How do I access X.509 certificates stored in a service account?

I'm trying to digitally sign a PDF document using Syncfusion PDF 10.4, like so:
PdfLoadedDocument document = new PdfLoadedDocument(inputStream);
PdfCertificate certificate = PdfCertificate.FindBySubject(certificateStoreType, certificateSubjectName);
PdfSignature signature = new PdfSignature(document, document.Pages[0], certificate, "Signatur");
signature.Bounds = new RectangleF(new PointF(5, 5), new SizeF(100, 100));
This works great for my local user account after installing a suitable certificate using MMC (adding the Certificates snap-in for My user account and storing it in Personal), but not for a service (choosing Service account this time, and picking my service). Running the same code results in no suitable certificate being found, i.e. certificate is null. Furthermore, PdfCertificate.GetCertificates() throws an AccessViolationException, which I assume is a bug on Syncfusion's end.
I can, however, reproduce the same problem without Syncfusion code:
var store = new System.Security.Cryptography.X509Certificates.X509Store("My");
store.Open(System.Security.Cryptography.X509Certificates.OpenFlags.ReadOnly);
foreach (var item in store.Certificates)
{
…
}
Run as my own user, the certificate shows up (as do all the others shown in MMC under Personal), but if I debug the service (by running it, then invoking System.Diagnostics.Debugger.Launch()), I only get a "CN=LOCAL SERVICE" certificate, which doesn't show up in MMC at all.
I'm assuming that I need to A) tell it to open the correct certificate store, or B) change something about the way the service is installed or run, such as giving it a different identity, enabling UserInteraction, etc. Currently, it runs using LocalService and with UserInteraction disabled.
From what I remember, Windows machine accounts (like LocalService) use the machine certificate store. This means that in your code, you have to access the store with StoreLocation.LocalMachine.
var store =
new System.Security.Cryptography.X509Certificates.X509Store(StoreLocation.LocalMachine);
Note that if you decide to run the service under specific identity, you should rather first login as the identity, then import the certificate to the Personal store and then, use StoreLocation.CurrentUser.
The answer appears to be that .NET doesn't support accessing service account certificate stores without P/Invoke or the like:
I don't think that any of the .NET APIs allow access to the Services Certificate store.
However, you can install the certificate into the CurrentUser store of the account that the service runs under.
I've changed the service to run under its own user (which doesn't need admin rights), ran mmc.exe as that user using runas, and imported the certificate to that user's personal store.
I ran into this problem, and to solve it had to allow the "Local Service" account to access the "Local Computer" certificate store using the tool "WinHttpCertCfg"
It is described in detail here:
https://support.microsoft.com/en-us/help/901183/how-to-call-a-web-service-by-using-a-client-certificate-for-authentication-in-an-asp-net-web-application

WCF Error : 'It is likely that certificate 'my cert' may not have a private key that is capable of key exchange

I have a WCF service I'm trying to host on our production web server (IIS6). I've set the web up and tied our cert to the web. When I try to browse to the service url, I receive the following error in the event log :
The exception message is: It is likely that certificate
'CN=.mydomain, OU=Secure Link SSL Wildcard, OU=I.T., C=US' may not
have a private key that is capable of key exchange or the process may
not have access rights for the private key. Please see inner exception
for detail.. ---> System.ArgumentException: It is likely that
certificate 'CN=.mydomain.com, OU=Secure Link SSL Wildcard,
OU=I.T., O=mydomain, C=US' may not have a private key that is capable
of key exchange or the process may not have access rights for the
private key. Please see inner exception for detail. --->
System.Security.Cryptography.CryptographicException: The handle is
invalid.
I've confirmed ASP.Net 1.1, 2, and 4 are all set to 'Allow' in 'Web Service Extensions'. I've also confirmed the cert is set up in iis and it shows 'You have a private key that corresponds to this certificate'. Also, Execute Permissions are set to 'Script and Executables'.
I had this problem, and it turned out that the account the service was running under did not have permissions to access the certificate's private key.
Here are the steps I used to solve it:
Start the Cetificate manager. Do this by running MMC, activate [File]-[Add/Remove Snap-in...], then add "Certificates", selecting "Computer Account" and "Local Computer" in the ensuing wizard dialogs.
In the certificate manager, right-click on the relevant certificate and activate [All Tasks]-[Manage Private Keys]
This gives you a permissions window. Click Add
Add the account name or group that this service runs under.
Seems like your certificate was created for signatures and not key exchange, what I suppose to be normal for SSL certificates.
If you look at the makecert documentation, you can see that the -sky switch lets you specify whether the certificate should be used for signatures or key exchange. You can try to create a self-signed certificate with type exchange and test whether the exception still occurs. Don't forget to put the self-signed certificate into the machine's trusted root certification authority folder in order to avoid exceptions that the certificate is not valid.
Ensure also that the account name or group that needs to access the certificate ALSO has access to the folder hierarchy that the certificate resides in. If your certificate is hiding in, for example, 'C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys', and the account that needs to access it is 'NETWORK SERVICE', then 'NETWORK SERVICE' needs access to that full path. Just assigning rights to the file is not enough.
Same problem here. To fix the problem I added the following line to the <system.web> node of the web.config.
<httpRuntime targetFramework="4.7.2"/>
I had this issue today and it was on a server cloned from another server. I had to uninstall the certs, reinstall the certs, and grant access to the certs (same manner as described in accepted answer).

Help with SAML error: "Keyset does not exist"

We have two environments that should be identical but one of them raises an error when we try to generate a SAML message signature. I haven't looked at SAML before and I am not exactly sure what it tries to do
Part of the code:
X509Certificate2 x509Certificate = (X509Certificate2)Application[ASP.global_asax.IdPX509Certificate];
try
{
SAMLMessageSignature.Generate(samlResponse, x509Certificate.PrivateKey, x509Certificate);
}
catch (Exception ex)
{
app = File.AppendText(#"C:\SAML.txt");
app.WriteLine(ex.Message.ToString());
app.Flush();
app.Close();
}
The exception message is
Keyset does not exist
Does anyone have any idea of what I should be looking at?
Thanks in advance.
Hi please check the following on your setup.
Set the correct access control entries, ACLs, to the certificate you installed.
Add Modify access role for NETWORK SERVICE to the certificate.
If you are using Windows 2008 and Windows 7, you can access the private key from the certificate
snap-in in the MMC.
If it still did not work, add Modify access role also for IIS_IUSRS.
Hope it will help you.
Thank you!
Check to see if the certificate stored in the HttpApplicationState object's key ASP.global_asax.IdPX509Certificate was loaded successfully. If the certificate is being loaded from a PFX file, ensure that it is present on the disk and accessible by the account your web app is running under. If the certificate is being loaded from a certificate store, ensure that it is installed in the correct store and that the account your web app is running under can access the certificate.
You can use winhttpcertcfg.exe to install certificates into system keystores and manage certificate ACLs. The KB article http://support.microsoft.com/kb/901183 contains some additional info.

Categories

Resources