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

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

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


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

    guipkcs7_tclpkcs11:



    Движущей силой этой утилиты является криптографический токен 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 издателя сертификата>.

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

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



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


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



    Продолжение здесь.
    Поделиться публикацией

    Комментарии 2

      0
      А разве для метки времени не требуется Time Stamp Authority?

      ps: А где принято брать описание на всякие 1.2.643.6.45.1.1.1, 1.2.643.2.45.1.1.4 и тому подобные безобразия?
        +1

        Я сознательно не употреблял выражение "метка времени". Везде на скриншотах присутствует выражение "время подписания документа". Время может браться с компьютера, а также с сервера штампов времени. Но поскольку о работе с подписанными документами речь пойдет во второй части, там и постараемся обо всем этом и поговорить.
        И насчет "безобразий". Я тоже считаю безобразиями, а на УЦ их называют ролями или для чего будет изпользоваться сертификат (откройте спойлер "Отправляемся за сертификатом в УЦ"). Эти роли содержат уйму oid-ов, зачем не понимаю. Нормальный человек самосточтельно в запросе не сможет их прописать. Вот этим и занимаются на УЦ, когда определяют для чего будет нужен сертификат. А некоторые за добавления этих oid-ов еще и деньги берут. Ну это полный отстой. Для того чтобы пойти на Госуслуги, надо прописать в сертификате следующие oid-ы:


        Расширенное использование ключа
        Допустимые цели: 1.3.6.1.5.5.7.3.2
        1.3.6.1.5.5.7.3.4
        1.3.6.1.4.1.311.20.2.2
        1.2.643.100.2.1 (Росреестр)
        1.2.643.2.2.34.6
        1.3.6.1.5.5.7.3.2
        1.3.6.1.5.5.7.3.4
        1.2.643.5.1.24.2.1.3
        1.2.643.6.14 (Центр реализации)
        1.2.643.3.215.4
        1.2.643.3.215.5
        1.2.643.3.215.6
        1.2.643.3.215.7
        1.2.643.3.215.8
        1.2.643.3.215.9
        1.2.643.3.215.11
        1.2.643.3.215.12
        1.2.643.3.215.13
        1.3.6.1.4.1.40870.1.1.1
        1.2.643.2.64.1.1.1
        1.2.643.3.5.10.2.12
        1.2.643.6.3.2
        1.2.643.5.1.24.2.46
        1.2.643.6.45.1.1.1
        1.2.643.5.1.24.2.30
        1.2.643.5.1.28.2
        1.2.643.5.1.28.3
        1.2.643.3.202.1.8

        Ну и как? А вы говорите безобразие. Пусть этим занимаются УЦ

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

        Самое читаемое