Get list of X509Certificate from cert store C# MVC - c#

I'm trying to get the list of certificates from cert store. This is the code I'm using from this post Get list of certificates from the certificate store in C#:
X509Store store = new X509Store(StoreName.My);
store.Open(OpenFlags.ReadOnly);
foreach (X509Certificate2 mCert in store.Certificates)
{
// TODO
}
When I run this code from Test Explorer is finding all available certificates, but when I run it on my MVC application is not returning any certificate.
I'm running VS 2013 as administrator.
Could you please address me what I'm doing wrong?
EDIT:
When I'm running the code on IIS Express I'm getting the list of certificates, but when I run it on Local IIS I'm not getting any results.
Regards,

Most of the time, you want to check the machine store certs, not the ones for your current user. To do that:
X509Store store = new X509Store(StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
foreach (X509Certificate2 certificate in store.Certificates)
{
// TODO
}
This gives you a consistent list, regardless of the IIS user.

If you're attempting to accept certificates from a user, IIS needs to be correctly configured to use HTTPS and accept SSL from the client. You won't be able to go from IIS Express and, let's say, IIS 8.0 without making a few changes in your code.
Check out the top rated answer in How do I get the X509Certificate sent from the client in web service? for the IIS code.
For IIS Express, you can't configure the SSL settings so if you want to pseudo-grab x509 attributes you can do so from the local store. It looks like that's what you're doing right now, which won't work on your local IIS because ApplicationPoolIdentity isn't privileged to access the certificate store.

May be you can try this.
X509Store store = new X509Store(StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly);
foreach (X509Certificate2 mCert in store.Certificates)
{
// TODO
}
You can iterate the store location and the certifications that exist on your computer by using the example provided on this link X509Store Class

Related

C# X509Store Access Web Hosting

I have an SSL cert stored in the Web Hosting Folder of the Certificate Store. I cannot seem to be able to access this store from C#. Does anyone know how to do this?
X509Store store = new X509Store("Web Hosting");
store.Open(OpenFlags.ReadOnly);
var t = store.Certificates.GetEnumerator();
while (t.MoveNext())
{
//this is always empty
}
Additional Detail
I need this cert for a gRPC service that I am writing. gRPC requires a certificate for the SSL connection. In the mean time aka development I am using Let's Encrypt to generate the certificate. When the cert was generated the cert was put into the Web Hosting folder of the cert store.
It turns out you can drag and drop the certs to a different location in the cert manager. I relocated the cert to the Personal folder and I was able to access it by:
X509Store store = new X509Store(StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);
Try removing the space; e.g., try the name as webhosting (no space)
In PowerShell, to display certs in this store location:
dir cert:\localmachine\webhosting

can't use certificates installed in the store as server-side certificates

I'm developing an application in UWP which acts as a server and i made a self-signed certificate and installed it on both the server and client, I checked the certificate if it has a private key and it does, and when I retrieve it from the store programmatically it loads the private key, but for some reason when the authentication handshake starts the server says that "the credentials supplied to the package were not recognized", the only way it works is when I get the certificate as a PFX from a folder...
this one works:
Selectedcert = new X509Certificate2("LocalCertificate.pfx","password", X509KeyStorageFlags.UserKeySet);
this doesn't:
X509Store store = new X509Store(StoreName.Root,StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly);
X509Certificate2Collection cers =
store.Certificates.Find(X509FindType.FindByIssuerName, "localhost", false);
X509Certificate2 Selectedcert = null;
foreach (var c in cers)
if (c.HasPrivateKey && c.PrivateKey != null)
Selectedcert = c;
I checked the manifest and the application has Capabilities to the Shared User Certificates also I checked the certificate permissions and the user does have permissions to read the private-key
Edit:
also i have Tried both StoreName.My and StoreName.Root
i tried the same code in a console application and the authentication handshake is ok, so the problem is with the UWP restrictions, it doesn't let the application to use the private key or something like that
i dont know how to get an X509Cert in UWP if someone knows please inform me, thank you :)
I think the issue is that UWP apps are running in the sandbox so that it has limitations when trying to accessing the localhost. So in your scenario, you could not get the certificates.
Update:
The sharedUserCertificates works for UWP APIs like UserCertificateStore Class and CertificateStores Class. You could check the official sample -UserCertificateStore to see how these APIs could get the certificates from the certificates store.

C#: How to invoke a SOAP service requiring client-side authentication with certificates installed at runtime

