Removing certificate from storage does not work - c#

I'm trying to remove certificate from storage and using this function:
public static void RemoveFromStorage(StoreName storeName, IEnumerable<CertInfo> certificates)
{
var store = new X509Store(storeName, StoreLocation.CurrentUser);
store.Open(OpenFlags.MaxAllowed | OpenFlags.IncludeArchived);
foreach (var cert in certificates)
{
var toRemove = store.Certificates.Find(X509FindType.FindByThumbprint, cert.Thumbprint, false);
store.Certificates.Remove(toRemove[0]);
}
store.Close();
}
Function doesn't throw any exception, Find function returns proper certificate from storage, but after calling Remove function it is not removed.
What am I doing wrong?

The store needs to be opened for ReadWrite.
Try this:
public static void RemoveFromStorage(StoreName storeName, IEnumerable<CertInfo> certificates)
{
var store = new X509Store(storeName, StoreLocation.CurrentUser);
store.Open(OpenFlags.MaxAllowed | OpenFlags.IncludeArchived | OpenFlags.ReadWrite);
foreach (var cert in certificates)
{
var toRemove = store.Certificates.Find(X509FindType.FindByThumbprint, cert.Thumbprint, false);
store.Certificates.Remove(toRemove[0]);
}
store.Close();
}

The problem was this line
store.Certificates.Remove(toRemove[0]);
Correct is:
store.Remove(toRemove[0]);

Related

how to install the certificate in certificate store without private key?

