In my C# code I have a X509Certificate2
object which represents an SSL certificate (from a local store or from a successful HTTP request over SSL). The certificate is signed with some intermediate certificate which maybe is present in the local store, maybe not, so using X509Chain.Build()
will probably not work.
A picture of Firefox certificate viewer (because I have no usable code yet):
Under Details, in the "Certificate Hierarchy", I see this:
My object represents "github.com", the lowest line in the chain. I need to programmatically identify the middle line ("DigiCert SHA2 Extended Validation Server CA").
How do I know a thumbprint or anything equivalent which would let me identify which certificate was used to sign my certificate?
Chrome has made it simple for any site visitor to get certificate information with just a few clicks: Click the padlock icon in the address bar for the website. Click on Certificate (Valid) in the pop-up. Check the Valid from dates to validate the SSL certificate is current.
msc in the windows search bar and choosing "Run as administrator." Expand both the "Personal" and "Trusted Root Certification" directories. In the Personal Certificates folder, you will find both the CA and the Self-Signed Certificate that we created in the previous steps.
A certificate is self-signed if the subject and issuer match. A certificate is signed by a Certificate Authority (CA) if they are different. To validate a CA-signed certificate, you also need a CA certificate. The Details tab (not shown here) sections can be expanded to show each field in a certificate.
In this specific case (github.com), X509Chain.Build
will work, because the end certificate contains information about the location of the issuer certificate (in the Authority Information Access extension).
But sometimes this may not work (for example, with Thawte certificates, because Thawte do not provide explicit information about issuer certificate location). And if the certificate is installed in the local certificate store, there is no way to automatically locate the issuer.
However, if you work with an SSL certificate and you can establish an SSL session, you can get the certificate by adding a listener to the ServicePointManager.ServerCertificateValidationCallback
property: https://msdn.microsoft.com/en-us/library/system.net.servicepointmanager.servercertificatevalidationcallback.aspx
RemoteCertificateValidationCallback delegate contains several parameters, one of them is chain
, which contains an SSL certificate chain returned by the server. And if the remote server contains an issuer certificate, it will be presented there in the ChainElements
collection. This object usually contains several elements:
-Leaf Certificate
-Issuer Certificate
-(Optional Issuer certs when available)
So, you need to check two things:
ChainElements
contains at least two elements (say, leaf certificate and proposed issuer).ChainElements
collection do not have NotSignatureValid
status in the ChainelementStatus
collection.You could add the following piece of code in the RemoteCertificateValidationCallback
delegate to perform these checks:
X509Certificate2 issuer = null;
if (
chain.ChainElements.Count > 1 &&
!chain.ChainElements[0].ChainElementStatus.Any(x => x.Status == X509ChainStatusFlags.NotSignatureValid)) {
issuer = chain.ChainElements[1].Certificate;
}
If after running this piece of code the issuer
variable is null
, then you cannot automatically determine who is the issuer of your certificate. This process will require some additional research. And it is not null
, then issuer
variable will hold actual issuer certificate.
Ok, according to your comments, you want to determine whether the issuer certificate is installed in the local certificate store or not. By reading your question I didn't get it. Why we should guess what you actually are looking? Eventually, I'm still unsure if you know/understand what you want to achieve.
If you want to find whether the issuer is installed in the local store, you can use the following algorithm:
1) use X509Certificate2Collection.Find method and find candidate certificates by their subject name
2) find in the candidate list (retrieved in step 1) ones that have Subject Key Identifier value the same as Authority Key Identifier value of the certificate in the subject.
X509Certificate2Collection certs = new X509Certificate2Collection();
// grab candidates from CA and Root stores
foreach (var storeName in new[] { StoreName.CertificateAuthority, StoreName.Root }) {
X509Store store = new X509Store(storeName, StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly);
certs.AddRange(store.Certificates);
store.Close();
}
certs = certs.Find(X509FindType.FindBySubjectDistinguishedName, cert.Issuer, false);
if (certs.Count == 0) {
Console.WriteLine("Issuer is not installed in the local certificate store.");
return;
}
var aki = cert.Extensions["2.5.29.35"];
if (aki == null) {
Console.WriteLine("Issuer candidates: ");
foreach (var candidate in certs) {
Console.WriteLine(candidate.Thumbprint);
}
return;
}
var match = Regex.Match(aki.Format(false), "KeyID=(.+)", RegexOptions.IgnoreCase);
if (match.Success) {
var keyid = match.Groups[1].Value.Replace(" ", null).ToUpper();
Console.WriteLine("Issuer candidates: ");
foreach (var candidate in certs.Find(X509FindType.FindBySubjectKeyIdentifier, keyid, false)) {
Console.WriteLine(candidate.Thumbprint);
}
} else {
// if KeyID is not presented in the AKI extension, attempt to get serial number from AKI:
match = Regex.Match(aki.Format(false), "Certificate SerialNumber=(.+)", RegexOptions.IgnoreCase);
var serial = match.Groups[1].Value.Replace(" ", null);
Console.WriteLine("Issuer candidates: ");
foreach (var candidate in certs.Find(X509FindType.FindBySerialNumber, serial, false)) {
Console.WriteLine(candidate.Thumbprint);
}
}
assuming that cert
variable stores certificate in subject (for which the issuer is searched). This approach have issues as it do not validate signature and may return false positives.
I asked, "What's wrong with the IssuerName property?"
The reply was, "Nothing except it doesn't have to match the subject of the signer certificate and even if it matches there's no way to know if it's exactly the right certificate or just some certificate with the same subject."
This is incorrect.
PKIX § 6.1 says, "for all x in {1, ..., n-1}, the subject of certificate x is the issuer of certificate x+1."
Subsections clarify this by instructing, "Assign the certificate subject name to working_issuer_name," and then, for the next certificate in the chain, verifying that the "… certificate issuer name is the working_issuer_name."
You might be confused because you may have many issuer certificates with the same name, but different keys. In that case, the correct signing key can be identified by matching the issuer's subject key identifier to the subject's authority key identifier. Matching key identifiers is not sufficient, however: the names have to match first.
It's not clear why you are trying to do this manually. You should be able to provide all available intermediates to your path-building library, and let find a valid chain if any exists.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With