Adding Certificate to Local Machine Store - c#

I've created a self signed certificate and I'm trying to place it in the Root, and Personal stores of the local machine through code.Previously the following code was working:
var cert = new X509Certificate2(PathToCert, Password, X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet);
if (cert != null)
{
var store = new X509Store(StoreName.Root, StoreLocation.LocalMachine);
store.Open(OpenFlags.MaxAllowed);
if (!store.Certificates.Contains(cert))
{
store.Add(cert);
}
store.Close();
var store1 = new X509Store(StoreName.My, StoreLocation.LocalMachine);
store1.Open(OpenFlags.MaxAllowed);
if (!store1.Certificates.Contains(cert))
{
store1.Add(cert);
}
store1.Close();
}
When I try to add the certificate to the stores (even when running as admin) it will not put them into the Local Machine Stores, it puts the certificate in my CURRENT USER store.
How do I make it put the certificate in the LOCAL MACHINE?

Related

Accessing Certificate Programatically for SAML2

Setup:
I have a .Net Framework WebForms app and just setup SAML2 using Sustainsys.Saml2 setup on Azure. I'm also using Git for version control (VC) and Azure Key Vault for App Settings and Connection Strings.
Question:
What are other ways I can store and access the certificate for SAML2, even if I can access it programmatically (I'm currently accessing/setting the certificate programmatically)?
I want to prevent adding certificates to our VC as well as adding it manually to the Azure directory.
I tried looking into Key Vault Certificates, but I'm unsure if I can even use this.
You can upload your certificate to your Azure App Service --> TSLSSL setting --> Private Key Certificates (.pfx).
Once uploaded there you can retrieve it programmatically via C# with the following:
public static X509Certificate2 GetX509Certificate2(string certThumbprint, bool validOnly = true, StoreName storeName = StoreName.My, StoreLocation storeLocation = StoreLocation.CurrentUser) {
using (var certStore = new X509Store(storeName, storeLocation)) {
var certCollection = certStore.Certificates.Find(X509FindType.FindByThumbprint, certThumbprint, validOnly);
// Get the first cert with the thumbprint
var cert = (X509Certificate2) certCollection.OfType<X509Certificate>().FirstOrDefault();
certStore.Close();
return cert;
}
}
Call it via:
var cert = GetX509Certificate2(certificateThumbprint, false, storeLocation: StoreLocation.LocalMachine);
Personally, so it can work locally, deployed and on any of our devs computers, I use the following code so that it can search different locations where it may be stored:
var cert = GetX509Certificate2(certificateThumbprint, false, storeLocation: StoreLocation.CurrentUser);
if (cert == null) {
cert = GetX509Certificate2(certificateThumbprint, false, storeLocation: StoreLocation.LocalMachine);
}

Unity load Certificate from Windows Store

I want to load a certificate from local windows certificate store.
With UWP my code looks like the following and I can fetch my certificate:
public static X509Certificate2 ClientCertificate()
{
X509Certificate2 cert = null;
X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
for (int i = 0; i < store.Certificates.Count; i++)
{
cert = store.Certificates[i];
if (cert.Thumbprint == "68c8e9abc4b950a88701047af543a94c61c9ae38".ToUpper())
{
break;
}
}
return cert;
}
When using Unity 2019.4 I get an error message in line store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);, saying:
System.Security.Cryptography.CryptographicException: Store My doesn\'t exists.
What am I doing wrong with my Unity-code that I cannot access certificates from local windows certificate store?

WebApi HttpClient not sending client certificate

