Search code examples
c#iisx509certificate2

X509 Certificate Intended purpose and SSL


I am looking at allowing a user to select an certificate that will be used for SSL for one of our services, the initial the user would need to select a store name and a list of certificates will show in a dropdown, I removed that after reading this article that the certificate should only come from the MY store location under Local Computer. After checking a few articles, I created the below code

using (var store = new X509Store(StoreName.My,StoreLocation.LocalMachine))
        {
            store.Open(OpenFlags.ReadOnly);

            var certificates = store.Certificates;
            foreach (var c in certificates)
            {
                bool isSslCompatible = false;
                bool ekuExists = false;//when this value does not exist, certificate can be used for all purposes [ https://tools.ietf.org/html/rfc3280#section-4.2.1.13 ] 
                if (c.HasPrivateKey)//only chose those that have a private key
                {

                    foreach (X509Extension extension in c.Extensions)
                    {
                        if (extension.Oid.Value == "2.5.29.37")//[ Friedlname = Enhanced Key Usage, names are localised, firendly name cannot be used
                        {
                            ekuExists = true;
                            X509EnhancedKeyUsageExtension ext = (X509EnhancedKeyUsageExtension) extension;
                            OidCollection oids = ext.EnhancedKeyUsages;
                            foreach (Oid oid in oids)
                            {
                                if (/*oid.FriendlyName == "Server Authentication" || -- cannot be used as friendly names are localised*/
                                    oid.Value == "1.3.6.1.5.5.7.3.1")
                                {
                                    isSslCompatible = true;
                                }
                            }
                        }
                    }
                    if (isSslCompatible || !ekuExists)//add only if value is ssl compatible
                    {
                        SSLCertficate certificate = new SSLCertficate();

                        certificate.CertificateHash = c.GetCertHash();
                        certificate.CertificateHashString = c.GetCertHashString();
                        certificate.CertificateThumbPrint = c.Thumbprint;
                        certificate.FriendlyName = c.FriendlyName;
                        certificate.SubjectName = c.Subject;
                        certificate.HasPrivateKey = c.HasPrivateKey;

                        sslCertificates.Add(certificate);
                    }
                }
            }

this issue with the above code is that it doesn't see certificates and have the All intended purposes, I am unsure how to get those,

Certificate Details,

Checking the properties window, I get the following, enter image description here, The above code doesn't seem to detect this certificate, checking under IIS, I am able to use this particular certificate for Https, is there something that I am missing in terms of my code to detect valid SSL certificates?


Solution

  • The MMC UI there is a bit misleading. What it means is "Enable all the purposes that the certificate is already claiming".

    In your filtering code you are requiring that an EKU extension be present and have the TLS Server Authentication purpose.

    IETF RFC 3280 section 4.2.1.13 says

    If the extension is present, then the certificate MUST only be used for one of the purposes indicated. If multiple purposes are indicated the application need not recognize all purposes indicated, as long as the intended purpose is present. Certificate using applications MAY require that a particular purpose be indicated in order for the certificate to be acceptable to that application.

    This is generally taken to mean "if the extension is absent, the certificate is considered valid for all purposes". The TLS RFCs don't seem to specifically reference the id-kp-serverAuth EKU, meaning it's a "by strong convention" application convention to check for it.

    There are different specifications which override the "valid for all purposes" implicit assessment, such as IETF RFC 3161 (Trusted Timestamps) section 2.3:

    The corresponding certificate MUST contain only one instance of the extended key usage field extension as defined in [RFC2459] Section 4.2.1.13 with KeyPurposeID having value:

    id-kp-timeStamping. This extension MUST be critical.

    But, again, TLS doesn't ever really talk about it.

    So you need to be more complicated. The logic that web browsers use is if (!hasEkuExtension || hasServerAuthEku), vs your if (hasServerAuthEku).

    Also, you should (almost) always compare Oid objects by their Value, not their FriendlyName. FriendlyName gets localized, so your code will only run reliably on English systems. The Oid.Value value is (a textualized form of) what is actually in the certificate. (The only exception is when Value is null, because that means the API is trying to represent a value with no defined OID (and that really only happens with ECCurve due to Curve25519)).