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

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


person KenD    schedule 05.02.2013    source источник
comment
Вы просто хотите проверить, является ли сертификат сертификатом EV, или вы также хотите выполнить фактическую расширенную проверку?   -  person Bruno    schedule 05.02.2013


Ответы (2)


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

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


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

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

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

Чтобы получить внутреннее содержимое расширений политики, мы можем использовать System.Security.Cryptography.AsnEncodedData для преобразования его в удобочитаемый string. Сама строка содержит политики, которые нам нужно сопоставить с нашим string[], чтобы убедиться, что он содержит один из OId 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;
    }

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

person Dennis Alexander    schedule 05.02.2013
comment
Фантастический - это приводит меня туда, где я должен быть. Благодарю вас! - person KenD; 05.02.2013
comment
К сожалению, это не работает. Попытка заставить это работать с startssl.com выглядит нормально. Если вы попробуете личный SSL-сертификат, это тоже скажет вам, что это EV. Я понял, что OID сохранен как CertificatePolicy. В настоящее время я тестирую CAPI, чтобы получить список этих политик, но я не могу получить доступ к этому списку. Это было бы решением. - person Dennis Alexander; 06.02.2013
comment
Я исправил эту проблему и переписал свой ответ. Теперь он содержит метод для проверки и некоторый источник для его быстрого тестирования. Кроме того, я пропустил несколько строк, чтобы понять, как этого можно достичь. - person Dennis Alexander; 06.02.2013
comment
Этот код можно обмануть, заставив думать, что у него есть сертификат EV, хотя на самом деле его нет. Представьте себе сертификат, который включает OID Verisign EV, но затем его подписывает Digicert. Digicert, вероятно, было бы все равно, если бы он не конфликтовал с их OID EV. Или даже самоподписанный сертификат может обмануть его, в зависимости от других проверок. Чтобы исправить это, я считаю, что OID должен быть связан с эмитентом. - person jww; 12.05.2014
comment
Спасибо за ваш комментарий. Это даже повысит уровень безопасности. - person Dennis Alexander; 24.10.2014
comment
Это не всегда работает. У меня есть сертификат подписи кода EV (на токене SafeNet 5110. Корнем является VeriSign, а промежуточным — Symantec Class 3 Extended Validation Code Signing CA — G2), и этот фрагмент кода не распознает этот сертификат как EV. - person Melvyn; 10.08.2020

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

Сертификат 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"]

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

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

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

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

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

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

Если вы посмотрите на источник Chromium по приведенной выше ссылке, вы увидите, что в дополнение к идентификаторам политики он также хранит отпечаток корня 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 есть соответствующая документация на веб-сайте Certificate Transparency< /а>. Если сертификат был выдан после 01.01.2015, требуется прозрачность сертификата. Некоторые сертификаты также занесены Chrome в белый список, как указано в Страница проекта Chromium.

Доверенный корень

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

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

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

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

Сочетая все это, если все они пройдены, сертификат можно считать сертификатом EV.

person vcsjones    schedule 21.04.2016