Pull to refresh

Криптографические токены PKCS#11. Графическая утилита «с функцией подписания и добавлением метки времени». Часть 1

Reading time8 min
Views6.5K
В комментариях к статье «Использование механизмов криптографических токенов PKCS#11 в скриптовых языках» читатель kovserg написал:
«С нетерпением ждём статью с функцией подписания документа и добавлением метки времени».
Еще раньше другой участник хабра pas писал о том, что было бы здорово для токенов PKCS#11, «которые сами все умеют считать» (имеются ввиду прежде всего криптографические операции по генерации ключей, формирования и проверки электронной), избавиться от всевозможных прослоек и иметь одну утилиту, которая могла бы, используя механизмы самого токена, и формировать запрос на сертификат, и подписывать документы, проверять подпись документов, проверять подпись и валидность сертификатов.

Знакомство с утилитой


В итоге мы представляем на суд пользователей утилиту

guipkcs7_tclpkcs11/cryptoarmpkcs:



Движущей силой этой утилиты является криптографический токен PKCS#11 c поддержкой российской криптографии как минимум ГОСТ Р 34.10-2012. Если вы собираетесь пользоваться госуслугами и т.п., то необходимо приобрести все же токен, выдерживший сертифицированные испытания в системе сертификации ФСБ России. Если это ваш внутренний электронный документооборот, то это, естественно, на ваше усмотрение. Токены PKCS#11 могут быть разные: программные, аппаратные и даже облачные. Утилита написана на скриптовом языке Tcl/Tk. Когда вы приобретаете токен PKCS#11 не забудьте получить или поинтересоваться, где можно скачать библиотеки для приобретаемого токена для тех или иных платформ. Библиотеки, как правило, находятся в свободном доступе. Для доступа к криптографическим и другим возможностях токена в утилите используется пакет TclPKCS11. Работа утилиты начинается с выбора библиотеки, которая поддерживает ваши токены. Отметим, что библотеки могут поддерживать одновременно работу с несколькими токенами (combobox «выберите токен/смаркарту»):



Может возникнуть вопрос, а что делать, если токен не проинициализирован или требуется поменять PIN-коды и т.д.? Ответ простой – воспользоваться утилитой конфигурирования токенов p11conf.

Для обновления списка токенов (отключили токен, добавили новый) достаточно щелкнуть
по иконке , находящейся справа от combobox-а «Выберите токен/смарткарту»
В combox-е «Сертификат» представлены метки всех сертификатов, хранящихся на выбранном (текущем) токене:



Если нажать на иконку , находящуюся справа от combobox-а «Сертификат», то появится окно с содержимым сертификата:



Если нажать на кнопку «Save/Сохранить» в окне просмотра, то содержимое разобранного сертификата в текстовом формате будет сохранено в указанном вами файле.
Для просмотра информации о текущем токене можно нажать кнопку « 6. Информация на токене» или подвести курсор на метку токена:



Для того, чтобы узнать, какие криптографические механизмы поддерживает текущий токен, достаточно нажать на кнопку « 5. Список механизмов»:



Создаем запрос на сертификат

Переходим к основным функциям утилиты. И первой такой функцией является создание запроса на сертификат (кнопка « 3. Запрос на сертификат»):



Сообщение «Токен не поддерживает ключи …» появляется в том случае, если выбранный токен не поддерживает генерацию данного типа ключа. В этом случае необходимо выбрать другой тип ключа или другой токен. В данном примере можно использвать токен RuToken ECP 2.0 (см. второй скриншот). Combobox «Владелец сертификата» позволяет указать кому будет принадлежать сертификат: физическому лицу, юридическому лицу или индивидуальному предпринимателю. В зависимости от этого будут формироваться на следующих страницах поля для заполнения.

Значимым полем здесь является поле «Наименование СКЗИ». Оно является неотъемлемой частью квалифицированного сертификата и указывает на то, каким СКЗИ будет сгенерирована ключевая пара. Наименование СКЗИ вы можете узнать в формуляре на изделие или в момент приобретения токена. Совпадает ли поле тип в информации о токене с наименованием СКЗИ, я затрудняюсь сказать. Так что лучше посмотреть формуляр. Нажимаем кнопку Next и заполняем требуемые поля:



При заполнении утилита старается контролировать правильность заполнения полей (email, ОГРН и т.п.), выдавая соответствующие предупреждения. После заполнения основных полей будет предложено определиться с форматом и местом хранения на вашем компьютере запроса на сертификата. От вас также потребуется ввести PIN-код вашего токена:



После нажатия кнопки «Next» вам будет предложено еще раз взглянуть на то, что вы ввели, и утвердить ваше решение нажатием клавиши «Finish»:



И, если вы нажмете кнопку «Finish», то на вашем токене будет сгенерирована ключевая пара, создан и подписан запрос:



А можно убедиться, что ключи сгенерированы и сохранены на токене? Да. Нажимаем кнопку « 7. Объекты токена», вводим PIN-код для доступа к токену и ищем объекты СКО_PRIVATE_KEY и CKO_PUBLIV_KEY, у которых метки совпадают с полем «COMMON NANE» (CN), которое вы заполнили при создании запроса на сертификат. В нашем примере это было «Всесильный Хабр»:



Посмотрели и сразу уходите на другую страницу. Лучшим доказательством, что ключевая пара успешно создана, является наличие самого подписанного запроса. Для того чтобы убедиться в этом нажимаем кнопку «Просмотр запроса/сертификата», выбираем сохраненный запрос, нажимаем кновку «Просмотр запроса на сертификат» и смотрим информацию о ключевой паре:



После того как убедились, что запрос успешно создан, надежно прячем токен с закрытым ключом или кладем поближе к сердцу (люди старшего поколения знают, как хранили партийный или комсомольский билеты), копируем запрос на флэшку, берем необходимые документы (паспорт и т.д) и идем в УЦ для получения сертификата. Да, если это не ведомственный УЦ, то еще придется заплатить деньги. Все как с паспортом.

Отправляемся за сертификатом в УЦ
В данном случае для выдачи сертификата достопочтенному Хабру, мы воспользуемся УЦ со страниц все тоже Хабра. УЦ начинает рассмотрение нашей заявки:



После того как заявка поступила в базу данных УЦ, уполномоченный администратор рассматривает ее и либо отклоняет либо утверждает:



После утверждения заявки заявитель вместе с уполномоченным лицом от УЦ определяют цели использования сертификата:



И после этого уже ничто не мешает выпуску сертификата:



После выпуска сертификата сотрудник УЦ экспортирует выпущенный сертификат на флэшку Достопочтенного Хабра:



Кладем сертификат на токен


И вот счастливый обладатель сертификата возвращается в родные пенаты и первым делом решает положить сертификат на токен рядом с ключевой парой. Для этого на главном окне утилиты нажимаем кнопку « 4. Просмотр запроса/сертификата», выбираем файл с сертификатом и операцию «Просмотр сертификата» и нажимаем кнопку выполнить операцию»:



Мы можем также проверить валидность сертификата (но мы еще не успели его отозвать) или корректность его подписи, выбрав соответствующую операцию:



Код утилиты проверки подписи сертификата
#!/usr/bin/env tclsh
package require pki
load ./tclpkcs11.so Tclpkcs11
#Задайте путь к вашей библиотеке PKCS#11
#set pkcs11_module "/usr/local/lib64/librtpkcs11ecp_2.0.so"
set pkcs11_module "/usr/local/lib64/libls11sw2016.so"
puts "Connect the Token and press Enter"
gets stdin yes
set handle [pki::pkcs11::loadmodule $pkcs11_module]
set slots [pki::pkcs11::listslots $handle]
set i 0
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 token_slotlabel $slotlabel
		set token_slotid $slotid