I have below 2 methods,
GetCaCertificate method reads the PFX certificate with has private key included.
AddCertToStore method add the certificate to the certificate store, here I want to add the certificate in the certificate store with-out Private key, how we can avoid it?
var rootCaCert = GetCaCertificate(#"C:\cert\ca-cert.pfx", "Password#123");
AddCertToStore(rootCaCert, StoreName.Root, StoreLocation.LocalMachine);
private static X509Certificate2 GetCaCertificate(string certPath, string password)
{
return new X509Certificate2(File.ReadAllBytes(certPath), password:password);
}
public static bool AddCertToStore(X509Certificate2 cert, StoreName storeName, StoreLocation storeLocation)
{
bool value;
try
{
using var store = new X509Store(storeName, storeLocation);
store.Open(OpenFlags.ReadWrite);
store.Add(cert);
store.Close();
value = true;
}
catch (Exception exception)
{
Console.WriteLine(exception);
value = false;
}
return value;
}
Get a copy of public part of the certificate and add it to the store. That is, replace this line:
store.Add(cert);
with this:
store.Add(new X509Certificate2(cert.RawData));
in this case, you will install only public part of the certificate without associating it with private key.

How import and export RSAParameters keys to file without making a change to the keys in C#

I'm writing a digital signing program using C# and I use RSACryptoServiceProvider class that generates public and private keys and signatures depending on the file. If in the program, I check the signature with the public key, signature and file, it works correctly but If I save my keys to any format in the file, In other words, I will change their format and return to the first state It doesn't work. because I can not turn it into RSAParameters correctly. please guide me?
Simple example test to show the change:
var publicParams = rsaWrite.ExportParameters(false); // Generate the public key.
var testpublicParams = publicParams;
string st = Encoding.ASCII.GetString(publicParams.Modulus);
testpublicParams.Modulus = Encoding.ASCII.GetBytes(st);
if(publicParams.Modulus != testpublicParams.Modulus) {
Console.WriteLine("The key has been changed.");
}
You can get PublicKey as string format and save it on the other text file.
public static string PublicKey(string certSubject)
{
var my = new X509Store(StoreName.My, StoreLocation.LocalMachine);
my.Open(OpenFlags.ReadOnly);
RSACryptoServiceProvider csp = null;
byte[] publicKeyByte = null;
foreach (var cert in my.Certificates)
{
if (cert.Subject.Contains(certSubject))
{
csp = (RSACryptoServiceProvider)cert.PublicKey.Key;
publicKeyByte = cert.PublicKey.EncodedKeyValue.RawData;
}
}
if (csp == null)
{
throw new Exception("No valid cert was found");
}
var publicKey = new StringBuilder();
publicKey.AppendLine("-----BEGIN PUBLIC KEY-----");
publicKey.AppendLine(Convert.ToBase64String(publicKeyByte, Base64FormattingOptions.InsertLineBreaks));
publicKey.AppendLine("-----END PUBLIC KEY-----");
return publicKey.ToString();
}
This code has two problems:
The use of Encoding.ASCII.GetBytes is wrong because it might have a non-ASCII characters so we use the Convert.ToBase64String.
publicParams.Modulus is C# byte array so != probably not the right answer so we use SequenceEqual.
And the key will not change.
var rsaWrite = new RSACryptoServiceProvider();
var publicParams = rsaWrite.ExportParameters(false); // Generate the public key.
var testpublicParams = publicParams;
string st = Convert.ToBase64String(publicParams.Modulus);
testpublicParams.Modulus = Convert.FromBase64String(st);
if (!publicParams.Modulus.SequenceEqual(testpublicParams.Modulus))
{
Console.WriteLine("The key has been changed.");
}
else
{
Console.WriteLine("The key has not been changed. :D");
}

How to check if digital signature of signed document signed by trusted certificate?

I develop application which works with pdf documents and I've to understand that my document signed by trusted signature.
I use itextsharp for getting information, but I don't how to check validity of signature.
var pdfReader = new PdfReader(document.FilePath);
var acroFields = pdfReader.AcroFields;
var names = acroFields.GetSignatureNames();
foreach (var name in names)
{
var signatureName = name as string;
var pk = acroFields.VerifySignature(signatureName);
var signatureIsValid = false;
foreach (var certificate in pk.Certificates)
{
signatureIsValid = certificate.IsValidNow; // It just check date
}
}
A document on the screen bellow has two digital signatures, but they signed without trusted certificate. I have to show some similar message for a user.
In order to check for trusted authority you need to have trusted CA certificate to check against. If you have one you could use code like this to check if cert came from trusted authority you are expecting it to be:
X509Certificate2 authorityCert = GetAuthorityCertificate();
X509Certificate2 certificateToCheck = GetYourCertificate();
X509Chain chain = new X509Chain();
chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
chain.ChainPolicy.RevocationFlag = X509RevocationFlag.ExcludeRoot;
chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority;
chain.ChainPolicy.VerificationTime = DateTime.Now;
chain.ChainPolicy.UrlRetrievalTimeout = new TimeSpan(0, 0, 0);
//Adding your CA root to the chain
chain.ChainPolicy.ExtraStore.Add(authorityCert);
bool isChainValid = chain.Build(certificateToCheck);
if (!isChainValid)
{
//Ok, let c what is wrong...
string[] errors = chain.ChainStatus
.Select(m => $"{m.StatusInformation.Trim()}, status: {m.Status}")
.ToArray();
string certificateErrors = "Error occured during checking certificate.";
if (errors != null && errors.Length > 0)
certificateErrors = string.Join(" \n", errors);
throw new ApplicationException("Trust chain is not from known authority. Errors: " + certificateErrors);
}
//Let see if our chain actually contains known root, for which you are cheking
if (!chain.ChainElements
.Cast<X509ChainElement>()
.Any(m => m.Certificate.Thumbprint == authorityCert.Thumbprint))
throw new ApplicationException("Could not locate CA root!Thumbprints did not match.");

How to get certificate from specific binding C#

I found on the internet only way to got all the certificates from the iis and i do it like the following (c#):
var store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);
store.Certificates;
Now I try to get a specific certificate of specific binding, how can I do it in C#?
The certificates themselves hold absolutely no information about the bindings used in IIS, so you cannot retrieve the certificates from the machine and expect them to have anything related to IIS. You would need to query that information from IIS.
To do this, you will need add a reference to the library that can be found under %windir%\system32\inetsrv\Microsoft.Web.Administration.dll (note: IIS 7 or newer must be installed). After this, you can do something like the following to get the certificate:
ServerManager manager = new ServerManager();
Site yourSite = manager.Sites["yourSiteName"];
X509Certificate2 yourCertificate = null;
foreach (Binding binding in yourSite.Bindings)
{
if (binding.Protocol == "https" && binding.EndPoint.ToString() == "127.0.0.1" /*your binding IP*/)
{
var store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);
yourCertificate = store.Certificates.Find(X509FindType.FindByThumbprint, ToHex(binding.CertificateHash), true)[0];
break;
}
}
public static string ToHex(byte[] ba)
{
var hex = new StringBuilder(ba.Length * 2);
foreach (byte b in ba)
{
hex.AppendFormat("{0:x2}", b);
}
return hex.ToString();
}
I think Camilo's answer has a small problem. As far as I can see (tested it) the code to find the certificate does not work, because System.Convert.ToBase64String(binding.CertificateHash) does not return a valid certificate thumbprint.
My version:
/// <summary>
/// Returns the https certificate used for a given local IIS website.
/// </summary>
/// <param name="sWebsite">Website url, e.g., "https://myserver.company.com"</param>
/// <returns>certificate, null if not found</returns>
private X509Certificate2 FindIisHttpsCert(string sWebsite)
{
Uri uriWebsite = new Uri(sWebsite);
using (ServerManager sm = new ServerManager())
{
string sBindingPort = string.Format(":{0}:", uriWebsite.Port);
Binding bdBestMatch = null;
foreach (Site s in sm.Sites)
{
foreach (Binding bd in s.Bindings)
{
if (bd.BindingInformation.IndexOf(sBindingPort) >= 0)
{
string sBindingHostInfo = bd.BindingInformation.Substring(bd.BindingInformation.LastIndexOf(':') + 1);
if (uriWebsite.Host.IndexOf(sBindingHostInfo, StringComparison.InvariantCultureIgnoreCase) == 0)
{
if ((bd.Protocol == "https") && ((bdBestMatch == null) || (bdBestMatch.BindingInformation.Length < bd.BindingInformation.Length)))
bdBestMatch = bd;
}
}
}
}
if (bdBestMatch != null)
{
StringBuilder sbThumbPrint = new StringBuilder();
for (int i = 0; i < bdBestMatch.CertificateHash.Length; i++)
sbThumbPrint.AppendFormat("{0:X2}", bdBestMatch.CertificateHash[i]);
X509Store store = new X509Store(bdBestMatch.CertificateStoreName, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);
X509Certificate2Collection coll = store.Certificates.Find(X509FindType.FindByThumbprint, sbThumbPrint.ToString(), true);
if (coll.Count > 0)
return coll[0];
}
}
return null; // if no matching site was found
}
This function also works if multiple https sites are hosted on the same server (tested) and should work if the site uses a port other than 443 (not tested). To get Binding info, %windir%\system32\inetsrv\Microsoft.Web.Administration.dll is used, as in Camilo's answer.
I had tried the solution, but ran into problems NOT finding the certificate. Ended up being that the cert store needs to be properly specified based on the binding:
ServerManager manager = new ServerManager();
Site yourSite = manager.Sites["yourSiteName"];
X509Certificate2 yourCertificate = null;
foreach (Binding binding in yourSite.Bindings)
{
if (binding.Protocol == "https" && binding.EndPoint.ToString() == "127.0.0.1" /*your binding IP*/)
{
var store = new X509Store(binding.CertificateStoreName, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);
var certs = store.Certificates.Find(X509FindType.FindByThumbprint, ToHex(binding.CertificateHash), true);
if (certs.Count > 0)
yourCertificate = certs[0];
break;
}
}
public static string ToHex(byte[] ba)
{
var hex = new StringBuilder(ba.Length * 2);
foreach (byte b in ba)
{
hex.AppendFormat("{0:x2}", b);
}
return hex.ToString();
}
The following link should help:
basically store.Certificates returns a collection of all certificates in the particular store, you can then search through for the one you want. The link shows how to do this if you know the subject name of the certificate you want.
How to get X509Certificate from certificate store and generate xml signature data?

