Как проверить, включен ли сертификат X509 "Расширенная проверка"?

Я изо всех сил пытаюсь найти надежный способ проверить из моего приложения на C# (.Net 4.0), установлен ли флаг X509Certificate (или X509Certificate2) "Расширенная проверка" (EV). Кто-нибудь знает лучший метод?

2 ответа

Решение

Вы можете проверить, если X509Certificate содержит один из этих OId. Кроме того, вы можете проверить исходный код Chromium в Source. Вы можете найти источник здесь. Если вы хотите придерживаться Firefox, вы можете взять реализацию здесь.

Теперь я обновил свой источник и проверил его. Я написал небольшой метод для проверки X509Certificate2 против OId-List из Википедии /Chromium. В этом методе я использую Wikipedia-List, лучше вместо этого взять Chromium-List.


Как сохраняется OId?

каждый CAимеет один или несколько ObjectIds OIds. Они не сохраняются как расширения, как вы можете догадаться, они сохраняются как записи в расширениях политики. Чтобы получить точное расширение, рекомендуется использовать Oid Policy Extension сам по себе, а не с помощью дружественного имени. Идентификатор расширений политики 2.5.29.32,

Извлечение информации

Чтобы получить внутреннее содержание расширений политики, мы можем использовать System.Security.Cryptography.AsnEncodedData преобразовать его в читаемый string, Сама строка содержит политики, которые мы должны сопоставить с нашими string[] чтобы убедиться, что он содержит один из OIds EV Certificate,

Источник

    /// <summary>
    /// Checks if a X509Certificate2 contains Oids for EV
    /// </summary>
    /// <param name="certificate"></param>
    /// <returns></returns>
    private static bool IsCertificateEV(X509Certificate2 certificate)
    {
        // List of valid EV Oids
        // You can find correct values here:
        // http://code.google.com/searchframe#OAMlx_jo-ck/src/net/base/ev_root_ca_metadata.cc&exact_package=chromium
        // or in Wikipedia
        string[] extendedValidationOids = 
        {
            "1.3.6.1.4.1.34697.2.1",
            "1.3.6.1.4.1.34697.2.2",
            "1.3.6.1.4.1.34697.2.1", 
            "1.3.6.1.4.1.34697.2.3", 
            "1.3.6.1.4.1.34697.2.4",
            "1.2.40.0.17.1.22",
            "2.16.578.1.26.1.3.3",
            "1.3.6.1.4.1.17326.10.14.2.1.2", 
            "1.3.6.1.4.1.17326.10.8.12.1.2",
            "1.3.6.1.4.1.6449.1.2.1.5.1",
            "2.16.840.1.114412.2.1",
            "2.16.528.1.1001.1.1.1.12.6.1.1.1",
            "2.16.840.1.114028.10.1.2",
            "1.3.6.1.4.1.14370.1.6",
            "1.3.6.1.4.1.4146.1.1",
            "2.16.840.1.114413.1.7.23.3",
            "1.3.6.1.4.1.14777.6.1.1", 
            "1.3.6.1.4.1.14777.6.1.2",
            "1.3.6.1.4.1.22234.2.5.2.3.1",
            "1.3.6.1.4.1.782.1.2.1.8.1",
            "1.3.6.1.4.1.8024.0.2.100.1.2",
            "1.2.392.200091.100.721.1",
            "2.16.840.1.114414.1.7.23.3",
            "1.3.6.1.4.1.23223.2", 
            "1.3.6.1.4.1.23223.1.1.1", 
            "1.3.6.1.5.5.7.1.1",
            "2.16.756.1.89.1.2.1.1",
            "2.16.840.1.113733.1.7.48.1",
            "2.16.840.1.114404.1.1.2.4.1",
            "2.16.840.1.113733.1.7.23.6",
            "1.3.6.1.4.1.6334.1.100.1",
        };

        // Logic:
        // Locate Certificate Policy Extension
        // Convert to AsnEncodedData (String)
        // Check if any of the EV Oids exist
        return (
                from X509Extension ext in certificate.Extensions 
                where ext.Oid.Value == "2.5.29.32" 
                select new AsnEncodedData(ext.Oid, ext.RawData).Format(true))
                .Any(asnConvertedData => extendedValidationOids.Where(asnConvertedData.Contains).Any()
            );
    }

Если вам нужен источник, чтобы начать:

    static void Main(string[] args)
    {
        // Create Delegate for analysis of X509Certificate
        ServicePointManager.ServerCertificateValidationCallback = ValidateServerCertificate;

        // Make sample request to EV-Website to get Certificate
        var wc = new WebClient();
        wc.DownloadString("https://startssl.com");  // EV
        wc.DownloadString("https://petrasch.biz");  // Not EV
        Console.ReadLine();
    }

    public static bool ValidateServerCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
    {
        var cert = (X509Certificate2) certificate;
        Console.WriteLine("Certificate: " + cert.GetNameInfo(X509NameType.SimpleName, true) + " -> " + IsCertificateEV(cert));
        return true;
    }

Если кто-то знает лучший способ достижения этой цели, сообщите нам об этом.

Я думал, что выложу более полный ответ, хотя этот вопрос довольно старый. Я не буду отвлекаться от существующего ответа, так что этот будет полным.

