Я пытаюсь найти надежный способ проверить приложение С# (.Net 4.0), если X509Certificate (или X509Certificate2) имеет флаг "Расширенная проверка" (EV). Кто-нибудь знает лучший метод?
Как проверить, включен ли сертификат X509 "Расширенная проверка"?
Ответ 1
Вы можете проверить, содержит ли X509Certificate
один из этих OId. Кроме того, вы можете проверить источник Chromium для списка реализованных OIds. Здесь вы можете найти Source . Если вы хотите придерживаться Firefox, вы можете захватить реализацию здесь.
Теперь я обновил свой источник и протестировал его. Я написал небольшой метод для проверки X509Certificate2
на OId-List из Википедии/Хром. В этом методе я использую Википедию-Список, возможно, лучше взять Chromium-List.
Как сохраняется OId?
Каждый CA
имеет один или несколько ObjectIds OId
s. Они не сохраняются как расширение, как вы могли догадаться, они сохраняются как запись внутри расширений политики. Чтобы получить точное расширение, рекомендуется использовать Oid самого Policy Extension
, а затем использовать дружественное имя. OId расширений политики 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;
}
Если кто-то знает лучший способ достичь этой цели, сообщите нам.
Ответ 2
Я думал, что отправлю более полный ответ, хотя этот вопрос довольно старый. Я не откажусь от существующего ответа, чтобы этот был завершен.
В сертификате EV есть несколько проверок, которые необходимо передать, чтобы браузер считал, что сертификат EV.
- Что сертификат имеет идентификатор политики, который, как известно, является политикой EV.
- Отпечаток корня сертификата совпадает с закрепленным идентификатором политики.
- Сертификат должен пройти онлайн-проверку отзыва.
- Если сертификат notBefore (дата выпуска) после 1/1/2015, сертификат должен поддерживать прозрачность сертификата.
- Сертификат должен быть выдан доверенным корневым.
- Что все цепочки действительны, если существует несколько путей доверия.
Пусть рассекает каждое из них.
Идентификатор политики
У сертификата есть расширение, называемое идентификаторами политики. Доступ к расширениям можно получить из свойства X509Certificate2.Extensions
. Расширение идентификатора политики имеет Идентификатор объекта ( "OID" ) 2.5.29.32
. Поэтому мы можем получить необработанное расширение, используя что-то вроде этого:
var extension = certificate.Extensions["2.5.29.32"]
Если это возвращает значение null, а это означает, что политики вообще нет, вы можете с легкостью утверждать, что это не сертификат EV.
Скорее всего, хотя у сертификата есть какая-то политика. В этом случае вам необходимо декодировать данные. Атрибут предоставит его вам в сыром ASN.1, нам нужно сделать из этого смысл.
К сожалению, в .NET нет ничего, что могло бы сделать это сегодня. Однако CryptDecodeObjectEx
может сделать это, если вы используете платформу invoke. Специфика при этом я не буду, но есть много информации, чтобы показать, как вызвать эту функцию. Вы хотите вызвать его с параметром lpszStructType, установленным в значение (IntPtr)16
. Это вернет вам структуру CERT_POLICIES_INFO
, которая имеет счетчик и указатель на массив структур CERT_POLICY_INFO
. Эта структура имеет поле на нем, называемое pszPolicyIdentifier
. Это наша политика OID, которая нас интересует.
Каждый орган сертификации имеет один или несколько идентификаторов OID, которые они используют для создания сертификата как EV. Каждый ЦС документирует их на своей странице политик. Тем не менее, лучшим местом для получения обновленного списка этого является, вероятно, Chromium Source Code.
Если сертификат имеет политику, которая соответствует одному из этих OID, мы можем перейти к следующей проверке.
Корневой отпечаток
Если вы посмотрите на источник Chromium в приведенной выше ссылке, вы увидите в дополнение к идентификаторам политик, он также сохранит отпечаток SHA256 от корня.
Это связано с тем, что в дополнение к сертификату, имеющему надлежащий OID, он должен быть выдан CA, чей отпечаток пальца совпадает. В источнике 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 есть соответствующая документация на веб-сайте Сертификат прозрачности. Если сертификат был выпущен после 1/1/2015, требуется прозрачность сертификата. Некоторые сертификаты также отмечены Chrome, как указано на странице Страница проекта Chromium.
Доверенный корень
Это довольно прямолинейно, но сертификат должен принадлежать доверенному корню. Если сертификат подписан сам по себе, он не может быть EV. Это можно снова проверить при вызове X509Chain.Build()
.
Несколько трасс доверия
Возможно, у сертификата есть несколько путей доверия, скажем, если сертификат был выпущен корнем, который был перекрестно подписан. Если существует несколько путей доверия, все пути должны быть действительными. Аналогично проверка отзыва должна выполняться со всеми путями. Если какой-либо из путей показывает сертификат как отозванный, то сертификат недействителен.
К сожалению,.NET и даже Win32 не имеют отличного средства проверки всех цепочек сертификатов или даже получения более одной цепочки, насколько мне известно.
Объединив все это, если все они пройдут, сертификат можно считать сертификатом EV.