Help with SAML error: "Keyset does not exist" - c#

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.

Related

The LDAP Server is Unavailable using PrincipalContext and ADLDS

We are making use of ADLDS for our user management and authentication. We can successfully query the instance without problems. However, trying to perform an operation such as SetPassword will fail or even trying to create a new user if a password is not set, it fails. I can successfully update a user as long as its not password I'm trying to update. I've been reading a lot of different articles relating to this but not finding a resolution. Posting to see if I can get some fresh perspective on this issue, thanks for any input.
EXAMPLE
ContextType ctxType = ContextType.ApplicationDirectory;
string server = "myadldsserver.com";
string usersCN = "CN=Users,..."; // container where users reside
ContextOptions ctxOpts = ContextOptions.SimpleBind;
string uname = "myuser";
string pswrd = "mypass";
using(var ctx = new PrincipalContext(ctxType, server, usersCN, ctxOpts, uname, pswrd)
using(var newUser = new UserPrincipal(ctx)) {
newUser.Name = "newusername";
newUser.Enabled = true;
newUser.UserPrincipalName = "newusername";
newUser.Save();
newUser.SetPassword("newuserpassword");
}
ERROR 1
The first problem I encounter if I try to create a new UserPrincipal and call Save without having set the password like in Example above I get the exception A constraint violation occurred. with an InnerException extend message of 0000052D: AtrErr: DSID-033807D7, #1:0: 0000052D: DSID-033807D7, problem 1005 (CONSTRAINT_ATT_TYPE), data 2246, Att 9005a (unicodePwd)
Because of this error I tried moving the SetPassword before calling Save along with other approaches I found online such as getting the DirectoryEntry from the UserPrincipal and trying to call SetPassword but got a different error.
ERROR 2
Calling SetPassword before calling UserPrincipal.Save, when save is called, results in the error The directory property cannot be found in the cache.
Note that the same error will occur if I trying calling ResetPassword or getting a DirectoryEntry and calling Invoke("SetPassword"... as well
ERROR 3
From my research most seem to indicate this could have to do with needing to access AD LDS using a Secure connection. So, I changed my server to include the port of 636 string server = "myadldsserver.com:636" and I changed the ContextOptions to be ContextOptions.SimpleBind | ContextOptions.SecureSocketLayer.
Making these changes when the PrincipalContext is being constructed I get the following exception The server could not be contacted. with an inner exception of The LDAP server is unavailable., HResult is -2146233087
JAVA and LDP
To add some background to this, we do have similar code written in an older Java application. We are trying to port some of this logic over to .NET side in C#. The code in Java makes use of a Java keystore that contains the certificate that was generated on the AD LDS server. The Java application of course has no issues using the SSL port. We know the server seems to be configured correctly, it's just an issue of how to access it from .NET side.
Is there an equivalent on the .NET side such as the keystore in Java? We know that an SSL connection can be made to server. We have verified this using LDP as well.
GOALS
Be able to create a new user and set their password during creation
Be able to ResetPassword or ChangePassword for a user
Connect to our AD LDS instance from .NET securely
Have you tried using Microsoft Management Console to import the certificate?
Two ways to install the certificate
Either
Open a cmd.exe console and type "MMC"
File > Add/Remove Snap-In...
Select Certificates, click Add
Choose Computer Account and Local Computer when prompted, then OK...
Certificates should now be showing under Console Root
Certificates > Trusted Root Certification Authorities > Certificates > (right-click) > All Tasks > Import Certificate...
Find the certificate you want to import, click Next and choose defaults (Trusted Root Certification Authorities should already be
selected)
Click Next, Finish
(or)
Simply double-click on the .cer file for the certificate in Windows
Explorer, click Install Certificate... > Next > select the option to
"Place all certificates in following store" > Browse... > Select
Trusted Root Certification Authorities. Continue with next until done.
At this point your certificate is installed, and you should be able to communicate securely with your ADLDS server.

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.

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).

Why does X509Certificate2 sometimes fail to create from a blob?

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)

Categories

Resources