I am trying to secure my RESTful WebApi service with ssl and client authentication using client certificates.
To test; I have generated a self signed certificate and placed in the local machine, trusted root certification authorities folder and i have generated a "server" and "client" certificates.
Standard https to the server works without issue.
However I have some code in the server to validate the certificate, this never gets called when I connect using my test client which supplies my client certificate and the test client is returned a 403 Forbidden status.
This imples the server is failing my certificate before it reaches my validation code.
However if i fire up fiddler it knows a client certificate is required and asks me to supply one to My Documents\Fiddler2. I gave it the same client certificate i use in my test client and my server now works and received the client certificate i expect!
This implies that the WebApi client is not properly sending the certificate, my client code below is pretty much the same as other examples i have found.
static async Task RunAsync()
{
try
{
var handler = new WebRequestHandler();
handler.ClientCertificateOptions = ClientCertificateOption.Manual;
handler.ClientCertificates.Add(GetClientCert());
handler.ServerCertificateValidationCallback += Validate;
handler.UseProxy = false;
using (var client = new HttpClient(handler))
{
client.BaseAddress = new Uri("https://hostname:10001/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/xml"));
var response = await client.GetAsync("api/system/");
var str = await response.Content.ReadAsStringAsync();
Console.WriteLine(str);
}
} catch(Exception ex)
{
Console.Write(ex.Message);
}
}
Any ideas why it would work in fiddler but not my test client?
Edit: Here is the code to GetClientCert()
private static X509Certificate GetClientCert()
{
X509Store store = null;
try
{
store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
store.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadOnly);
var certs = store.Certificates.Find(X509FindType.FindBySubjectName, "Integration Client Certificate", true);
if (certs.Count == 1)
{
var cert = certs[0];
return cert;
}
}
finally
{
if (store != null)
store.Close();
}
return null;
}
Granted the test code does not handle a null certificate but i am debugging to enssure that the correct certificate is located.
There are 2 types of certificates. The first is the public .cer file that is sent to you from the owner of the server. This file is just a long string of characters. The second is the keystore certificate, this is the selfsigned cert you create and send the cer file to the server you are calling and they install it. Depending on how much security you have, you might need to add one or both of these to the Client (Handler in your case). I've only seen the keystore cert used on one server where security is VERY secure. This code gets both certificates from the bin/deployed folder:
#region certificate Add
// KeyStore is our self signed cert
// TrustStore is cer file sent to you.
// Get the path where the cert files are stored (this should handle running in debug mode in Visual Studio and deployed code) -- Not tested with deployed code
string executableLocation = Path.GetDirectoryName(AppDomain.CurrentDomain.RelativeSearchPath ?? AppDomain.CurrentDomain.BaseDirectory);
#region Add the TrustStore certificate
// Get the cer file location
string pfxLocation = executableLocation + "\\Certificates\\TheirCertificate.cer";
// Add the certificate
X509Certificate2 theirCert = new X509Certificate2();
theirCert.Import(pfxLocation, "Password", X509KeyStorageFlags.DefaultKeySet);
handler.ClientCertificates.Add(theirCert);
#endregion
#region Add the KeyStore
// Get the location
pfxLocation = executableLocation + "\\Certificates\\YourCert.pfx";
// Add the Certificate
X509Certificate2 YourCert = new X509Certificate2();
YourCert.Import(pfxLocation, "PASSWORD", X509KeyStorageFlags.DefaultKeySet);
handler.ClientCertificates.Add(YourCert);
#endregion
#endregion
Also - you need to handle cert errors (note: this is BAD - it says ALL cert issues are okay) you should change this code to handle specific cert issues like Name Mismatch. it's on my list to do. There are plenty of example on how to do this.
This code at the top of your method
// Ignore Certificate errors need to fix to only handle
ServicePointManager.ServerCertificateValidationCallback = MyCertHandler;
Method somewhere in your class
private bool MyCertHandler(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors error)
{
// Ignore errors
return true;
}
In the code you are using store = new X509Store(StoreName.My, StoreLocation.LocalMachine);.
Client certificates are not picked up from LocalMachine, you should instead use StoreLocation.CurrentUser.
Checking MMC -> File -> Add or Remove Snap-ins -> Certificates -> My user account you will see the certificate that fiddler uses. If you remove it from My user account and only have it imported in Computer account you will see that Fiddler can not pick it up either.
A side note is when finding certificates you also have to address for culture.
Example:
var certificateSerialNumber= "‎83 c6 62 0a 73 c7 b1 aa 41 06 a3 ce 62 83 ae 25".ToUpper().Replace(" ", string.Empty);
//0 certs
var certs = store.Certificates.Find(X509FindType.FindBySerialNumber, certificateSerialNumber, true);
//null
var cert = store.Certificates.Cast<X509Certificate>().FirstOrDefault(x => x.GetSerialNumberString() == certificateSerialNumber);
//1 cert
var cert1 = store.Certificates.Cast<X509Certificate>().FirstOrDefault(x =>
x.GetSerialNumberString().Equals(certificateSerialNumber, StringComparison.InvariantCultureIgnoreCase));
try this.
Cert should be with the current user store.
Or give full rights and read from a file as it is a console application.
// Load the client certificate from a file.
X509Certificate x509 = X509Certificate.CreateFromCertFile(#"c:\user.cer");
Read from the user store.
private static X509Certificate2 GetClientCertificate()
{
X509Store userCaStore = new X509Store(StoreName.My, StoreLocation.CurrentUser);
try
{
userCaStore.Open(OpenFlags.ReadOnly);
X509Certificate2Collection certificatesInStore = userCaStore.Certificates;
X509Certificate2Collection findResult = certificatesInStore.Find(X509FindType.FindBySubjectName, "localtestclientcert", true);
X509Certificate2 clientCertificate = null;
if (findResult.Count == 1)
{
clientCertificate = findResult[0];
}
else
{
throw new Exception("Unable to locate the correct client certificate.");
}
return clientCertificate;
}
catch
{
throw;
}
finally
{
userCaStore.Close();
}
}

Find certificate location on box/server

I am trying to find where the certificate is stored on my local machine and then as well as our dev servers. I can go Run -> MMC -> File - > Add/Remove SnapIns and select certificates and Current User and see my personal certificates. However, I am trying to utilize this code for an HttpWebRequest and I cannot find the url.
string certPath = #"e:\mycertificate.cer"; //This Value
X509Certificate myCert = X509Certificate.CreateFromCertFile(certPath);
request.ClientCertificates.Add(myCert);
In another area we set up a proxy and do it like this.
proxy.ClientCredentials.ClientCertificate.SetCertificate(StoreLocation.LocalMachine, StoreName.My, X509FindType.FindBySubjectName, CertificateName);
So obviously a little different implementation and I am unsure as to where/how to find the location to fill in for the first example.
Solution that worked for me
public WebRequest GetWebRequest(string address)
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(address);
X509Certificate myCert = null;
X509Store store = new X509Store("My");
store.Open(OpenFlags.ReadOnly);
foreach (X509Certificate2 mCert in store.Certificates)
{
if (mCert.FriendlyName.Contains("certname"))
{
myCert = mCert;
}
}
if (myCert != null) { request.ClientCertificates.Add(myCert); }
return request;
}
Assuming like you want to pick a certificate somehow and not really care if it is from file or not. In this case you can use certificate store object and find one you need (i.e. by thumbprint). Check out this Get list of certificates from the certificate store in C# and MSDN article on X509Store.Certificates which contains sample too:
X509Store store = new X509Store("My");
store.Open(OpenFlags.ReadOnly);
foreach (X509Certificate2 mCert in store.Certificates){
//TODO's
}

