If you know which certificates can be root and intermediate certificates for the certificate to check, you can load the public keys of the root and intermediate certificates in the ChainPolicy.ExtraStore
collection of the X509Chain
object.
My task was also to write a Windows Forms application to install a certificate, only if it was issued dependent on the known "National Root certificate" of my country's government. There also is a limited number of CA's that are allowed to issue certificates to authenticate connections to the national web services, so I had a limited set of certificates that can be in the chain and might be missing on the target machine. I collected all public keys of the CA's and the government root certificates in a subdirectory "cert" of the application:
In Visual Studio, I added the directory cert to the solution and marked all files in this directory as embedded resource. This allowed me to enumerate the collection of "trusted" certificates in my c# library code, to build a chain to check the certificate even if the issuer certificate is not installed. I made a wrapper class for X509Chain for this purpose:
private class X509TestChain : X509Chain, IDisposable
{
public X509TestChain(X509Certificate2 oCert)
: base(false)
{
try
{
ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority;
if (!Build(oCert) || (ChainElements.Count <= 1))
{
Trace.WriteLine("X509Chain.Build failed with installed certificates.");
Assembly asmExe = System.Reflection.Assembly.GetEntryAssembly();
if (asmExe != null)
{
string[] asResources = asmExe.GetManifestResourceNames();
foreach (string sResource in asResources)
{
if (sResource.IndexOf(".cert.") >= 0)
{
try
{
using (Stream str = asmExe.GetManifestResourceStream(sResource))
using (BinaryReader br = new BinaryReader(str))
{
byte[] abResCert = new byte[str.Length];
br.Read(abResCert, 0, abResCert.Length);
X509Certificate2 oResCert = new X509Certificate2(abResCert);
Trace.WriteLine("Adding extra certificate: " + oResCert.Subject);
ChainPolicy.ExtraStore.Add(oResCert);
}
}
catch (Exception ex)
{
Trace.Write(ex);
}
}
}
}
if (Build(oCert) && (ChainElements.Count > 1))
Trace.WriteLine("X509Chain.Build succeeded with extra certificates.");
else
Trace.WriteLine("X509Chain.Build still fails with extra certificates.");
}
}
catch (Exception ex)
{
Trace.Write(ex);
}
}
public void Dispose()
{
try
{
Trace.WriteLine(string.Format("Dispose: remove {0} extra certificates.", ChainPolicy.ExtraStore.Count));
ChainPolicy.ExtraStore.Clear();
}
catch (Exception ex)
{
Trace.Write(ex);
}
}
}
In the calling function, I could now successfully check if an unknown certificate derives from the national root certificate:
bool bChainOK = false;
using (X509TestChain oChain = new X509TestChain(oCert))
{
if ((oChain.ChainElements.Count > 0)
&& IsPKIOverheidRootCert(oChain.ChainElements[oChain.ChainElements.Count - 1].Certificate))
bChainOK = true;
if (!bChainOK)
{
TraceChain(oChain);
sMessage = "Root certificate not present or not PKI Overheid (Staat der Nederlanden)";
return false;
}
}
return true;
To complete the picture: to check the root certificate (that usually is installed because it is included in Windows Update, but in theory could be missing as well), I compare the friendly name and thumbprint to the published values:
private static bool IsPKIOverheidRootCert(X509Certificate2 oCert)
{
if (oCert != null)
{
string sFriendlyName = oCert.FriendlyName;
if ((sFriendlyName.IndexOf("Staat der Nederlanden") >= 0)
&& (sFriendlyName.IndexOf(" Root CA") >= 0))
{
switch (oCert.Thumbprint)
{
case "101DFA3FD50BCBBB9BB5600C1955A41AF4733A04": // Staat der Nederlanden Root CA - G1
case "59AF82799186C7B47507CBCF035746EB04DDB716": // Staat der Nederlanden Root CA - G2
case "76E27EC14FDB82C1C0A675B505BE3D29B4EDDBBB": // Staat der Nederlanden EV Root CA
return true;
}
}
}
return false;
}
I am not sure if this check is secure at all, but in my case the operator of the Windows Forms application is quite sure to have access to a valid certificate to be installed. The goal of the software is just to filter the certificates list to help him install only the correct certificate in the machine store of the computer (the software also installs the public keys of the intermediate and root certificate, to ensure that the runtime behavior of the web service client is correct).
PkixCertPathBuilder
API?. – Prolific