I have an application deployed to IIS that needs to invoke a SOAP service. It's using WCF from .NET Framework. That SOAP service requires that requests made be authenticated with a client-side certificate which is given at runtime. Admin users of the application can update the used certificate in a back-office. The goal is for autonomy and the certificate lifecycle management be independent from IIS or the underlying system so using the machine certificate store is not an option.
Here's the initial code:
var binding = new BasicHttpBinding(BasicHttpSecurityMode.Transport);
var client = new ServiceReference1.myClient(binding, new EndpointAddress(serviceUrl));
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Certificate;
var certificate = new X509Certificate2(certificateBinary, certificatePassword);
client.ClientCredentials.ClientCertificate.Certificate = certificate;
//use the client
var result = client.myMethod(new ServiceReference1.MethodRequest());
certificateBinary is the result of loading a PFX file containing the full certificate chain (client certificate, intermediate and root CAs) and certificatePassword the password used to create that file.
But the request is rejected by the server. From looking at Wireshark, it seems only the client-certificate is sent. This is different from what happens if we install the PFX on the machine store which works fine.
So the next step I tried was to install the certificates at runtime. First load them:
X509Certificate2Collection collection = new X509Certificate2Collection();
try {
collection.Import(ssCertificateFile, ssPassword, X509KeyStorageFlags.UserKeySet | X509KeyStorageFlags.PersistKeySet);
}
Then identify what kind of certificates they are and finally installing them on the current user store:
private static void InstallCertificates(X509Certificate2Collection clientCerts, X509Certificate2Collection intermediateCAs, X509Certificate2Collection RootCAs) {
using (X509Store personalStore = new X509Store(StoreName.My, StoreLocation.CurrentUser)) {
personalStore.Open(OpenFlags.ReadWrite);
personalStore.AddRange(clientCerts);
}
using (X509Store intermediateStore = new X509Store(StoreName.CertificateAuthority, StoreLocation.CurrentUser)) {
intermediateStore.Open(OpenFlags.ReadWrite);
intermediateStore.AddRange(intermediateCAs);
}
using (X509Store trustedCAsStore = new X509Store(StoreName.Root, StoreLocation.CurrentUser)) {
trustedCAsStore.Open(OpenFlags.ReadWrite);
trustedCAsStore.AddRange(rootCAs);
}
}
This fails when installing the root CAs in trusted root certificate authorities (StoreName.Root) with:
System.Security.Cryptography.CryptographicException: The request is not supported.
at System.Security.Cryptography.X509Certificates.X509Store.Add(X509Certificate2 certificate)
at System.Security.Cryptography.X509Certificates.X509Store.AddRange(X509Certificate2Collection certificates)
at OutSystems.NssCertificationExtremeXP.CssCertificationExtremeXP.InstallCertificates(CertificatesClassifier certificates)
so only the client certificate and the intermediate CAs get installed and at runtime apparently this is not enough.
But if I take the exact same code as it is and run it with in a separate C# project, when installing the root CAs there's a confirmation dialog
and if I click OK the certificate is installed.
From here and here, it looks like every time we want to install something in the user Trusted Root Certificate Authorities, that prompt happens and it probably is not supported on the context of a non-GUI usage.
The problem is that even if I don't install the root CA in the store, I can successfully call the SOAP service when running this stand-alone app, only when running under IIS this fails.
Does anyone know why this happens and how to solve it?

Dynamically adding a website with an HTTPS binding pointing to a dynamically generated self-signed certificate