C# RunAs /Smartcard

My application used to accept username/password to authenticate to a remote share (on Windows server) and then get the file listings etc.
public int ConnectNetResource(string server, string user, string password, ref string driveLeter) {
NETRESOURCE net = new NETRESOURCE();
net.dwScope = 0;
net.dwType = 0;
net.dwDisplayType = 0;
net.dwUsage = 0;
net.lpRemoteName = server;
net.lpLocalName = driveLeter;
net.lpProvider = null;
return WNetAddConnection2(ref net, password, user, 0);
}
Now, we need to authenticate using SmartCard. I have code to GetSmartCards, but I do not know how to make use of the certificate to authenticate.
public static X509Certificate2 GetClientCertificate()
{
X509Certificate2 certificate = null;
var store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
try
{
store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
// Nothing to do if no cert found.
if (store.Certificates != null && store.Certificates.Count > 0)
{
if (store.Certificates.Count == 1)
{
// Return the certificate present.
certificate = store.Certificates[0];
}
else
{
// Request the user to select a certificate
var certificates = X509Certificate2UI.SelectFromCollection(store.Certificates, "Digital Certificates", "Select a certificate from the following list:", X509SelectionFlag.SingleSelection);
// Check if one has been returned
if (certificates != null && certificates.Count > 0)
certificate = certificates[0];
}
}
}
finally
{
store.Close();
}
return certificate;
}
GetClientCertificate function will return a user selected certificate, now HOW do I use this certificate to connect to a remote share. What API's or .dll can i use.
Btw, I am a windows engineer who can google code and make it work somehow. Will greatly appreciate a working code. Thank you.

Categories