#Найден слот с токеном
		incr i
		break
	}
}
if {$i == 0} {
    puts "Нет ни одного токена. Вставьте."
    exit
}
#Из PEM в DER
proc ::cert_to_der {data} {
    if {[string first "-----BEGIN CERTIFICATE-----" $data] != -1} {
	set data [string map {"\r\n" "\n"} $data]
    }
    array set parsed_cert [::pki::_parse_pem $data "-----BEGIN CERTIFICATE-----" "-----END CERTIFICATE-----"]
    if {[string range $parsed_cert(data) 0 0 ] == "0" } {
#Очень похоже на DER-кодировка "0" == 0x30 
	set asnblock $parsed_cert(data)
    } else {
	set asnblock ""
    }
    return $asnblock
}
proc usage {use error} {
    puts "Copyright(C) Orlov Vladimir (http://museum.lissi-crypto.ru/) 2019"
    if {$use == 1} {
	puts $error
	puts "Usage:\nverify_cert_with_pkcs11 <file with certificate> \[<file with CA certificate>\]\n"
    }
}
set countcert [llength $argv]
if { $countcert < 1 ||  $countcert > 2 } {
    usage 1 "Bad usage!"
    exit
}
set file [lindex $argv 0]
if {![file exists $file]} {
    usage 1 "File $file not exist"
    exit
}
#Проверяемый сертификат cert_user
puts "Loading user certificate: $file"
set fd [open $file]
chan configure $fd -translation binary
set cert_user [read $fd]
close $fd
if {$cert_user == "" } {
    usage 1 "Bad file with certificate user: $file"
    exit
}
set cert_user [cert_to_der $cert_user]
if {$cert_user == ""} {
    puts "User certificate bad"
    exit
}
catch {array set cert_parse [::pki::x509::parse_cert $cert_user]}
if {![info exists cert_parse]} {
    puts "User certificate bad"
    exit
}
#parray cert_parse
if {$countcert == 1} {
    if {$cert_parse(issuer) != $cert_parse(subject)} {
	puts "Bad usage: not self signed certificate"
    } else {
	set cert_CA $cert_user
    }
} else {
    set fileca [lindex $argv 1]
    if {![file exists $fileca]} {
	usage 1 "File $fileca not exist"
	exit
    }
    #Сертификат издателя cert_CA
    puts "Loading CA certificate: $fileca"
    set fd [open $fileca]
    chan configure $fd -translation binary
    set cert_CA [read $fd]
    close $fd
    if {$cert_CA == "" } {
	usage 1 "Bad file with certificate CA=$fileca"
	exit
    }
    set cert_CA [cert_to_der $cert_CA]
    if {$cert_CA == ""} {
	puts "CA certificate bad"
	exit
    }
}
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 token_slotlabel $slotlabel
		set token_slotid $slotid
	}
}
#Ключ от корневого сертификата
catch {array set cert_parse_CA [::pki::x509::parse_cert $cert_CA]}
if {![info exists cert_parse_CA]} {
    puts "CA certificate bad"
    exit
}
#Проверяем издателя
if {$cert_parse(issuer) != $cert_parse_CA(subject)} {
    puts "Bad issuer"
    exit
}
set aa [dict create pkcs11_handle $handle pkcs11_slotid $token_slotid]
set tbs_cert [binary format H* $cert_parse(cert)]
catch {set signature_algo_number [::pki::_oid_name_to_number $cert_parse(signature_algo)]}
if {![info exists signature_algo_number]} {
    set signature_algo_number $cert_parse(signature_algo)
}
switch -- $signature_algo_number {
    "1.2.643.2.2.3" - "1 2 643 2 2 3" { 
#    "GOST R 34.10-2001 with GOST R 34.11-94"
	set digest_algo "gostr3411"
    }
    "1.2.643.7.1.1.3.2" - "1 2 643 7 1 1 3 2" {
#     "GOST R 34.10-2012-256 with GOSTR 34.11-2012-256"
	set digest_algo "stribog256"
    }
    "1.2.643.7.1.1.3.3" - "1 2 643 7 1 1 3 3" { 
#    "GOST R 34.10-2012-512 with GOSTR 34.11-2012-512"
	set digest_algo "stribog512"
    }
    default {
	puts "Неизвестная алгоритм подписи:$signature_algo_number"
	exit
    }
}
#Посчитать хэш от tbs-сертификата!!!!
set digest_hex    [pki::pkcs11::digest $digest_algo $tbs_cert  $aa]
#Получаем asn-структуру публичного ключа
#Создаем список ключевых элементов
binary scan $cert_CA H* cert_CA_hex
array set infopk [pki::pkcs11::pubkeyinfo $cert_CA_hex  [list pkcs11_handle $handle pkcs11_slotid $token_slotid]] 
set lpk [dict create pkcs11_handle $handle pkcs11_slotid $token_slotid]
#Добавляем pybkeyinfo в список ключевых элементов
lappend lpk "pubkeyinfo"
lappend lpk $infopk(pubkeyinfo)
array set lpkar $lpk
puts "Enter PIN user for you token \"$token_slotlabel\":"
gets stdin password
if { [pki::pkcs11::login $handle $token_slotid $password] == 0 } {
    puts "Bad PIN"
    exit
}
if {[catch {set verify [pki::pkcs11::verify $digest_hex $cert_parse(signature) $lpk]} res] } {
    puts "Ошибка проверки подписи=$res"
    exit
}
if {$verify != 1} {
    puts "BAD SIGNATURE=$verify"
} else {
    puts "SIGNATURE OK=$verify"
}


Но нас сейчас интересует операция импорта полученного сертификата на наш токен. Выбираем операцию «Импорт сертификата на токен» и нажимаем кнопку «Выполнить операцию». Утилита проверит электронную подпись сертификата. Для этого от вас потребуется ввести PIN-код к токену. И если все пройдет нормально, то сертификат будет импортирован на токен:



Метку (nickname ) сертификата можно будет лицезреть в списке сертификатов:



Это наш личный сертификат, сертификат для которого есть ключевая пара. И если еще раз просмотреть список объектов на токене, то мы найдем три объекта, имеющую метку «Всесильный Хабр from УЦ 12_512» и одинаковые CKA_ID. Этими тремя объектами являются сам сертификат (CKO_CERTIFICATE), открытый (CKO_PUBLIC_KEY) и закрытый (CKO_PRIVATE_KEY) ключи. Метка для этой тройки объектов устанавливается следующим образом:
<CN владельца сертификат> from <CN издателя сертификата>.

Ниже мы покажем как изменить метку.

Теперь, когда мы положили сертификат на токен, как получить доступ к нему? Для того, чтобы получить доступ к функциям работы с сертификатами, находящимися на токене, достаточно подвести курсор на метку «Сертификат» и нажать правую кнопку мыши:



Подписываем электронной подписью первый документ


Дождемся следующей статьи. Ждать придется пару дней. Что там еще будет рассмотрено можно понять из скриншота:



Продолжение здесь.
Tags:
Hubs:
+4
Comments2

Articles