I'm currently attempting to dynamically add a new website using the Microsoft.Web.Administration library provided by IIS7+. This is going to be a part of an installation process in which a self-signed certificate needs to be added and bound to the HTTPS binding of this website.
My research took me to this post on StackOverflow which makes use of the BouncyCastle API: Generate self signed certificate on the fly. I have tried to replicate the functionality provided by IIS administration tool for creating such a certificate by making slight alterations to the code. I have modified the signature algorithm in both methods from SHA256WithRSA to SHA1WithRSA. I am also passing the SubjectName and IssuerName as the local machine's name using Dns.GetHostEntry("127.0.0.1").HostName. I'm also setting the FriendlyName property of the X509Certificate2 object. The final modification involved passing the sl parameter of the addCertToStore method as StoreLocation.LocalMachine.
The following is a snippet of the code for dynamically creating the website and adding an HTTPS binding passing the cert.GetCertHash() data as a parameter to the Site.Bindings.Add(...) method which takes in the Binding Information, Certificate Hash and the Certificate Store Name (see MSDN documentation):
using (ServerManager iisManager = new ServerManager())
{
Site testSite = iisManager.Sites.Add(siteName, targetDir, sitePortNumber);
testSite.Bindings.Add("*:" + sitePortNumber + ":", certificateHash, "MY");
iisManager.CommitChanges();
}
The issue is that when attempting to commit the changes in IIS I get the following COMException:
A specified logon session does not exist. It may already have been terminated. (Exception from HRESULT: 0x80070520)
Checking what happened in IIS it seems that the certificate is present but it is not being mapped correctly:
In the image linked above, the topmost certificate is the one being added dynamically i.e. the one mentioned in my post. The last one is a sample self-signed certificate created through the IIS administration tool.
The image linked above shows that the mapping between the HTTPS binding and the newly created SSL certificate failed.
Personal Certificates under Local Computer -> Personal:
This image contains a list of personal certificates which are present in certificate manager under the Local Computer section. The top one is the dynamically generated one whilst the bottom one was created using the IIS administration tool.
Above are the certificate details of the self-signed certificates created using the IIS administration tool (left hand side) and the dynamically generated one (right hand side).
Some of the most notable differences between the two of them are:
Missing Key Usage and Enhanced Key Usage details in the dynamically generated one.
The certificate status and the icon indicating that there is something wrong with it.
So the question is what is wrong with the code involving the creation of the self-signed certificate which is causing the mapping to the new IIS website to fail?
Thanks
Moved the SSL certificate from current user to local machine and it worked.
Here is my code to add the certificate.
I also noticed X509KeyStorageFlags.MachineKeySet was the only flag that did not produce the error.
string certPath = "c:/test2/certName.p12";
string certPass = "TestPassword";
// Create a collection object and populate it using the PFX file
X509Certificate2Collection collection = new X509Certificate2Collection();
collection.Import(certPath, certPass, X509KeyStorageFlags.MachineKeySet);
foreach (X509Certificate2 cert in collection)
{
if(cert.Subject.Equals("CN=TestServer, O=Test"))
{
X509Store store1 = new X509Store(StoreName.Root, StoreLocation.LocalMachine);
store1.Open(OpenFlags.ReadWrite);
store1.Add(cert);
store1.Close();
}
if (cert.Subject.Equals("CN=TestClient, OU=Applications, O=Test"))
{
X509Store store1 = new X509Store(StoreName.My, StoreLocation.LocalMachine);
store1.Open(OpenFlags.ReadWrite);
store1.Add(cert);
store1.Close();
}
}
And here is my code to create the binding
ServerManager serverManager = new ServerManager();
var site = serverManager.Sites["Default Web Site"];
site.Bindings.Add("*:443:TestClient", certificate[0].GetCertHash(), "MY");
// site.Bindings.Add("*:443:TestClient", "https");
serverManager.CommitChanges();

Can't make BITS upload work with client certificate authentication

I have an IIS extension for BITS with upload enabled on the server (Win2008) and a C# .NET4 client running as a windows service on the client machine (Win8.1)
The BITS upload without the certificate auth is working fine.
I have generated a self-signed root cert, added it to TrustedCA storage on both machines, generated child client and server certs, added them to the respective storages. Both are displayed as valid and trusted in the MMC console.
However, when I try to use client cert authentication (setting "Require client certificate" in the IIS and adding the certificate to the BITS job on the client), the client certificate does not seem to be received by IIS.
The code I'm using to set the cert on client is:
var httpOptions = (IBackgroundCopyJobHttpOptions) job2;
httpOptions.SetClientCertificateByName(certificate.certLocation, certificate.certStoreName, certificate.certSubjectName);
The certLocation is
BG_CERT_STORE_LOCATION.BG_CERT_STORE_LOCATION_LOCAL_MACHINE
since the client certificate is installed into LocalMachine and the service is running as LocalSystem.
Additionally, right after I have called SetClientCertificateByName, I try to get it back:
string stName, stLocation;
var hash = new IntPtr();
httpOptions.GetClientCertificate(out loc, out stName, hash, out stLocation)
But it returns empty values.
I have tried to check the certificate presence via:
var store = new X509Store(certificate.certStoreName, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);
var certs = store.Certificates.Find(X509FindType.FindBySubjectName, certificate.certSubjectName, true)
and the certificate is indeed there.
I have also tried using WinHttpCertCfg.exe (no errors, but nothing changed) and FindPrivateKey.exe (says "Unable to obtain private key file name") to grant the access to the cert's private key to the LocalSystem account.
I'm out of ideas on this one. Any insight would be very appreciated.

Categories