How to retrieve all certificates in your X509Store

I am using the following code to retrieve all certificates in my PC from an asp.net webapp. The certificates collection is empty, and I can't understand why.
I tried impersonating my own user account and I didn't succeed as well. What am I doing wrong?
var store = new X509Store(StoreLocation.CurrentUser); //StoreLocation.LocalMachine fails too
var certificates = store.Certificates;
foreach (var certificate in certificates)
{
var friendlyName = certificate.FriendlyName;
Console.WriteLine(friendlyName);
}
//original problem: fetch a single certificate by its subject name
X509Certificate2 clientCertificate = CertificateUtility.GetCertificate(StoreName.My, StoreLocation.CurrentUser, "CN=mypc.domainname"); //returns null :(
Add this line of code to the second line and see how it works:
store.Open(OpenFlags.ReadOnly);
and then this at the bottom :):
store.Close();
All in one ...
I have an apache server (xamp) with https. I access through https and c# (vs2010)
to a PHP upload page
Install the certificate from i.e in the personal folder certificate, for example.
To view the certicates run "certmgr.msc" , at least in win7
Listing the personal certificates
var store = new X509Store(StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly);
var certificates = store.Certificates;
foreach (var certificate in certificates)
{
var friendlyName = certificate.FriendlyName;
var xname = certificate.GetName(); //obsolete
Console.WriteLine(friendlyName);
}
store.Close();
Find specific certificate
string certificateName = "CN=localhost"; //name found in the var xname
X509Store storex = new X509Store(StoreName.My, StoreLocation.CurrentUser);
storex.Open(OpenFlags.ReadOnly);
X509Certificate2Collection certificatesx =
storex.Certificates.Find(X509FindType.FindBySubjectName,
certificateName,
true);
X509Certificate certificatex = certificates[0];
storex.Close();
I can find certificates by ...
var certificateStore = new X509Store(StoreName.TrustedPeople, StoreLocation.LocalMachine);
certificateStore.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
var certificateCollection = certificateStore.Certificates.Find(X509FindType.FindBySubjectName, "mycert.me.com",false);
certificateStore.Close();
var certificate = certificateCollection[0];
certificateCollection will have the certificates I care about ... if it is just one then I get first element in the collection.
Look in your certificate store(mmc/add/certificate snap-in/my user account/Certificates - Current User/Personal/Certificates) to see the subject name to make sure "CN=mypc.domainname" is whats actually on the cert.
"CN=mypc.domainname"
vs
"CN = mypc.domainname"
...etc

Categories

Resources