Delete any cert of certmgr without FriendlyName or <None> - c#

I I'm having trouble making a filter that deletes any cert that doesn't have firendlyname from my certificate store in general.
Im using c# console.
I tried that but it doesn't seem to be doing anything. Still not doing anything about what I want, does anyone have a solution, thank you.
private static void RemoveUnwantedCert()
{
// Open the Root store
string RootStoreName = "Root";
StoreLocation RootStoreLocation = StoreLocation.LocalMachine;
X509Store RootStore = new X509Store(RootStoreName, RootStoreLocation);
RootStore.Open(OpenFlags.ReadOnly);
// Get all certificates in the Root store
X509Certificate2Collection certificates = RootStore.Certificates;
// Loop through all the certificates in the Root store
foreach (X509Certificate2 certificate in certificates)
{
if (certificate.FriendlyName == "None")
try
{
// Open the Root store again, this time with ReadWrite permissions
RootStore.Open(OpenFlags.ReadWrite);
// Remove the certificate from the Root store
RootStore.Remove(certificate);
// Close the Root store
RootStore.Close();
}
catch (Exception) { }
// Break out of the loop
break;
}
// Close the Root store
RootStore.Close();
```
`
I tried that but it doesn't seem to be doing anything. Still not doing anything about what I want, does anyone have a solution, thank you.

To remove a certificate and all certificates in the chain -
X509Certificate2Collection col = store.Certificates.Find(X509FindType.FindBySubjectName, "yoursubjectname", false);
var chain = new X509Chain();
chain.Build(col[0]);
var allCertsInChain = new X509Certificate2Collection();
foreach (var entry in chain.ChainElements)
{
allCertsInChain.Add(entry.Certificate);
}
store.RemoveRange(allCertsInChain);

Thanks but i just want delete a specified one.

Related

C# list installed certificates

first i'm an complete newby on c#, so im just searching on the web for posibilities
What i want to get: I want a button which retrieves a list of installed certificates in the personal store.
i tried already a little, but get messages about missing references etc. So i hope someone can give me a litte advice how to achieve this.
what i found on the web is:
using System.Security.Cryptography.X509Certificates;
public static X509Certificate2 selectCert(StoreName store, StoreLocation location, string windowTitle, string windowMsg)
{
X509Certificate2 certSelected = null;
X509Store x509Store = new X509Store(store, location);
x509Store.Open(OpenFlags.ReadOnly);
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;
}
yours
(i use visual studio...)
Add a reference to System.Security.dll to use the X509Certificate2UI class.
you can use foreach
if (sel.Count > 0){
foreach(var cert in sel){
certSelected = cert ;
}
}
you can make it even shorte when using linq
using System.Linq;
...
if (sel.Count > 0){
return sel.FirstOrDefault();
}

Service account cannot load the X509Certificate from windows certificate store

All, I run into an issue where the service account my ASP.NET web forms application (.Net Framework 4.6.1) runs under cannot load the X509Certificate(.pfx) from the personal store on the windows 2012 R2 server .Here is how I imported the certificate to the certificate store
I Logged into the server using the service account(domain\username) ,used mmc snap in to import the certificate to Current User Personal Certificate Store (please see screenshot at the end)
This is the code I am using to load the certificate in C#.But the certificate
is null
public X509Certificate2 Load()
{
X509Certificate2 x509Certificate = null;
var store = new X509Store(StoreName.My,StoreLocation.CurrentUser);
string thumbPrint = StripTheSpacesAndMakeItUpper(ConfigurationManager.AppSettings["pfxthumbPrint"]);
store.Open(OpenFlags.ReadOnly);
var certCollection = store.Certificates;
foreach (var x509 in certCollection)
{
if (x509.Thumbprint.Equals(thumbPrint))
{
x509Certificate = x509;
break;
}
}
store.Close();
return x509Certificate;
}
private string StripTheSpacesAndMakeItUpper(string thumbPrint)
{
if(!string.IsNullOrWhiteSpace(thumbPrint))
{
return Regex.Replace(thumbPrint, #"\s|\W", "").ToUpper();
}
return thumbPrint;
}
Any suggestions on why the method Load returns null ?
]3
I don't know how you set the value of ConfigurationManager.AppSettings["pfxthumbPrint"]. I guess you double clicked the certificate in your CurrentUser store and copied the thumbprint from the Details tab, right? If that is the case, you copied also one invisible character from the beginning of the thumbprint.
The saddest thing is that this character (I don't know what it is) is not visible in your app.config/web.config. The only way to get rid of if is to delete the first quote character with the first character of the thumbprint and type them in again manually. Or delete entire thumbprint and the quotes and type them again if you wish.
Instead of
if (x509.Thumbprint.Equals(x509CertificateFriendlyName))
Shouldn't it be
if (x509.Thumbprint.Equals(thumbPrint))
...?
Also, you appear to have x509Certificate declared as a local variable and then you discard it. Did you intend to assign the value to an instance variable perhaps? I don't even see a return statement.
Also, you're not disposing your store, although that probably isn't the cause of your issue.
Here's a different version that addresses these issues, and will also eliminate any invisible characters in the configuration entry (see pepo's answer for why).
public X509Certificate2 Load()
{
var thumbPrintFromConfig = ConfigurationManager.AppSettings["pfxthumbPrint"]
var thumbPrint = Regex.Replace(thumbPrintFromConfig.ToUpper(),"[^A-F0-9]",""); //Keep only hex digits
return Load(thumbPrint);
}
private X509Certificate2 Load(string thumbPrint)
{
using (var store = new X509Store(StoreName.My,StoreLocation.CurrentUser))
{
store.Open(OpenFlags.ReadOnly);
var cert = store
.Certificates
.OfType<X509Certificate2>()
.Where(x => x.Thumbprint == thumbPrint)
.Single();
store.Close();
return cert;
}
}

Add a generated certificate to the store and update an IIS site binding

I'm running into the following and after feeling like I've exhausted various avenues of research on Google and Stack Overflow I decided to just ask my own question about it.
I'm trying to generate a personal certificate (using BouncyCastle) based on a CA certificate that I already have and own. After generating the certificate, placing it in the 'My' store, I then attempt to update my IIS website's SSL binding to use this new certificate.
What I'm noticing is that the updates to the IIS website (using ServerManager) are not throwing exception, yet when I go to the IIS Manager console I notice the website's binding has no SSL certificate selected. When I attempt to select the certificate that I created (shows up fine as a viable option) I get the following error message:
A specified logon session does not exist. It may already have been terminated. (Exception from HRESULT: 0x80070520)
As a test I exported my generated certificate (with the private key) and reinstalled it via the wizard and then once again tried setting up the binding (via IIS Manager) which worked.
Because of this behavior I assumed it was an issue with how I was generating or adding the certificate to the store. I was hoping someone may have some idea of what the issue I'm having may be. The following are the relevant functions (I believe) used in creating the certificate, adding it to the store, and updating the website's binding programmatically:
Main function the generates that get the CA certificate private key, generates the personal self-signed certificate, and updates the sites binding:
public static bool GenerateServerCertificate(
X509Certificate2 CACert,
bool addToStore,
DateTime validUntil)
{
try
{
if (CACert.PrivateKey == null)
{
throw new CryptoException("Authority certificate has no private key");
}
var key = DotNetUtilities.GetKeyPair(CACert.PrivateKey).Private;
byte[] certHash = GenerateCertificateBasedOnCAPrivateKey(
addToStore,
key,
validUntil);
using (ServerManager manager = new ServerManager())
{
Site site = manager.Sites.Where(q => q.Name == "My Site").FirstOrDefault();
if (site == null)
{
return false;
}
foreach (Binding binding in site.Bindings)
{
if (binding.Protocol == "https")
{
binding.CertificateHash = certHash;
binding.CertificateStoreName = "MY";
}
}
manager.CommitChanges();
}
}
catch(Exception ex)
{
LOG.Error("Error generating certitifcate", ex);
return false;
}
return true;
}
Generating the certificate based on the CA private key:
public static byte[] GenerateCertificateBasedOnCAPrivateKey(
bool addToStore,
AsymmetricKeyParameter issuerPrivKey,
DateTime validUntil,
int keyStrength = 2048)
{
string subjectName = $"CN={CertSubjectName}";
// Generating Random Numbers
CryptoApiRandomGenerator randomGenerator = new CryptoApiRandomGenerator();
SecureRandom random = new SecureRandom(randomGenerator);
ISignatureFactory signatureFactory = new Asn1SignatureFactory("SHA512WITHRSA", issuerPrivKey, random);
// The Certificate Generator
X509V3CertificateGenerator certificateGenerator = new X509V3CertificateGenerator();
certificateGenerator.AddExtension(
X509Extensions.ExtendedKeyUsage,
true,
new ExtendedKeyUsage((new List<DerObjectIdentifier> { new DerObjectIdentifier("1.3.6.1.5.5.7.3.1") })));
// Serial Number
BigInteger serialNumber = BigIntegers.CreateRandomInRange(BigInteger.One, BigInteger.ValueOf(Int64.MaxValue), random);
certificateGenerator.SetSerialNumber(serialNumber);
// Issuer and Subject Name
X509Name subjectDN = new X509Name(subjectName);
X509Name issuerDN = new X509Name(CACertificateName);
certificateGenerator.SetIssuerDN(issuerDN);
certificateGenerator.SetSubjectDN(subjectDN);
// Valid For
DateTime notBefore = DateTime.UtcNow.Date;
DateTime notAfter = validUntil > notBefore ? validUntil : notBefore.AddYears(1);
certificateGenerator.SetNotBefore(notBefore);
certificateGenerator.SetNotAfter(notAfter);
// Subject Public Key
AsymmetricCipherKeyPair subjectKeyPair;
var keyGenerationParameters = new KeyGenerationParameters(random, keyStrength);
var keyPairGenerator = new RsaKeyPairGenerator();
keyPairGenerator.Init(keyGenerationParameters);
subjectKeyPair = keyPairGenerator.GenerateKeyPair();
certificateGenerator.SetPublicKey(subjectKeyPair.Public);
// Generating the Certificate
Org.BouncyCastle.X509.X509Certificate certificate = certificateGenerator.Generate(signatureFactory);
// correcponding private key
PrivateKeyInfo info = PrivateKeyInfoFactory.CreatePrivateKeyInfo(subjectKeyPair.Private);
// merge into X509Certificate2
X509Certificate2 x509 = new X509Certificate2(certificate.GetEncoded());
Asn1Sequence seq = (Asn1Sequence)Asn1Object.FromByteArray(info.ParsePrivateKey().GetDerEncoded());
if (seq.Count != 9)
{
throw new PemException("Malformed sequence in RSA private key");
}
RsaPrivateKeyStructure rsa = RsaPrivateKeyStructure.GetInstance(seq);
RsaPrivateCrtKeyParameters rsaparams = new RsaPrivateCrtKeyParameters(
rsa.Modulus,
rsa.PublicExponent,
rsa.PrivateExponent,
rsa.Prime1,
rsa.Prime2,
rsa.Exponent1,
rsa.Exponent2,
rsa.Coefficient);
x509.PrivateKey = DotNetUtilities.ToRSA(rsaparams);
if (addToStore)
{
// Add certificate to the Personal store
AddCertToStore(x509, StoreName.My, StoreLocation.LocalMachine, "Certificate Friendly Name");
}
return x509.GetCertHash();
}
Adding the certificate to the store:
private static void AddCertToStore(X509Certificate2 cert, StoreName storeName, StoreLocation storeLocation, string friendlyName)
{
X509Store store = new X509Store(storeName, storeLocation);
try
{
store.Open(OpenFlags.ReadWrite);
store.Add(cert);
if (!string.IsNullOrWhiteSpace(friendlyName)) {
var certs = store.Certificates.Find(X509FindType.FindBySubjectDistinguishedName, cert.Subject, true);
if (certs.Count > 0)
{
certs[0].FriendlyName = friendlyName;
}
}
}
finally
{
store.Close();
}
}
Just a final note, I have tried a few things from what I've seen on various sites in regards to that error (doesn't seem very clear what the issue is):
This works on a different box (my personal development machine) but I hit these snags on a server machine (running Windows Server 2012 R2)
The IIS Help dialog informs me the machine is running IIS 8.5
Verified the validity generated certificate and the CA certificate with CertUtil.exe
Verified the generated certificate and the CA certificate had a private key that could be found
Verified administrators (and eventually even my logged in account) had access to where the private key file for both the CA certificate and the generated certificate.
Any ideas what my issue could be?
Update:
I was able to get some results by doing the following:
Export my certificate to a file programmatically by doing File.WriteAllBytes(filePath, cert.Export(X509ContentType.Pkcs12, password));
Then I import this certificate file to the store by doing:
var cert = new X509Certificate2(certFilePath, certPassword, X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet);
// My original AddCertToStore function
AddCertToStore(cert, StoreName.My, StoreLocation.LocalMachine, "Friendly Name");
Finally, I set the binding as I was doing earlier:
using (ServerManager manager = new ServerManager())
{
Site site = manager.Sites.Where(q => q.Name == "My Site").FirstOrDefault();
if (site == null)
{
return false;
}
foreach (Binding binding in site.Bindings)
{
if (binding.Protocol == "https")
{
binding.CertificateHash = certHash;
binding.CertificateStoreName = "MY";
}
}
manager.CommitChanges();
}
Doing it this way works, but I don't see why I would have export the certificate to a file, THEN load it into a X509Certificate2 object, add to the store, and finally set up the binding.
The ToRSA method most likely creates an ephemeral RSA key, so when the references are all gone the key gets deleted. Exporting the ephemeral structure into a PFX then re-importing it with PersistKeySet is one way to turn it into a persisted key. Others exist, but that one is one of the less convoluted ones.
You don't actually have to write it to a file, though.
byte[] pkcs12Blob = cert.Export(X509ContentType.Pkcs12, password);
ver certWithPersistedKey = new X509Certificate2(pkcs12Blob, password, allTheFlagsYouAlreadySet);
There are also other subtleties going on, like setting the PrivateKey property has different behaviors for a cert instance that was loaded from a store and one which was loaded from bytes... the PFX/PKCS#12 export/import works around all of those.
For us, it was related to an invalid certificate. We went to IIS >> Server Certificates and exported the certificate from there.
The certificate was correctly bound to IIS site after that.

