В комментариях к статье «Англоязычная кроссплатформенная утилита для просмотра российских квалифицированных сертификатов x509» было пожелание от пользователя Pas иметь не только «парсинг сертификатов», но и получать «цепочки корневых сертификатов и проводить PKI-валидацию, хотя бы для сертификатов на токенах с неизвлекаемым ключом». О получении цепочки сертификатов рассказывалось в одной из предыдущих статей. Правда там речь шла о сертификатах, хранящихся в файлах, но мы обещали добавить механизмы для работы с сертификатами, хранящимися на токенах PKCS#11. И вот что в итоге получилось.
Утилита разбора и просмотра написана на Tcl/Tk и, чтобы в нее добавить просмотр сертификатов на токенах/смарткартах PKCS#11, а также проверку валидности сертификатов потребовалось решить несколько задач:
Для доступа к токену и сертификатам, хранящимя на нем, воспользуемся пакетом TclPKCS11. Пакет распространяется как в бинарниках, так и в исходниках. Исходные коды пригодятся позднее, когда мы будем добавлять в пакет поддержку токенов с российской криптографией. Загрузить пакет TclPKCS11 можно двумя способами, либо командой tcl вида:
Либо загрузить просто как пакет pki::pkcs11, предварительно положив библиотеку tclpkcs11 и файл pkgIndex.tcl в удобный вам каталог (в нашем случае это подкаталог pkcs11 текущего каталога) и добавив его в путь auto_path:
Поскольку нас интересуют токены прежде всего с поддержкой российской криптографии, то из пакета TclPKCS11 мы будем задействовать следующие функции:
Первая функция ::pki::pkcs11::loadmodule предназначена для загрузки библиотеки PKCS#11, которая поддерживает токен/смарткарту, на котором находятся сертификаты. Библиотека может быть получена либо при приобретении токена, либо загружена из Интернета или она была предустановлена на компьютере. В любом случае надо знать какая библиотека поддерживает ваш токен. Функция loadmodule возвращает указатель (handle) на загруженную библиотеку:
Соответственно есть функция выгрузки загруженной библиотеки:
После того как была загружена библиотека и у нас есть ее handle можно получить список слотов, поддерживаемых этой библиотекой:
В данном примере список содержит 15 (пятнадцать от 0 до 14) элементов. Именно столько слотов может поддерживать библиотека токенов семейства RuToken. В свою очередь каждый элемент списка сам является списком из трех элементов:
Первый элемент списка – это номер слота. Второй элемент списка это метка, находящегося в слоте токена (32 байта). Если слот пуст, то второй элемент содержит 32 пробела. И последний, третий элемент списка содержит флаги. Мы не будем рассматривать все множество флагов. Нас интересует в этих флагах только наличие флага TOKEN_PRESENT. Именно этот флаг говорит о том, что в слоте находится токен, а на токене могут находиться интересующие нас сертификаты. Флаги очень полезная вещь, они описывают состояние токена, состояние PIN –кодов и т.д. На основание значения флагов проводится управление токенами PKCS#11:
Теперь ничто не мешает написать процедуру slots_with_token, которая будет возвращать список слотов с метками находящихся в них токенов:
Если выполнить этот скрипт, предварительно сохранив его в файле slots_with_token.tcl, то в результате получим:
Из 15 доступных слотов для данной библиотеки задействовано только два, нулевой и первый.
Теперь ничего не мешает получить список сертификатов, находящихся на том или ином токене:
Каждый элемент списка содержит сведения об одном сертификате. Для получения сведений из сертификата используется функция ::pki::pkcs11::listcerts использует в свою очередь функцию ::pki::x509::parse_cert из пакета pki. Но функция ::pki::pkcs11::listcerts дополняет этот список данные, присущими протоколу PKCS#11, а именно:
Напомним, что остальные элементы в основном определяются функцией pki::parse_cert.
Ниже представлена процедура, получения списка меток (listCert) сертификатов (CKA_LABEL, pkcs11_label) и массива распарсенных сентификатоы (::certs_p11). Ключом для доступа к элементу массива сертификатов служит метка сертификата (CKA_LABEL, pkcs11_label):
А теперь, когда мы имеем распарсенные сертификаты, мы спокойно отображаем в combobox список их меток:
Как распарсить ГОСТ-овые публичные ключи мы рассматривали в предыдущей статье.
Два слова об экспорте сертификата. Сертификаты экспортируются как в PEM-кодировке, так и DER-кодировке (кнопки DER, PEM-формат). Для преобразования в PEM-формат в пакете pki имеется удобная функция pki::_encode_pem:
например:
Выбрав метку септификата в combobox, мы получаем доступ к телу сертификата:
Дальнейший механизм разбора сертификата и его отображения был ранее рассмотрен здесь.
При разборе сертифмката в переменных ::notbefore и ::notafter хранится дата, с которой сертификат может использоваться в криптографических операциях (подписать, зашифровать и т.д.), и дата окончания срока действия сертификата. Процедура проверки срока действия сертификата имеет вид:
Возвращаемый список содержит два элемента. Первый элемент может содержать либо 0 (ноль) либо 1 (один). Значение «1» указывает на то, что сертификат действует, а 0 – на то, что сертификат не действует. Причина по которой не действует сертификат раскрывается во втором элементе. Этот элемент может содержать одно из трех значений:
Валидность сертификата определяется не только периодом его действия. Действие сертификата может быть приостановлено или прекращено удостоверяющим центром, как по его инициативе, так и по заявлению владельца сертификата, например при утрате носителя с закрытым ключом. В этом случае сертификат включается удостоверяющим центром в список отозванных сертификатов СОС/CRL, которые распространяются УЦ. Как правило, точка распространения CRL включается в сертификат. Именно по списку отозванных сертификатов и проверяется валидность сертификата.
Первым шагом необходимо получить СОС, затем его распарсить и проверить по нему сертификат.
Список точек выдачи СОС/CRL находится в расширении сертификата с oid-ом 2.5.29.31 (id-ce-cRLDistributionPoints):
Собственно загрузка файла с СОС/CRL ведется следующим образом:
Собственно для загрузки СОС/CRL используется процедура readca:
В переменной dir хранится путь к каталогу, в котором будет сохранен СОС/CRL, а в переменной url – ранее полученный список точек распространения CRL.
При получении СОС/CRL неожиданно пришлось столкнуться с тем, что для некоторых сертификатов этот список приходиться получать по протоколу https (tls) в анонимном режиме. Честно говоря, это удивительно: список CRL это публичный документ и его целостность защищена электронной подписью и иметь доступ к нему по анонимному https на мой взгляд перебор. Но делать нечего, приходится подключать пакет tls – package require tls.
Если СОС/CRL загрузить не удалось, то валидность сертификата проверена быть не может, если только в сертификате не указана точка доступа с сервису OCSP. Но об этом речь пойдет в одной из следующих статей.
Итак, сертификат для проверки есть, список СОС/CRL есть, осталось проверить по нему сертификт. К сожалению, в пакете pki отсутствуют соответствующие функции. Поэтому пришлось написать процедуру для проверки валидности сертификата (его неотозванности) по списку отозванных сертификатов
Параметрами этой функции являются список отозванных сертификатов (crl), серийный номер проверяемого сертификата (sernum) и его издатель (issuer).
Список отозванных сертификатов (crl) загружается следующим образом:
Серийный номер проверяемого сертификата (sernum) и его издатель (issuer) берутся из распарсенного сертификата и сохраненные в переменных ::sncert и ::issuercert.
Все процедуры можно найти в исходном коде. Исходный код утилиты и ее дистрибутивы для платформ Linux, OS X (macOS) и MS Windows можно найти здесь
В утилите также сохранена возможность просмотра и проверки сертификатов, хранящихся в файле:
Кстати, просматриваемые сертификаты из файлов, также можно экспортировать, как и хранящиеся на токене. Это позволяет легко конвертировать файлы с сертификатами из DER-формата в PEM и наоборот.
Теперь у нас есть единый просмоторщик для сертификатов хранящихся как в файлах, так и на токенах/смаркартах PKCS#11.
Да, упустил главное, для проверки валидности сертификата надо нажать кнопку «Дополнительно» («Additionaly») и выбрать пункт меню «Валидность по СОС/CRL» («Validaty by CRL») или нажать правую кнопку мыши и при нахождении курсора на основном информационном поле и также выбрать пункт меню «Валидность по СОС/CRL» («Validaty by CRL»):
На данном скриншоте показан просморт и проверка валидности сертификатов, находящихся в облачном токене.
В заключении отметим следующее. В своих комментариях к статье пользователь Pas очень правильно заметил про токены PKCS#11, что они «сами все умеют считать». Да, токены фактически являются криптографическими компьютерами. И в следующих статьях мы поговорим не только о том как проверяются сертификаты по OCSP-протоколу, но и о том как задействовать криптографические механизмы (речь идет, конечно, о ГОСТ-криптографии) токенов/смартарт для вычисления хэша (ГОСТ Р 34-10-94/2012), формирования и проверки подписи и т.п.
Утилита разбора и просмотра написана на Tcl/Tk и, чтобы в нее добавить просмотр сертификатов на токенах/смарткартах PKCS#11, а также проверку валидности сертификатов потребовалось решить несколько задач:
- определиться с механизмом получения сертификатов с токена/смарт карты;
- проверить сертификат по списку отозванных сертификатов CRL;
- проверить сертификат на валидность по механизму OCSP.
Доступ к токену PKCS#11
Для доступа к токену и сертификатам, хранящимя на нем, воспользуемся пакетом TclPKCS11. Пакет распространяется как в бинарниках, так и в исходниках. Исходные коды пригодятся позднее, когда мы будем добавлять в пакет поддержку токенов с российской криптографией. Загрузить пакет TclPKCS11 можно двумя способами, либо командой tcl вида:
load <библиотека tclpkcs11> Tclpkcs11
Либо загрузить просто как пакет pki::pkcs11, предварительно положив библиотеку tclpkcs11 и файл pkgIndex.tcl в удобный вам каталог (в нашем случае это подкаталог pkcs11 текущего каталога) и добавив его в путь auto_path:
#lappend auto_path [file dirname [info scrypt]]
lappend auto_path pkcs11
package require pki
package require pki::pkcs11
Поскольку нас интересуют токены прежде всего с поддержкой российской криптографии, то из пакета TclPKCS11 мы будем задействовать следующие функции:
Сразу оговоримся, что функции login и logout здесь рассматриваться не будут. Это связано с тем, что в рамках этой статьи мы будем иметь дело только с сертификатами, а они являются публичными объектами токена. Для доступа к публичным объектам нет необходимости авторизовываться через PIN-код на токене.::pki::pkcs11::loadmodule <filename> -> handle ::pki::pkcs11::unloadmodule <handle> -> true/false ::pki::pkcs11::listslots <handle> -> list: slotId label flags ::pki::pkcs11::listcerts <handle> <slotId> -> list: keylist ::pki::pkcs11::login <handle> <slotId> <password> -> true/false ::pki::pkcs11::logout <handle> <slotId> -> true/false
Первая функция ::pki::pkcs11::loadmodule предназначена для загрузки библиотеки PKCS#11, которая поддерживает токен/смарткарту, на котором находятся сертификаты. Библиотека может быть получена либо при приобретении токена, либо загружена из Интернета или она была предустановлена на компьютере. В любом случае надо знать какая библиотека поддерживает ваш токен. Функция loadmodule возвращает указатель (handle) на загруженную библиотеку:
set filelib "/usr/local/lib64/librtpkcs11ecp_2.0.so"
set handle [::pki::pkcs11::loadmodule $filelib]
Соответственно есть функция выгрузки загруженной библиотеки:
::pki::pkcs11::unloadmodule $handle
После того как была загружена библиотека и у нас есть ее handle можно получить список слотов, поддерживаемых этой библиотекой:
::pki::pkcs11::listslots $handle
{0 {ruToken ECP } {TOKEN_PRESENT RNG LOGIN_REQUIRED USER_PIN_INITIALIZED TOKEN_INITIALIZED REMOVABLE_DEVICE HW_SLOT}}
{1 { } {REMOVABLE_DEVICE HW_SLOT}}
. . .
{14 { } {REMOVABLE_D
EVICE HW_SLOT}}
В данном примере список содержит 15 (пятнадцать от 0 до 14) элементов. Именно столько слотов может поддерживать библиотека токенов семейства RuToken. В свою очередь каждый элемент списка сам является списком из трех элементов:
{{номер слота} {метка токена} {флаги слота и токена}}
Первый элемент списка – это номер слота. Второй элемент списка это метка, находящегося в слоте токена (32 байта). Если слот пуст, то второй элемент содержит 32 пробела. И последний, третий элемент списка содержит флаги. Мы не будем рассматривать все множество флагов. Нас интересует в этих флагах только наличие флага TOKEN_PRESENT. Именно этот флаг говорит о том, что в слоте находится токен, а на токене могут находиться интересующие нас сертификаты. Флаги очень полезная вещь, они описывают состояние токена, состояние PIN –кодов и т.д. На основание значения флагов проводится управление токенами PKCS#11:
Теперь ничто не мешает написать процедуру slots_with_token, которая будет возвращать список слотов с метками находящихся в них токенов:
#!/usr/bin/tclsh
lappend auto_path pkcs11
package require pki
package require pki::pkcs11
#Список токенов со слотами
proc ::slots_with_token {handle} {
set slots [pki::pkcs11::listslots $handle]
# puts "Slots: $slots"
array set listtok []
foreach slotinfo $slots {
set slotid [lindex $slotinfo 0]
set slotlabel [lindex $slotinfo 1]
set slotflags [lindex $slotinfo 2]
if {[lsearch -exact $slotflags TOKEN_PRESENT] != -1} {
set listtok($slotid) $slotlabel
}
}
#Список найденных токенов в слотах
parray listtok
return [array get listtok]
}
set filelib "/usr/local/lib64/librtpkcs11ecp_2.0.so"
if {[catch {set handle [::pki::pkcs11::loadmodule $filelib]} res]} {
puts "Cannot load library $filelib : $res"
exit
}
#Получаем список слотов
set listslots {}
set listslots [::slots_with_token $handle]
#Если все слоты пустые ждем когда вставят токен
while {[llength $listslots] == 0} {
puts "Вставьте токен"
after 3000
set listslots [::slots_with_token $handle]
}
#Печатаем номер заполненного слота и метку вставленного токена
foreach {slotid labeltok} $listslots {
puts "Number slot: $slotid"
puts "Label token: $labeltok"
}
Если выполнить этот скрипт, предварительно сохранив его в файле slots_with_token.tcl, то в результате получим:
$ ./slots_with_token.tcl
listtok(0) = ruToken ECP
listtok(1) = RuTokenECP20
Number slot: 0
Label token: RuTokenECP20
Number slot: 1
Label token: ruToken ECP
$
Из 15 доступных слотов для данной библиотеки задействовано только два, нулевой и первый.
Теперь ничего не мешает получить список сертификатов, находящихся на том или ином токене:
set listcerts [::pki::pkcs11::listcerts $handle $slotid]
Каждый элемент списка содержит сведения об одном сертификате. Для получения сведений из сертификата используется функция ::pki::pkcs11::listcerts использует в свою очередь функцию ::pki::x509::parse_cert из пакета pki. Но функция ::pki::pkcs11::listcerts дополняет этот список данные, присущими протоколу PKCS#11, а именно:
- элемент pkcs11_ label (в терминологии PKCS#11 атрибут CKA_LABEL);
- элемент pkcs11_id (в терминологии PKCS#11 атрибут CKA_ID);
- элемент pkcs11_handle, содержащий указание на загруженную библиотеку PKCS#11;
- элемент pkcs11_slotid, содержащий номер слота с токеном, на котором находится данный сертификат;
- элемент type, который содержит значение pkcs11 для сертификата, находящегося на токене.
Напомним, что остальные элементы в основном определяются функцией pki::parse_cert.
Ниже представлена процедура, получения списка меток (listCert) сертификатов (CKA_LABEL, pkcs11_label) и массива распарсенных сентификатоы (::certs_p11). Ключом для доступа к элементу массива сертификатов служит метка сертификата (CKA_LABEL, pkcs11_label):
#Список сертификатов
proc listcerttok {handle token_slotlabel token_slotid} {
#Список меток сертификатов на токене
set listCer {}
#Массив распарсенных сертификатов
array set ::arrayCer []
set ::certs_p11 [pki::pkcs11::listcerts $handle $token_slotid]
if {[llength $::certs_p11] == 0} {
puts {Certificates are not on the token:$tokenslotlabel}
return $listCer
}
foreach certinfo_list $::certs_p11 {
unset -nocomplain certinfo
array set certinfo $certinfo_list
set certinfo(pubkeyinfo) [::pki::x509::parse_cert_pubkeyinfo $certinfo(cert)]
set ::arrayCer($certinfo(pkcs11_label)) $certinfo(cert)
lappend listCer $certinfo(pkcs11_label)
}
return $listCer
}
А теперь, когда мы имеем распарсенные сертификаты, мы спокойно отображаем в combobox список их меток:
Как распарсить ГОСТ-овые публичные ключи мы рассматривали в предыдущей статье.
Два слова об экспорте сертификата. Сертификаты экспортируются как в PEM-кодировке, так и DER-кодировке (кнопки DER, PEM-формат). Для преобразования в PEM-формат в пакете pki имеется удобная функция pki::_encode_pem:
set bufpem [::pki::_encode_pem <der-buffer> <Headline> <Lastline>]
например:
set certpem [::pki::encode_pen $cert_der "-----BEGIN CERTIFICATE-----" "-----END CERTIFICATE-----"]
Выбрав метку септификата в combobox, мы получаем доступ к телу сертификата:
#Читаем метку выбранного сертификата
set nick [.saveCert.labExp.listCert get]
#Ищем в списке сертификатов сертификат с выбранной меткой
foreach certinfo_list $::certs_p11 {
unset -nocomplain cert_parse
array set cert_parse $certinfo_list
if {$cert_parse(pkcs11_label) == $nick} {
#Читаем публичный ключ
set cert_parse(pubkeyinfo) [::pki::x509::parse_cert_pubkeyinfo $cert_parse(cert)]
break
}
}
#Тип хранения сертификата file|pkcs11
set ::tekcert "pkcs11"
Дальнейший механизм разбора сертификата и его отображения был ранее рассмотрен здесь.
Проверка срока действия сертификата
При разборе сертифмката в переменных ::notbefore и ::notafter хранится дата, с которой сертификат может использоваться в криптографических операциях (подписать, зашифровать и т.д.), и дата окончания срока действия сертификата. Процедура проверки срока действия сертификата имеет вид:
proc cert_valid_date {} {
# Проверяем валидность сертификата по срокам действия
#Дата начала действия сертификата
set startdate $::notbefore
#Дата окончания действия сертификата
set enddate $::notafter
# Получаем текущее время в секундах
set now [clock seconds]
set isvalid 1
set reason "Certificate is valid"
if {$startdate > $now} {
set isvalid 0
#Срок действия сертификата еще не наступил
set reason "Certificate is not yet valid"
} elseif {$now > $enddate} {
set isvalid 0
#Срок действия сертификата истек
set reason "Certificate has expired"
}
return [list $isvalid $reason]
}
Возвращаемый список содержит два элемента. Первый элемент может содержать либо 0 (ноль) либо 1 (один). Значение «1» указывает на то, что сертификат действует, а 0 – на то, что сертификат не действует. Причина по которой не действует сертификат раскрывается во втором элементе. Этот элемент может содержать одно из трех значений:
- certificate valid (первый элемент списка равен 1):
- certificate is not yet valid (время действия сертификата еще не наступило)
- certificate has expired (срок действия сертификата истек).
Валидность сертификата определяется не только периодом его действия. Действие сертификата может быть приостановлено или прекращено удостоверяющим центром, как по его инициативе, так и по заявлению владельца сертификата, например при утрате носителя с закрытым ключом. В этом случае сертификат включается удостоверяющим центром в список отозванных сертификатов СОС/CRL, которые распространяются УЦ. Как правило, точка распространения CRL включается в сертификат. Именно по списку отозванных сертификатов и проверяется валидность сертификата.
Проверка валидности сертификата по СОС/CRL
Первым шагом необходимо получить СОС, затем его распарсить и проверить по нему сертификат.
Список точек выдачи СОС/CRL находится в расширении сертификата с oid-ом 2.5.29.31 (id-ce-cRLDistributionPoints):
array set extcert $cert_parse(extensions)
set ::crlfile ""
if {[info exists extcert(2.5.29.31)]} {
set ::crlfile [crlpoints [lindex $extcert(2.5.29.31) 1]]
} else {
puts "cannot load CRL"
}
Собственно загрузка файла с СОС/CRL ведется следующим образом:
set filecrl ""
set pointcrl ""
foreach pointcrl $::crlfile {
set filecrl [readca $pointcrl $dir]
if {$filecrl != ""} {
set f [file join $dir [file tail $pointcrl]]
set fd [open $f w]
chan configure $fd -translation binary
puts -nonewline $fd $filecrl
close $fd
set filecrl $f
break
}
#Прочитать CRL не удалось. Берем следующую точку с CRL
}
if {$filecrl == ""} {
puts "Cannot load CRL"
}
Собственно для загрузки СОС/CRL используется процедура readca:
proc readca {url dir} {
set cer ""
#Проверяем тип протокола
if { "https://" == [string range $url 0 7]} {
#должен быть загружен пакет tls
http::register https 443 ::tls::socket
}
#Читаем сертификат в бинарном виде
if {[catch {set token [http::geturl $url -binary 1]
#получаем статус выполнения функции
set ere [http::status $token]
if {$ere == "ok"} {
#Получаем код возврата с которым был прочитан сертификат
set code [http::ncode $token]
if {$code == 200} {
#Сертификат успешно прочитан и будет созвращен
set cer [http::data $token]
} elseif {$code == 301 || $code == 302} {
#Сертификат перемещен в другое место, получаем его
set newURL [dict get [http::meta $token] Location]
#Читаем сертификат с другого сервера
set cer [readca $newURL $dir]
} else {
#Сертификат не удалось прочитать
set cer ""
}
}
} error]} {
#Сертификат не удалось прочитать, нет узла в сети
set cer ""
}
return $cer
}
В переменной dir хранится путь к каталогу, в котором будет сохранен СОС/CRL, а в переменной url – ранее полученный список точек распространения CRL.
При получении СОС/CRL неожиданно пришлось столкнуться с тем, что для некоторых сертификатов этот список приходиться получать по протоколу https (tls) в анонимном режиме. Честно говоря, это удивительно: список CRL это публичный документ и его целостность защищена электронной подписью и иметь доступ к нему по анонимному https на мой взгляд перебор. Но делать нечего, приходится подключать пакет tls – package require tls.
Если СОС/CRL загрузить не удалось, то валидность сертификата проверена быть не может, если только в сертификате не указана точка доступа с сервису OCSP. Но об этом речь пойдет в одной из следующих статей.
Итак, сертификат для проверки есть, список СОС/CRL есть, осталось проверить по нему сертификт. К сожалению, в пакете pki отсутствуют соответствующие функции. Поэтому пришлось написать процедуру для проверки валидности сертификата (его неотозванности) по списку отозванных сертификатов
validaty_cert_from_crl :
proc validaty_cert_from_crl {crl sernum issuer} {
array set ret [list]
if { [string range $crl 0 9 ] == "-----BEGIN" } {
array set parsed_crl [::pki::_parse_pem $crl "-----BEGIN X509 CRL-----" "-----END X509 CRL-----"]
set crl $parsed_crl(data)
}
::asn::asnGetSequence crl crl_seq
::asn::asnGetSequence crl_seq crl_base
::asn::asnPeekByte crl_base peek_tag
if {$peek_tag == 0x02} {
# Номер версии СОС.CRL
::asn::asnGetInteger crl_base ret(version)
incr ret(version)
} else {
set ret(version) 1
}
::asn::asnGetSequence crl_base crl_full
::asn::asnGetObjectIdentifier crl_full ret(signtype)
::::asn::asnGetSequence crl_base crl_issue
set ret(issue) [::pki::x509::_dn_to_string $crl_issue]
#Проверка издателя проверяемого сертификата и СОС/CRL
if {$ret(issue) != $issuer } {
#СОС/CRL издан чужим УЦ
set ret(error) "Bad Issuer"
return [array get ret]
}
binary scan $crl_issue H* ret(issue_hex)
#Дата издания
::asn::asnGetUTCTime crl_base ret(publishDate)
#Следующая дата издания
::asn::asnGetUTCTime crl_base ret(nextDate)
#Список сертификатов отозванных
::asn::asnPeekByte crl_base peek_tag
if {$peek_tag != 0x30} {
#Список сертификатов отозванных пустой
return [array get ret]
}
::asn::asnGetSequence crl_base lcert
# binary scan $lcert H* ret(lcert)
while {$lcert != ""} {
::asn::asnGetSequence lcert lcerti
#Разбираем очередной отозванный сертификат
::asn::asnGetBigInteger lcerti ret(sernumrev)
set ret(sernumrev) [::math::bignum::tostr $ret(sernumrev)]
#Проверяем отозванность сертификата по номеру из CRL
if {$ret(sernumrev) != $sernum} {
continue
}
#Сертификат отозван. Определяем дату отзыва
::asn::asnGetUTCTime lcerti ret(revokeDate)
if {$lcerti != ""} {
#Разбираем причину отзыва
::asn::asnGetSequence lcerti lcertir
::asn::asnGetSequence lcertir reasone
::asn::asnGetObjectIdentifier reasone ret(reasone)
::asn::asnGetOctetString reasone reasone2
::asn::asnGetEnumeration reasone2 ret(reasoneData)
}
break;
}
return [array get ret]
}
Параметрами этой функции являются список отозванных сертификатов (crl), серийный номер проверяемого сертификата (sernum) и его издатель (issuer).
Список отозванных сертификатов (crl) загружается следующим образом:
set f [open $filecrl r]
chan configure $f -translation binary
set crl [read $f]
close $f
Серийный номер проверяемого сертификата (sernum) и его издатель (issuer) берутся из распарсенного сертификата и сохраненные в переменных ::sncert и ::issuercert.
Все процедуры можно найти в исходном коде. Исходный код утилиты и ее дистрибутивы для платформ Linux, OS X (macOS) и MS Windows можно найти здесь
В утилите также сохранена возможность просмотра и проверки сертификатов, хранящихся в файле:
Кстати, просматриваемые сертификаты из файлов, также можно экспортировать, как и хранящиеся на токене. Это позволяет легко конвертировать файлы с сертификатами из DER-формата в PEM и наоборот.
Теперь у нас есть единый просмоторщик для сертификатов хранящихся как в файлах, так и на токенах/смаркартах PKCS#11.
Да, упустил главное, для проверки валидности сертификата надо нажать кнопку «Дополнительно» («Additionaly») и выбрать пункт меню «Валидность по СОС/CRL» («Validaty by CRL») или нажать правую кнопку мыши и при нахождении курсора на основном информационном поле и также выбрать пункт меню «Валидность по СОС/CRL» («Validaty by CRL»):
На данном скриншоте показан просморт и проверка валидности сертификатов, находящихся в облачном токене.
В заключении отметим следующее. В своих комментариях к статье пользователь Pas очень правильно заметил про токены PKCS#11, что они «сами все умеют считать». Да, токены фактически являются криптографическими компьютерами. И в следующих статьях мы поговорим не только о том как проверяются сертификаты по OCSP-протоколу, но и о том как задействовать криптографические механизмы (речь идет, конечно, о ГОСТ-криптографии) токенов/смартарт для вычисления хэша (ГОСТ Р 34-10-94/2012), формирования и проверки подписи и т.п.