У сертификата EV есть несколько проверок, которые необходимо пройти, чтобы браузер считал, что сертификат является EV.

  1. Что сертификат имеет идентификатор политики, который, как известно, является политикой EV.
  2. Отпечаток корня сертификата соответствует закрепленному идентификатору политики.
  3. Сертификат должен пройти онлайн-проверку отзыва.
  4. Если сертификат notBefore (дата выдачи) находится после 01.01.2015, сертификат должен поддерживать прозрачность сертификата.
  5. Сертификат должен быть выдан доверенным корнем.
  6. Что все цепочки действительны, если существует несколько путей доверия.

Давайте разберем каждый из них.

Идентификатор политики

Сертификат имеет расширение, называемое идентификатором политики. Расширения могут быть доступны из X509Certificate2.Extensions имущество. Расширение идентификатора политики имеет Идентификатор объекта ("OID") 2.5.29.32, Таким образом, мы можем получить необработанное расширение, используя что-то вроде этого:

var extension = certificate.Extensions["2.5.29.32"]

Если это возвращает ноль, то есть политики вообще нет, вы можете сразу предположить, что это не сертификат EV.

Скорее всего, у сертификата есть какая-то политика. В этом случае вам необходимо декодировать данные. Атрибут даст его вам в сыром ASN.1, нам нужно разобраться в этом.

К сожалению, в.NET нет ничего, что могло бы сделать это из коробки сегодня. тем не мение CryptDecodeObjectEx может сделать это, если вы используете платформу invoke. Подробную информацию об этом я опущу, но вокруг достаточно информации, чтобы показать, как вызывать эту функцию. Вы захотите вызвать его с параметром lpszStructType, установленным в значение (IntPtr)16, Это вернет вам CERT_POLICIES_INFO структура, которая имеет счетчик и указатель на массив CERT_POLICY_INFO структур. Эта структура имеет поле под названием pszPolicyIdentifier, Именно этот OID политики нас интересует.

Каждый центр сертификации имеет один или несколько OID, которые они используют для создания сертификата в качестве EV. Каждый CA документирует их на своей странице политики. Тем не менее, лучшее место, чтобы получить актуальный список этого, вероятно , исходный код Chromium.

Если сертификат имеет политику, соответствующую одному из этих OID, мы можем перейти к следующей проверке.

Корневой отпечаток

Если вы посмотрите на Chromium Source в приведенной выше ссылке, вы увидите, что в дополнение к идентификаторам политики он также сохраняет отпечаток корневого каталога SHA256.

Это связано с тем, что в дополнение к сертификату, имеющему правильный OID, он должен выдаваться центром сертификации, чей отпечаток совпадает. В источнике Chromium мы видим что-то вроде этого:

{{0x06, 0x3e, 0x4a, 0xfa, 0xc4, 0x91, 0xdf, 0xd3, 0x32, 0xf3, 0x08,
      0x9b, 0x85, 0x42, 0xe9, 0x46, 0x17, 0xd8, 0x93, 0xd7, 0xfe, 0x94,
      0x4e, 0x10, 0xa7, 0x93, 0x7e, 0xe2, 0x9d, 0x96, 0x93, 0xc0}},
    {
        // AC Camerfirma uses the last two arcs to track how the private key
        // is managed - the effective verification policy is the same.
        "1.3.6.1.4.1.17326.10.14.2.1.2", "1.3.6.1.4.1.17326.10.14.2.2.2",
    }

Таким образом, сертификат должен иметь идентификаторы политики "1.3.6.1.4.1.17326.10.14.2.1.2" или "1.3.6.1.4.1.17326.10.14.2.2.2", но корень должен иметь отпечаток SHA1 двоичного кода, показанного выше.,

Это предотвращает использование мошенническим ЦС идентификатора политики, который ему не принадлежит.

Проверка отзыва

Если браузер не может проверить, отозван ли сертификат, он не будет считаться сертификатом EV. Должна быть сделана онлайн проверка отзыва, хотя клиент может кэшировать результат.

Вы можете выполнить проверку отзыва при использовании X509Chain.Build установив соответствующие флаги в цепочке перед вызовом Build,

Прозрачность сертификата

Это немного сложнее проверить, но у Google есть соответствующая документация на веб-сайте прозрачности сертификатов. Если сертификат был выдан после 01.01.2015, требуется прозрачность сертификата. Некоторые сертификаты также занесены в белый список Chrome, как указано на странице проекта Chromium.

Надежный корень

Этот довольно простой, но сертификат должен принадлежать доверенному корню. Если сертификат самозаверяющий, он не может быть EV. Это можно проверить еще раз при звонке X509Chain.Build(),

Несколько путей доверия

Сертификат может иметь несколько путей доверия, скажем, если сертификат был выдан корнем, который был кросс-подписан. Если существует несколько путей доверия, все пути должны быть действительными. Аналогично, проверка отзыва должна выполняться со всеми путями. Если какой-либо из путей показывает сертификат как отозванный, то сертификат недействителен.

К сожалению. Насколько я знаю,.NET и даже Win32 не имеют больших средств проверки всех цепочек сертификатов или даже получения более одной цепочки.

Объединяя все это, если они все проходят, то сертификат можно считать сертификатом EV.

Другие вопросы по тегам