SQL CLR created in C# cannot access local cert store

I cannot create SQL CLR to do a decrypt function and encrypt function from cert store with getting the resultant string from a cert.
'System.Security.SecurityException: Request for the permission of type 'System.Security.Permissions.StorePermission, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=blahblahblahblah' failed.
System.Security.SecurityException: '
Is there a way to make SQL CLR have access to your cert stores in a sys config you can change or another trust setting? Do I need to load the dependent System.Security.Cryptography dependent library? I ultimately want to set this up on Azure SQL Server too so that may be a roadblock as well. I know that my decrypt and encrypt work fine when hard coding the resultant data obtained from the cert that I need for the pattern. But the access to the cert would be better to just do:
fDecrypt('mythumbprintstringtofinditlocally', 'DLQUOUIEWLKCJLAKJA=!##$'(some data that is encrypted with local cert)
rather than hard code the data in. As this cert can change for environments.
private void SetEncryptionDirectlyFromThumbprint(string certThumbprint)
{
var cert = GetCertificateByThumbprint(certThumbprint, StoreLocation.LocalMachine, StoreName.My);
}
//needs System.Security.Cryptography library.
private X509Certificate2 GetCertificateByThumbprint(string thumbprint, StoreLocation storeLocation, StoreName storeName, bool requirePrivateKey = false)
{
if (string.IsNullOrEmpty(thumbprint))
{
throw new ArgumentNullException("thumbprint");
}
var cleanedThumbprint = thumbprint.Replace(" ", "").ToUpperInvariant();
var store = new X509Store(storeName, storeLocation);
try
{
store.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadOnly);
var foundCerts = store.Certificates.Find(X509FindType.FindByThumbprint, cleanedThumbprint, false);
foreach (var cert in foundCerts)
{
if (!requirePrivateKey || cert.HasPrivateKey)
{
return cert;
}
}
}
finally
{
store.Close();
}
return null;
}
Looks like all it was thus far was the 'Permission level' set under Project>SQLCLR>Permission Level. I changed it to 'External_Access' and voila, it worked right away. Now the problem it seems will be that Azure only likes safe assemblies and just included the ability to use CLR recently. Arghhh, cannot win them all I guess.

