Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I programmatically find which certificate was used to sign a given certificate?

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):

enter image description here

Under Details, in the "Certificate Hierarchy", I see this:

  • DigiCert High Assurance EV Root CA
    • DigiCert SHA2 Extended Validation Server CA
      • github.com

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?

like image 574
sharptooth Avatar asked Feb 24 '16 11:02

sharptooth


People also ask

How can I check what certificates are being used?

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.

How do I find a self signed certificate?

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.

How do you know if certificate is self-signed or CA signed?

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.


2 Answers

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.

Option 1 -- SSL connection

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:

  1. If ChainElements contains at least two elements (say, leaf certificate and proposed issuer).
  2. If first element of the 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.

Option 2 -- searching local certificate store

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.

like image 165
Crypt32 Avatar answered Sep 17 '22 02:09

Crypt32


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.

like image 38
erickson Avatar answered Sep 18 '22 02:09

erickson