X509 store can not find certificate by SerialNumber

I need to get a X509 Certificate by Serial Number, I have the serial number and I am looping through them and i see the serial number in the collection I need but it is never found.
Here is my debug code just ot make sure I am seeing the proper serial numbers:
X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);
foreach (X509Certificate2 cert in store.Certificates)
{
System.Web.HttpContext.Current.Response.Write (cert.SerialNumber + "=" + oauthCertificateFindValue + "<br/>");
if (cert.SerialNumber == oauthCertificateFindValue)
{
System.Web.HttpContext.Current.Response.Write("<br/>FOUND FOUND FOUND<br/>");
}
}
Here is the output from this code:
0091ED5F0CAED6AD52‎‎=0091ED5F0CAED6AD52
3D3233116A894CB244DB359DF99E7862=0091ED5F0CAED6AD52
Clearly the first one I loop though matches the serial number but the if always fails and what I really need to work based on this serial number also fails:
X509Certificate2Collection certificateCollection = store.Certificates.Find(x509FindType, oauthCertificateFindValue, false);
if (certificateCollection.Count == 0)
{
throw new ApplicationException(string.Format("An OAuth certificate matching the X509FindType '{0}' and Value '{1}' cannot be found in the local certificate store.", oauthCertificateFindType, oauthCertificateFindValue));
}
return certificateCollection[0];
What am I doing wrong here?
It would appear that there are two invisible characters in the certificate serial number of the certificate you're trying to find, which is why they aren't matching. You should be able to confirm this if you change the output from your foreach loop to:
System.Web.HttpContext.Current.Response.Write (string.Format("{0} (Length: {1}) = {2} (Length: {3})<br/>", cert.SerialNumber, cert.SerialNumber.Length oauthCertificateFindValue, oauthCertificateFindValue.Length);
You'll most likely see that the values look the same, but their lengths differ (indicating the presence of these invisible characters).
You will need to update your search value to match the certificate's serial number including the invisible characters.
The first X509Store you have opened that was not closed yet and from the same you tried to get the certificate which was already read out.
First close the store in code where you are matching the serials and the code where you are reading from store, reopen the store.
Below code worked for me. Just verify once.
private static void CompareCertSerialse()
{
string oauthCertificateFindValue = "7C00001851CBFF5E9F563E236F000000001851";
X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);
foreach (X509Certificate2 cert in store.Certificates)
{
Console.WriteLine(cert.SerialNumber + "=" + oauthCertificateFindValue);
if (cert.SerialNumber == oauthCertificateFindValue)
{
Console.WriteLine("FOUND FOUND FOUND>");
//Close the store
store.Close();
GetCert(oauthCertificateFindValue);
}
}
}
private static X509Certificate2 GetCert(string oauthCertificateFindValue)
{
//Reopen the store
X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);
X509Certificate2Collection certificateCollection = store.Certificates.Find(X509FindType.FindBySerialNumber, oauthCertificateFindValue, false);
if (certificateCollection.Count == 0)
{
Console.WriteLine("Nothing Found");
}
return certificateCollection[0];
}

Categories

Resources