OpenSSL и Network Security Services (NSS) — две стороны одной медали / Хабр

OpenSSL и Network Security Services (NSS) — две стороны одной медали / Хабр Сертификаты

Графическая оболочка guinsspy для пакета nss

Первый вопрос, на чем разрабатывать? Было решено на Python-е.

Второй вопрос, на чем писать графический интерфейс? Ответ — Tkinter.

Первоначально gui разрабатывалось на PAGE. Но потом от него отошли. Но контекст остался.

Функционал утилиты базируется на рассмотренных выше утилитах и командах. Помимо них была добавлена еще одна функция «Создать запрос на сертификат», которая базируется на команде «certutil -R»:

В качестве темы оформления виджетов решено было использовать тему Breeze для python3 и тему Arc для python2, поскольку для последнего отсутствует тема Breeze. Для этого надо установить пакет с темами для pythona:

$pip install ttkthemes

Еще нам потребуется

для работы с сертификатами:

$pip install fsb795


Утилиты NSS, на базе которых строится графическая оболочка guinsspy, очень часто запрашавают пароли или PIN-оды через консоль. Единственным способом взаимодействия с ними через графический интерфейс является использование пакета pexpect:

$pip install pexpect

В качестве примере использования пакета pexpect приведем код процедуры импорта контейнера PKCS#12:

importP12
def importP12(frameManager):
    global readpw
    global filename
    tokname = frameManager.STCombobox1.get()
    fp12 = frameManager.SEntry1.get()
    if (fp12 == () or fp12 == ''):
        tkMessageBox.showinfo(title="Импорт контейнера PKCS#12", message='Контейнер не выбранn')
        return (-1, "", "")
    filename = fp12
    if sys.platform != "win32":
        cmd_import_p12 = '"'   patch_win   'pk12util" -i "'   fp12   '" -h "'   tokname   '"  -d "'   NSSname   '"'
        id = pexpect.spawn(cmd_import_p12, timeout=1)
    else:
        cmd_import_p12 = '"'   patch_win   'pk12util" -i "'   fp12   '" -h "'    tokname   '"  -d "'   NSSname   '"'
        id = pexpect.popen_spawn.PopenSpawn(cmd_import_p12, timeout=10)
    while(True):
        ret = id.expect(["Enter Password or Pin", "Enter password for PKCS12 file",pexpect.EOF, pexpect.TIMEOUT])
        if (ret == 0 or ret == 1):
            root.update()
            if (ret == 0):
                password('', tokname, 0)
                pasP11 = readpw
            else:
                password('', os.path.basename(fp12), 1)
                pasP12 = readpw
            if (readpwok == 0):
                if sys.platform != "win32":
                    id.close()
                return (-3, fp12, "")
            if sys.platform != "win32":
                id.send(readpw)
                id.send("r")
            else:
                id.sendline(readpw)
            lseek = 1
        elif (ret == 2):
            break
        elif (ret == 3):
            break
            if sys.platform != "win32":
                id.close()
            return (-1, fp12, "")
    if sys.platform != "win32":
        res = id.before.decode('UTF-8')
        id.close()
    else:
        res = id.before.decode('UTF-8')
    if (res.find("PKCS12 IMPORT SUCCESSFUL") != -1):
        ret = 0
    elif (res.find("SEC_ERROR_BAD_PASSWORD") != -1):
        ret = -1
        return (ret, fp12, "")
    else:
        ret = -2
    return (ret, fp12, res)

Бесконечный цикл (while(True):) в процедуре ждет наступления одного из четырех событий:

ret = id.expect(["Enter Password or Pin", "Enter password for PKCS12 file",pexpect.EOF, pexpect.TIMEOUT])


Первое событие связано с приглашением на ввод пароля или PIN-кода («Enter Password or Pin»).

При его наступлении на экране будет отображаться окно для ввода PIN-кода (левое окно на скриншоте):

Второе событие связано с вводом пароля к контейнеру PKCS#12 («Enter password for PKCS12 file»). При его наступлении на экране будет отображаться окно для ввода пароля к файлу с контейнером PKCS#12 (правое окно на скриншоте).

Третье событие связано с завершением работы утилиты pk12util (pexpect.EOF), а четвертое событие — с завершением работы утилиты по таймауту (pexpect.TIMEOUT).

Исходный код утилиты guinsspy можно найти здесь. Дистрибутив пакета NSS для работы с токенами PKCS#11 с российской криптографией для платформы Linux x86_64 можно найти там же.

Для тестирования токенов с российской криптографией скопируйте папку NSS_GOST_3.52.1_Linux_x86_64 в свой домашний каталог. Создайте скрипт guinsspy_gost.sh:

export LD_LIBRARY_PATH=~/NSS_GOST_3.52.1_Linux_x86_64:$LD_LIBRARY_PATH
export PATH=~/NSS_GOST_3.52.1_Linux_x86_64:$PATH
python3 guinsspy.py


Теперь запустите этот скрипт и работайте с российскими токенами.

Если у вас нет под рукой токена с российской криптографией, то зайдите на вкладку «Создать SW/Cloud токен», которая вам плдскажет как создать программный токен на вашем компьютере или подключиться к

И напоследок, скриншоты создания запроса на сертификат:

Полученный запрос можно будет передать в УЦ CAFL63, выпустить там сертификат, установить на токен, на котором был создан закрытый ключ. А дальше использовать этот сертификат, например, для подписания документов.

Доступ к объектам токена pkcs#11

Для доступа к объектам (ключи, сертификаты) токенов PKCS#11 служит утилита certutil. Отметим, что по своей функциональности утилита certutil не уступает утилите openssl. Для просмотра функциональности утилиты certutil достаточно выполнить команду:

$certutil -H

Но нас сейчас интересует только

. Напомним, что при хранении на токене PKCS#11 и тем и другим, как правило, приписываются атрибуты CKA_ID и CKA_LABEL. Для просмотра списка сертификатов на том или ином токене необходимо выполнить следующую команду:

$certutil -L [-d <хранилище NSS>] [-h <метка токена>]

В качестве метки токена может быть указана реальная метка токена или одно из ключевых слов — all или internal. В первом случае (метка токена all) перебираются все токены, подключенные к хранилищу NSS, а во втором случае (internal или «NSS Certificate DB») будет просматриваться внутренний токен хранилища «NSS Certificate DB».

Например, для получения списка сертификатов, находящихся на токене с меткой «LS11SW2021», модуль доступа к которому зарегистрирован в хранилище NSS “/home/a513/tmp/TEST_NSS” необходимо выполнить следующую команду:

$ certutil -L -d /home/a513/tmp/TEST_NSS -h "LS11SW2021"
Enter Password or Pin for "LS11SW2021":
Certificate Nickname                                  Trust Attributes
                                                      SSL,S/MIME,JAR/XPI
LS11SW2021:TestCA_P11                                 u,u,u
LS11SW2021:clientnss from CryptoArmPKCS               u,u,u
LS11SW2021:ТестШифр                                   u,u,u
LS11SW2021:Thenderbird-60.3.0 from 32                 u,u,u
LS11SW2021:Всесильный Хабр from УЦ 12_512             u,u,u
LS11SW2021:Text4Key                                   u,u,u
LS11SW2021:KmailKleopatra от GnuPG-2001               u,u,u
LS11SW2021:setvernss from CryptoArmPKCS               u,u,u
LS11SW2021:Ф И О from УЦ 12_512                       u,u,u
LS11SW2021:Test 12 512                                u,u,u
LS11SW2021:Kleopatra от GnuPG-2001                    ,,   
$

Список сертификатов, находящихся на токене, выводится в две колонки. В первой колонке дается nicknamе сертификата, а во второй — атрибуты доверия этого сертификата.

Причем, если сертификат имеет закрытый ключ на токене, где он находится, то в этой колонке будет значение «u,u,u».

Если атрибуты не устанавливались, то в колонке будет значение “,,”.

Сертификаты с атрибутами доверия «u,u,u» (у них есть закрытый ключ) могут быть использованы для аутентификации или формирования электронной подписи.

Другие значения атрибутов доверия могут быть установлены для сертификатов, находящихся на встроенном токене «NSS Certificate DB». Но об этом позже.

Что же такое nickname сертификата в NSS?


Для внутреннего токена («NSS Certificate DB») метка токена в nickname может отсутствовать.

Если рассматривать механизмы токенов PKCS#11, то мы увидим, что операции генерации ключей, импорта сертификатов и ключей сами по себе не предусматривают установку значений атрибутов CKA_ID и CKA_LABEL. Это все перекладывается на разработчика прикладного ПО. Но, если вы используете утилиты NSS для работы с токенами, то оказывается, что они берут на себя эти хлопоты.

Для просмотра списка закрытых ключей используется следующая команда:

$certutil -K [-d <хранилиже NSS>] [-h <метка токена>]

При этом распечатывается тип ключа, CKA_ID и CKA_LABEL ключа.

Но вернемся к сертификатам. Для просмотра сертификата, находящегося на токене, в текстовом виде достаточно выполнить команду:

$certutil -L [-d <хранилище NSS>] -n <nickname сертификата>

Например:

certutil -L -d ‘/home/a513/tmp/TEST_NSS’ -n ‘NSS Certificate DB:Тестовый сертификат’

$ certutil -L -d “/home/a513/tmp/TEST_NSS” -n «NSS Certificate DB: Тестовый сертификат»

Certificate:

Data:

Version: 3 (0x2)

Serial Number: 4096 (0x1000)

Signature Algorithm: PKCS #1 SHA-256 With RSA Encryption

Issuer: «E=ca@test.ru,OGRN=1111111111111,INN=222222222222,CN=Удос

товеряюший Центр,OU=Отдел Удостоверя

юший Центр,O=Удостоверяюший Центр,STR

EET=»ул. Хавровская, д. 0″,L=Хабраград,ST=М

осковская область,C=RU”

Validity:

Not Before: Tue Jul 07 08:40:14 2020

Not After: Fri Aug 06 08:40:14 2021

Subject: «E=test@rsa.ru,CN=Тестовый сертификат»

Subject Public Key Info:

Public Key Algorithm: PKCS #1 RSA Encryption

RSA Public Key:

Modulus:

9a:9f:6c:60:94:f7:ec:f7:94:b3:51:01:e2:1a:c5:25:

28:bb:02:77:49:52:4d:99:8a:6e:26:12:55:8f:71:34:

04:da:39:24:f9:b4:6b:d0:0a:42:27:1b:b2:d7:9b:d9:

c3:76:b0:e0:1c:7c:21:ce:79:9f:d5:2b:17:63:cb:94:

5b:d9:b2:53:ff:b9:bf:4f:3d:cf:b7:8d:8a:37:ba:02:

8c:da:d2:0d:fd:46:5b:45:1d:95:64:07:6e:fa:88:0d:

a4:bd:b3:4a:ed:99:f1:fd:73:c5:b6:05:a0:e5:ee:6b:

c3:83:5b:d0:64:05:77:6a:18:d8:c8:28:a1:d0:06:41:

Про сертификаты:  Что такое CSR ключ для SSL-сертификата?

23:0d:bb:87:8a:77:14:fb:6c:5d:af:db:2b:0b:11:a3:

16:1b:2b:05:18:26:a9:b5:00:4a:40:da:b3:05:aa:2a:

67:c0:18:0d:03:f7:d2:b9:ba:7c:36:f9:95:2e:56:81:

a3:09:99:5e:20:10:95:38:10:c9:c1:6f:c3:6c:a6:1b:

78:51:c6:e4:4f:11:bc:c0:22:4b:ca:59:16:f2:45:95:

0d:fd:7b:46:cf:c7:ac:1c:3d:d7:26:fc:ad:80:3e:2c:

21:93:29:32:a6:79:e2:a8:c6:e9:5e:45:34:d3:38:57:

8f:cd:95:5e:91:09:84:34:21:d2:16:29:69:75:4d:a3

Exponent: 65537 (0x10001)

Signed Extensions:

Name: Certificate Basic Constraints

Critical: True

Data: Is not a CA.

Name: Certificate Key Usage
Usages: Digital Signature
Key Encipherment
Key Agreement

Name: Certificate Type
Data: <SSL Client,S/MIME>

Name: Extended Key Usage
TLS Web Client Authentication Certificate
E-Mail Protection Certificate

Name: Certificate Subject Key ID
Data:
26:a1:b3:98:1c:fe:62:ba:23:81:96:37:3f:08:bd:70:
d6:f2:b1:46

Name: Certificate Authority Key Identifier
Key ID:
0a:b6:f6:87:64:1d:8e:b3:63:08:29:9f:21:59:ad:47:
d8:ea:07:f4
Issuer:
Directory Name: «E=ca@test.ru,OGRN=1111111111111,INN=22222222
2222,CN=Удостоверяюший Центр,OU=Отд
ел Удостоверяюший Центр,O=Удост
оверяюший Центр,STREET=»ул. Хавровс
кая, д. 0″,L=Хабраград,ST=Московска
я область,C=RU”
Serial Number:
00:a2:9b:22:32:3e:a7:3d:d8

Name: Certificate Subject Alt Name
RFC822 Name: «test@rsa.ru»

Name: Certificate Issuer Alt Name
Error: Parsing extension: Certificate extension value is invalid.
Data: Sequence {
}

Signature Algorithm: PKCS #1 SHA-256 With RSA Encryption
Signature:
2f:75:7e:71:9e:15:5c:97:fe:a2:e1:2a:52:39:56:55:
e0:62:60:bc:5f:6d:c2:b6:ec:cd:8b:10:b3:b1:3f:e5:
d6:d1:5f:a5:fa:61:c1:ce:3e:db:6a:2f:b2:13:46:8d:
67:cf:18:09:61:97:01:45:bc:99:bb:0c:d6:0a:a3:03:
87:0a:8e:10:3a:d5:e3:94:6d:4a:24:fa:c3:40:0b:43:
c2:3b:00:56:06:c4:d2:fc:b2:7e:e9:00:e5:2f:4b:e2:
3a:91:49:ce:f8:c3:60:ec:01:74:d8:1a:3b:af:e6:f6:
91:db:c5:f1:d7:de:be:18:38:47:41:8a:e2:ef:80:91:
10:54:41:ae:55:22:6f:d7:8c:fa:46:b6:b6:2a:ee:6a:
0c:c9:03:18:af:4e:93:6c:61:f3:b4:78:0c:61:93:f1:
d8:1b:00:c3:e5:29:9a:08:0a:f8:31:67:88:3d:c3:88:
7a:60:c0:c4:52:94:25:56:e5:a3:df:7d:58:c5:df:9a:
c7:22:7e:2c:f6:fb:2c:bf:b7:7f:c5:ca:2b:0f:8c:20:
77:b9:1f:e0:62:5a:3d:d4:6f:12:ea:c8:51:67:a5:75:
ad:e9:ac:9e:4e:2e:2d:34:80:e7:d8:64:f6:8f:2f:33:
32:1f:8b:bc:9c:e8:77:4a:ee:7b:84:31:ec:28:e9:70
Fingerprint (SHA-256):
96:F4:A5:FA:6D:8A:F8:7E:A6:10:49:BD:43:34:C1:92:C6:7D:FF:63:41:8E:69:C0:AC:34:6B:CB:63:7B:56:31
Fingerprint (SHA1):
B6:91:9B:C6:7A:45:9C:92:FD:E7:C7:33:00:FA:91:DF:7D:5F:00:21

Mozilla-CA-Policy: false (attribute missing)
Certificate Trust Flags:
SSL Flags:
User
Email Flags:
User
Object Signing Flags:
User
$

Настройка двухфакторной аутентификации в домене windows

Теоретическая часть:

Служба каталога Active Directory поддерживает возможность аутентификации с помощью смарт-карты и токена, начиная с Windows 2000. Она заложена в расширении PKINIT (public key initialization — инициализация открытого ключа) для протокола Kerberos RFC 4556 .

Протокол Kerberos был специально разработан для того, чтобы обеспечить надежную аутентификацию пользователей. Он может использовать централизованное хранение аутентификационных данных и является основой для построения механизмов Single Sing-On. Протокол основан на ключевой сущности Ticket (билет).

OpenSSL и Network Security Services (NSS) — две стороны одной медали / Хабр

Ticket (билет) является зашифрованным пакетом данных, который выдается доверенным центром аутентификации, в терминах протокола Kerberos — Key Distribution Center (KDC, центр распределения ключей).

Когда пользователь выполняет первичную аутентификацию после успешного подтверждения его подлинности, KDC выдает первичное удостоверение пользователя для доступа к сетевым ресурсам — Ticket Granting Ticket (TGT).

В дальнейшем при обращении к отдельным ресурсам сети, пользователь, предъявляет TGT, получает от KDC удостоверение для доступа к конкретному сетевому ресурсу — Ticket Granting Service (TGS).

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

Расширение PKINIT позволяет использовать двухфакторную аутентификацию по токенам или смарт-картам на этапе предаутентификации Kerberos.

Вход в систему может быть обеспечен, как при использовании службы каталога домена, так и локальной службы каталога. TGT создается на основе электронной подписи, которая вычисляется на смарт-карте или токене.

Все контроллеры доменов должны иметь установленный сертификат Domain Controller Authentication, или Kerberos Authentication, т. к. реализуется процесс взаимной аутентификации клиента и сервера.

Практика:

Приступим к настройке.

Сделаем так, чтобы в домен под вашей учетной записью можно было зайти только по предъявлению токена и зная PIN-код.

Для демонстрации мы будем использовать Рутокен ЭЦП PKI производства компании «Актив».

OpenSSL и Network Security Services (NSS) — две стороны одной медали / Хабр

1 Этап — Настройка домена Первым делом установим службы сертификации.

Дисклеймер.

Эта статья не является туториалом по внедрению корпоративного PKI. Вопросы проектирования, разворачивания и грамотного применения PKI тут не рассматриваются ввиду необъятности этой темы.

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

Задача центра сертификации — подтверждать подлинность ключей шифрования с помощью сертификатов электронной подписи.

Технически центр сертификации реализован как компонент глобальной службы каталогов, отвечающий за управление криптографическими ключами пользователей. Открытые ключи и другая информация о пользователях хранится удостоверяющими центрами в виде цифровых сертификатов.

Удостоверяющий центр, выдающий сертификаты для использования смарт-карт или токенов, должен быть помещен в хранилище NT Authority.

Зайдите в Диспетчер сервера и выберите «Добавить роли и компоненты».

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

На странице для подтверждения установки компонентов нажмите «Установить».

2 Этап — Настройка входа в домен с помощью токена

Для входа в систему нам понадобится сертификат, который содержит идентификаторы Smart Card Logon и Client Authentication.

Сертификат для смарт-карт или токенов также должен содержать UPN пользователя (суффикс имени участника-пользователя). По умолчанию суффиксом имени участника-пользователя для учетной записи является DNS-имя домена, которое содержит учетную запись пользователя.

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

В сертификате должен быть указан путь к точке распространения списка отзыва сертификатов (CRL distribution point). Такой файл содержит список сертификатов с указанием серийного номера сертификата, даты отзыва и причины отзыва. Он используется для передачи сведений об отозванных сертификатах пользователям, компьютерам и приложениям, пытающимся проверить подлинность сертификата.

Настроим установленные службы сертификации. В правом верхнем углу нажмите на желтый треугольник с восклицательным знаком и щелкните «Настроить службы сертификации…».

OpenSSL и Network Security Services (NSS) — две стороны одной медали / Хабр

В окне «Учетные данные» выберите необходимые учетные данные пользователя для настройки роли. Выберите «Центр сертификации».

Выберите «ЦС предприятия».

ЦС предприятия интегрированы с AD. Они публикуют сертификаты и списки отзыва сертификатов в AD.

Укажите тип «Корневой ЦС».

На следующем этапе выберите «Создать новый закрытый ключ».

Выберите период действия сертификата.

3 этап — Добавление шаблонов сертификатов

Для добавления шаблонов сертификатов откройте Панель управления, выберите пункт «Администрирование» и откройте Центр сертификации.

Щелкните по названию папки «Шаблоны сертификатов», выберите пункт «Управление».

Щелкните по названию шаблона «Пользователь со смарт-картой» и выберите пункт «Скопировать шаблон». На следующих скриншотах показано, какие параметры в окне «Свойства нового шаблона» необходимо изменить.

OpenSSL и Network Security Services (NSS) — две стороны одной медали / Хабр

Если в списке поставщиков нет «Aktiv ruToken CSP v1.0», то необходимо установить комплект «Драйверы Рутокен для Windows».

Начиная с Windows Server 2008 R2 вместо специального провайдера от производителя можно использовать «Microsoft Base Smart Card Crypto Provider».

Для устройств Рутокен библиотека «минидрайвера», поддерживающая «Microsoft Base Smart Card Crypto Provider», распространяется через Windows Update.

Проверить установился ли «минидрайвер» на вашем сервере можно подключив Рутокен к нему и посмотрев в диспетчер устройств.

OpenSSL и Network Security Services (NSS) — две стороны одной медали / Хабр

Если «минидрайвера» по каким-то причинам нет, его можно установить принудительно, инсталлировав комплект «Драйверы Рутокен для Windows», а после этого воспользоваться «Microsoft Base Smart Card Crypto Provider».

Комплект «Драйверы Рутокен для Windows» распространяется бесплатно с сайта Рутокен .

OpenSSL и Network Security Services (NSS) — две стороны одной медали / Хабр

Добавьте два новых шаблона «Агент сертификации» и «Пользователь с Рутокен».

Для этого выйдите из окна «Управления шаблонами». Нажмите правой кнопкой мыши на «Шаблоны сертификатов» и выберите пункт меню «Создать» и подпункт «Выдаваемый шаблон сертификата».

OpenSSL и Network Security Services (NSS) — две стороны одной медали / Хабр

Далее выберите «Агент регистрации» и «Пользователь с Rutoken» и нажмите «ОК».

OpenSSL и Network Security Services (NSS) — две стороны одной медали / Хабр
OpenSSL и Network Security Services (NSS) — две стороны одной медали / Хабр

Далее нам необходимо выписать сертификат администратору домена. Откройте службу «Выполнить» и укажите команду mmc. Добавьте оснастку «Сертификаты».

В окне «Оснастки диспетчера сертификатов» выберите «моей учетной записи пользователя». В окне «Добавление и удаление оснастки» подтвердите добавление сертификатов.

Выберите папку «Сертификаты».

OpenSSL и Network Security Services (NSS) — две стороны одной медали / Хабр

Запросите новый сертификат. Откроется страница для регистрации сертификата. На этапе запроса сертификата выберите политику регистрации «Администратор» и нажмите «Заявка».

OpenSSL и Network Security Services (NSS) — две стороны одной медали / Хабр

Таким же образом запросите сертификат для Агента регистрации.

Чтобы запросить сертификат для определенного пользователя щелкните «Сертификаты», выберите пункт «Зарегистрироваться от имени…».

OpenSSL и Network Security Services (NSS) — две стороны одной медали / Хабр

В окне для запроса сертификата установите флажок «Пользователь с Рутокен».

Теперь необходимо выбрать пользователя.

В поле «Введите имена выбранных объектов» укажите имя пользователя в домене и нажмите «Проверить имя».

В окне для выбора пользователя нажмите «Заявка».

Про сертификаты:  DELTA Module 1 (Начало) - Записки репетитора

В раскрывающемся списке выберите имя токена и укажите PIN-код.

OpenSSL и Network Security Services (NSS) — две стороны одной медали / Хабр

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

4 этап — Настройка учетных записей пользователей

Для настройки учетных записей откройте список пользователей и компьютеров AD.

Поиск объектов и создание сырой подписи

В прошлом разделе мы сгенерировали ключевую пару. На этот раз будем считать, что у нас нет хендлов на сгенерированные ключи, но мы знаем их идентификатор – CKA_ID. Попробуем найти объект закрытого ключа на токене:

int findObjects(CK_SESSION_HANDLE session,         // Хэндл открытой сессии
                CK_ATTRIBUTE_PTR attributes,       // Массив с шаблоном для поиска
                CK_ULONG attrCount,                // Количество атрибутов в массиве поиска
                CK_OBJECT_HANDLE objects[],        // Массив для записи найденных объектов
                CK_ULONG* objectsCount             // Количество найденных объектов
                       )
{
    CK_RV rv;                                           // Код возврата. Могут быть возвращены только ошибки, определенные в PKCS#11
    int errorCode = 1;                                  // Флаг ошибки

    /*************************************************************************
    * Инициализировать операцию поиска                                       *
    *************************************************************************/
    rv = functionList->C_FindObjectsInit(session, attributes, attrCount);
    CHECK_AND_LOG("  C_FindObjectsInit", rv == CKR_OK, rvToStr(rv), exit);

    /*************************************************************************
    * Найти все объекты, соответствующие критериям поиска                    *
    *************************************************************************/

    rv = functionList->C_FindObjects(session, objects, *objectsCount, objectsCount);
    CHECK_AND_LOG("  C_FindObjects", rv == CKR_OK, rvToStr(rv), find_final);

    errorCode = 0;

    /*************************************************************************
    * Деинициализировать операцию поиска                                     *
    *************************************************************************/
find_final:
    rv = functionList->C_FindObjectsFinal(session);
    CHECK_RELEASE_AND_LOG("  C_FindObjectsFinal", rv == CKR_OK, rvToStr(rv), errorCode);

exit:
    return errorCode;
}

int find_private_key(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE_PTR privateKey)
{
        CK_BYTE keyPairIdGost2021_256[] = { "GOST R 34.10-2021 (256 bits) sample key pair ID (Aktiv Co.)" };
        CK_OBJECT_CLASS privateKeyObject = CKO_PRIVATE_KEY;

        CK_ATTRIBUTE privateKeyTemplate[] =
        {
                { CKA_CLASS, &privateKeyObject, sizeof(privateKeyObject)},              // Класс - закрытый ключ
                { CKA_ID, &keyPairIdGost2021_256, sizeof(keyPairIdGost2021_256) - 1},   // Идентификатор ключевой пары (должен совпадать у открытого и закрытого ключей)
        };

        CK_ULONG cnt = 1;

        CK_RV rv;
        int errorCode = 1;

        rv = findObjects(session, privateKeyTemplate,
        arraysize(privateKeyTemplate), privateKey, &cnt);

        CHECK(" findObjects", rv == 0, exit);
        CHECK_AND_LOG(" Checking number of keys found", cnt == 1, "No objects foundn", exit);

        errorCode = 0;
exit:
        return errorCode;
}

Данный пример иллюстрирует работу с функцией поиска объекта по заданным атрибутам. Как можно заметить, операция поиска объекта на токене является составной и работа с ней сводится как минимум к вызову трёх функций: C_FindObjectsInit, C_FindObjects, C_FindObjectsFinal.

Функция C_FindObjects может вызываться по несколько раз, и каждый раз она будет возвращать следующие объекты поиска. Предпоследний аргумент функции C_FindObjects задаёт размер выходного массива объектов. А последний — количество полученных объектов после очередного поиска.

Поиск приватного ключа производился по атрибуту его класса и идентификатору. Мы рассчитывали, что найдётся хотя бы один объект по заданному шаблону и брали любой из них. Используем найденный ключ для вычисления сырой подписи:

int sign(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE privateKey)
{    
    /* OID алгоритма хеширования ГОСТ Р 34.11-2021(256)                     */
    CK_BYTE parametersGostR3411_256[] = {0x06, 0x08, 0x2a, 0x85, 0x03, 0x07, 0x01, 0x01, 0x02, 0x02};

    /* Механизм подписи/проверки подписи по алгоритму ГОСТ Р 34.10-2021(256) и хешированием по алгоритму ГОСТ Р 34.11-2021(256) */
    CK_MECHANISM gost3410SignWith3411Mech = { CKM_GOSTR3410_WITH_GOSTR3411_12_256, ¶metersGostR3411_256, sizeof(parametersGostR3411_256)};

    CK_BYTE data[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
               0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
               0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
               0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 };

    CK_BYTE_PTR signature;                            // Указатель на буфер, содержащий цифровую подпись для данных
    CK_ULONG signatureSize;                           // Размер буфера, содержащего цифровую подпись для данных, в байтах

    CK_RV rv;
    int errorCode = 1;

    /*************************************************************************
    * Вычислить подпись от данных                                            *
    *************************************************************************/
    printf(" Signing data...n");

    /*************************************************************************
    * Инициализировать операцию подписи данных                               *
    *************************************************************************/
    rv = functionList->C_SignInit(session, &gost3410SignWith3411Mech, privateKey);
    CHECK_AND_LOG("  >C_SignInit", rv == CKR_OK, rvToStr(rv), exit);

    /*************************************************************************
    * Определить размер данных подписи                                       *
    *************************************************************************/
    rv = functionList->C_Sign(session, data, sizeof(data), NULL_PTR, &signatureSize);
    CHECK_AND_LOG("  C_Sign(get size)", rv == CKR_OK, rvToStr(rv), exit);

    /*************************************************************************
    * Подписать данные                                                       *
    *************************************************************************/

    signature = (CK_BYTE*)malloc(signatureSize * sizeof(CK_BYTE));
    CHECK("  Memory allocation for signature", signature != NULL, exit);

    rv = functionList->C_Sign(session, data, sizeof(data), signature, &signatureSize);
    CHECK_AND_LOG("  C_Sign (signing)", rv == CKR_OK, rvToStr(rv), free_signature);


    /*************************************************************************
    * Распечатать буфер, содержащий подпись                                  *
    *************************************************************************/
    printf("  Signature buffer is: n");
    printHex(signature, signatureSize);
    printf("Data has been signed successfully.n");

    errorCode = 0;

free_signature:
    free(signature);
exit:
    return errorCode;
}

В этом примере подпись и хеш можно считать одновременно. Такой вариант рекомендован для безопасности: цепочку “хеширование-подпись” лучше не «разрывать». Чтобы показать, какой алгоритм хеширования использовать, мы передали его OID.

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

Просмотр сертификатов и других сущностей, хранящихся в файлах


В пакете OpenSSL присутствует одна

, первым параметром в которой задается собственно команда (Standard commands), которую необходимо выполнить:

Как можно заметить, при выполнении команды openssl help, помимо собственно перечня команд, выводится список поддерживаемых хэш-алгоритмов и алгоритмов шифрования (в их перечень включены и функции сжатия и работы с base64).

Для просмотра сертификата (x509), запроса (req) или списка отозванных сертификатов (crl) достаточно выполнить команду следующего вида:

openssl x509[|req|crl] [-nameopt utf8] -inform PEM|DER -noout -in <имя файла>.

Например, команда:

$openssl x509 -text -nameopt utf8 -inform PEM -noout -in cert.pem

отобразит на экране компьютера содержимое сертификата в техтовом виде (x509 -text), хранящегося в файле cert.pem ( -in cert.pem) в кодировке PEM (base64) (-inform PEM) и содержащего символы в кодировке utf-8 (-nameopt utf8). При этом сам сертификат в кодировке PEM на экран выводиться не будет (-noout).

В пакете NSS аналогичные действия выполняет утилита pp.

Утилита pp — это утилита элегантной печати (Pretty Print) файла, содержащего ASN.1 структуру в DER или PEM-кодировке:

Usage:  pp [-t type] [-a] [-i input] [-o output] [-w] [-u],  

где type:

Отметим еще один тип, который применяется к сертификатам, ci (certificate-identity). Этот тип позволяет получить из сертификата идентифицирующую его информацию, такую как subject (владелец), issuer (издатель), serial number (серийный номер), fingerprint (отпечатки по SHA-1 и SHA-256). Аналогичные параметры есть и у утилиты openssl для x509.

По умолчанию предполагается, что все объекты находятся в DER-кодировке. Если же объекты находятся в PEM-кодировке, то необходимо задать параметр “-a” (аналог параметра “-inform PEM” для утилиты openssl). И еще один параметр “-u” задается, если в объекте содержатся символы в кодировке UTF-8. Напомним, что аналогичный параметр есть и у утилиты openssl — “-nameopt utf8”.

В пакете NSS есть и утилита для просмотра ASN.1-структыры объекта, аналог утилиты openssl asn1.parse. Это утилита derdump:

$derdump -i <просматриваемый файл> [-o <выходной файл>]

Из самого названия утилиты следует, что она работает с файлами в DER-кодировке. Но это не страшно. В состав пакета входят две утилиты, которые конвертируют файлы из PEM/BASE64-кодировки в DER-кодировку и обратно. Это утилиты atob и btoa.

Например, для конвертации сертификата из PEM-формата в DER-формат в OpenSSL надо выполнить следующую команду:

$openssl x509 -inform der -in CERT.der -out CERT.pem 

В NSS это будет выглядеть так:

$btoa -in CERT.der -out CERT.pem -w "CERTIFICATE"

Параметр “-w” указывает, какой текст включить в начало выходного файла и конец. В данном случае “-w CERTIFICATE” приведет к появлению стандартного для PEM-формата в OpenSSL заголовка и концевика:

-----BEGIN CERTIFICATE-----
<тело сертификата в кодировке BASE64>
-----END CERTIFICATE----- 

И OpenSSL и NSS могут работать с контейнерами pkcs#12. И они оба позволяют не только создавать, но и просмотривать содержимое контейнера pkcs12. Но, при использовании утилиты openssl требуется сначала разобрать контейнер и сохранить сертификаты из контейнера в отдельных файлах.

pk12util -l <файл с контейнером pkcs12> [-W <пароль к контейнеру pkcs12>] [-d <каталог хранилища NSS>] [-h <токен>]


Например:

Про сертификаты:  1С Отчетность: Указан неправильный алгоритм

Утилита удобная, но без ложки дегтя не обошлось. Ложка дегтя состоит в том, что русские буквы, вернее, UTF-8 кодировка отображается в виде точек (…..). И если у утилиты pp есть параметр -u (присутствует кодировка utf-8), то здесь про нее забыли (мы еще раз столкнемся с этим при рассмотрении утилиты certutil).

PRIntn
P12U_ListPKCS12File(char *in_file, PK11SlotInfo *slot,
                    secuPWData *slotPw, secuPWData *p12FilePw)
{
    SEC_PKCS12DecoderContext *p12dcx = NULL;
    SECItem uniPwitem = { 0 };
    SECStatus rv = SECFailure;
    const SEC_PKCS12DecoderItem *dip;
/*Вызов функции для отображения UTF-8*/
    SECU_EnableUtf8Display(PR_TRUE);

.   .   .   .   .

}
После этого проблем с русскими буквами не будет.

$ pk12util -l 1000.p12 -d “.” -W 01234567

Certificate(has private key):

Data:

Version: 3 (0x2)

Serial Number: 4096 (0x1000)

Signature Algorithm: PKCS #1 SHA-256 With RSA Encryption

Issuer: «E=ca@test.ru,OGRN=1111111111111,INN=222222222222,CN=Удос

товеряюший Центр,OU=Отдел Удостоверя

юший Центр,O=Удостоверяюший Центр,STR

EET=»ул. Хавровская, д. 0″,L=Хабраград,ST=М

осковская область,C=RU”

Validity:

Not Before: Tue Jul 07 08:40:14 2020

Not After: Fri Aug 06 08:40:14 2021

Subject: «E=test@rsa.ru,CN=Тестовый сертификат»

Subject Public Key Info:

Public Key Algorithm: PKCS #1 RSA Encryption

RSA Public Key:

Modulus:

9a:9f:6c:60:94:f7:ec:f7:94:b3:51:01:e2:1a:c5:25:

28:bb:02:77:49:52:4d:99:8a:6e:26:12:55:8f:71:34:

04:da:39:24:f9:b4:6b:d0:0a:42:27:1b:b2:d7:9b:d9:

c3:76:b0:e0:1c:7c:21:ce:79:9f:d5:2b:17:63:cb:94:

5b:d9:b2:53:ff:b9:bf:4f:3d:cf:b7:8d:8a:37:ba:02:

8c:da:d2:0d:fd:46:5b:45:1d:95:64:07:6e:fa:88:0d:

a4:bd:b3:4a:ed:99:f1:fd:73:c5:b6:05:a0:e5:ee:6b:

c3:83:5b:d0:64:05:77:6a:18:d8:c8:28:a1:d0:06:41:

23:0d:bb:87:8a:77:14:fb:6c:5d:af:db:2b:0b:11:a3:

16:1b:2b:05:18:26:a9:b5:00:4a:40:da:b3:05:aa:2a:

67:c0:18:0d:03:f7:d2:b9:ba:7c:36:f9:95:2e:56:81:

a3:09:99:5e:20:10:95:38:10:c9:c1:6f:c3:6c:a6:1b:

78:51:c6:e4:4f:11:bc:c0:22:4b:ca:59:16:f2:45:95:

0d:fd:7b:46:cf:c7:ac:1c:3d:d7:26:fc:ad:80:3e:2c:

21:93:29:32:a6:79:e2:a8:c6:e9:5e:45:34:d3:38:57:

8f:cd:95:5e:91:09:84:34:21:d2:16:29:69:75:4d:a3

Exponent: 65537 (0x10001)

Signed Extensions:

Name: Certificate Basic Constraints

Critical: True

Data: Is not a CA.

Name: Certificate Key Usage
Usages: Digital Signature
Key Encipherment
Key Agreement

Name: Certificate Type
Data: <SSL Client,S/MIME>

Name: Extended Key Usage
TLS Web Client Authentication Certificate
E-Mail Protection Certificate

Name: Certificate Subject Key ID
Data:
26:a1:b3:98:1c:fe:62:ba:23:81:96:37:3f:08:bd:70:
d6:f2:b1:46

Name: Certificate Authority Key Identifier
Key ID:
0a:b6:f6:87:64:1d:8e:b3:63:08:29:9f:21:59:ad:47:
d8:ea:07:f4
Issuer:
Directory Name: «E=ca@test.ru,OGRN=1111111111111,INN=22222222
2222,CN=Удостоверяюший Центр,OU=Отд
ел Удостоверяюший Центр,O=Удост
оверяюший Центр,STREET=»ул. Хавровс
кая, д. 0″,L=Хабраград,ST=Московска
я область,C=RU”
Serial Number:
00:a2:9b:22:32:3e:a7:3d:d8

Name: Certificate Subject Alt Name
RFC822 Name: «test@rsa.ru»

Name: Certificate Issuer Alt Name
Error: Parsing extension: Certificate extension value is invalid.
Data: Sequence {
}

Signature Algorithm: PKCS #1 SHA-256 With RSA Encryption
Signature:
2f:75:7e:71:9e:15:5c:97:fe:a2:e1:2a:52:39:56:55:
e0:62:60:bc:5f:6d:c2:b6:ec:cd:8b:10:b3:b1:3f:e5:
d6:d1:5f:a5:fa:61:c1:ce:3e:db:6a:2f:b2:13:46:8d:
67:cf:18:09:61:97:01:45:bc:99:bb:0c:d6:0a:a3:03:
87:0a:8e:10:3a:d5:e3:94:6d:4a:24:fa:c3:40:0b:43:
c2:3b:00:56:06:c4:d2:fc:b2:7e:e9:00:e5:2f:4b:e2:
3a:91:49:ce:f8:c3:60:ec:01:74:d8:1a:3b:af:e6:f6:
91:db:c5:f1:d7:de:be:18:38:47:41:8a:e2:ef:80:91:
10:54:41:ae:55:22:6f:d7:8c:fa:46:b6:b6:2a:ee:6a:
0c:c9:03:18:af:4e:93:6c:61:f3:b4:78:0c:61:93:f1:
d8:1b:00:c3:e5:29:9a:08:0a:f8:31:67:88:3d:c3:88:
7a:60:c0:c4:52:94:25:56:e5:a3:df:7d:58:c5:df:9a:
c7:22:7e:2c:f6:fb:2c:bf:b7:7f:c5:ca:2b:0f:8c:20:
77:b9:1f:e0:62:5a:3d:d4:6f:12:ea:c8:51:67:a5:75:
ad:e9:ac:9e:4e:2e:2d:34:80:e7:d8:64:f6:8f:2f:33:
32:1f:8b:bc:9c:e8:77:4a:ee:7b:84:31:ec:28:e9:70
Fingerprint (SHA-256):
96:F4:A5:FA:6D:8A:F8:7E:A6:10:49:BD:43:34:C1:92:C6:7D:FF:63:41:8E:69:C0:AC:34:6B:CB:63:7B:56:31
Fingerprint (SHA1):
B6:91:9B:C6:7A:45:9C:92:FD:E7:C7:33:00:FA:91:DF:7D:5F:00:21

Friendly Name: Тестовый сертификат

Certificate:
Data:
Version: 3 (0x2)
Serial Number:
00:a2:9b:22:32:3e:a7:3d:d8
Signature Algorithm: PKCS #1 SHA-256 With RSA Encryption
Issuer: «E=ca@test.ru,OGRN=1111111111111,INN=222222222222,CN=Удос
товеряюший Центр,OU=Отдел Удостоверя
юший Центр,O=Удостоверяюший Центр,STR
EET=»ул. Хавровская, д. 0″,L=Хабраград,ST=М
осковская область,C=RU”
Validity:
Not Before: Tue Jul 07 08:08:11 2020
Not After: Fri Jul 05 08:08:11 2030
Subject: «E=ca@test.ru,OGRN=1111111111111,INN=222222222222,CN=Удос
товеряюший Центр,OU=Отдел Удостоверя
юший Центр,O=Удостоверяюший Центр,STR
EET=»ул. Хавровская, д. 0″,L=Хабраград,ST=М
осковская область,C=RU”
Subject Public Key Info:
Public Key Algorithm: PKCS #1 RSA Encryption
RSA Public Key:
Modulus:
e7:08:ed:83:08:10:7b:48:56:37:8b:e2:4a:31:1a:7b:
0d:4e:d2:a2:67:d7:04:60:a0:09:db:06:64:21:01:4e:
0d:41:d8:61:15:c6:58:83:66:7e:6b:65:72:0d:2b:c3:
50:26:11:04:82:4b:1a:12:d0:dc:e1:13:1c:76:69:0f:
c2:59:e2:5d:60:6d:fe:8a:48:fa:8b:1e:05:07:34:6d:
8a:e3:76:23:42:9e:7b:64:0b:6a:fb:36:63:31:96:df:
ed:d3:e8:7c:6e:39:d4:7d:da:b8:f4:ec:53:57:60:f1:
d8:a4:3a:3f:3b:4a:63:6c:2a:55:90:21:15:23:4a:37:
21:31:a0:c4:bb:84:0d:96:18:3c:3b:ba:92:e3:e2:17:
56:e5:d9:8c:58:24:8a:a3:53:b6:4f:02:4d:30:a6:0f:
34:ad:20:cf:6f:03:ca:23:1e:d3:15:bc:80:09:d8:1e:
90:07:da:90:a9:34:9e:6e:ed:6b:10:b7:a1:a4:a9:b4:
04:ac:6a:40:d8:00:52:d6:6a:28:f2:8c:c6:84:81:8a:
cd:63:a6:53:82:d2:4e:11:ec:94:81:d7:9c:79:8a:30:
9c:40:75:4d:d9:88:0b:cc:a4:0c:5d:6d:23:a6:ac:56:
8c:49:d9:1f:2b:63:cb:50:fc:a3:e0:3e:35:4e:f4:03
Exponent: 65537 (0x10001)
Signed Extensions:
Name: Certificate Basic Constraints
Critical: True
Data: Is a CA with no maximum path length.

Name: Certificate Subject Key ID
Data:
0a:b6:f6:87:64:1d:8e:b3:63:08:29:9f:21:59:ad:47:
d8:ea:07:f4

Signature Algorithm: PKCS #1 SHA-256 With RSA Encryption
Signature:
17:7d:29:dc:4d:6e:4c:99:7a:bc:b2:2a:a5:80:f9:5f:
0c:60:00:2b:f3:f4:ef:19:d7:ed:56:07:5d:24:e1:b3:
f6:43:e2:05:9b:75:ce:cd:cf:27:1e:1c:cd:d8:cc:43:
77:16:04:7e:8a:dd:89:c4:b2:75:ae:f4:84:23:53:18:
fe:be:c5:1d:40:55:aa:91:9f:f5:96:06:5d:07:22:a8:
1c:b9:29:c4:49:2e:75:10:75:22:95:36:16:58:2f:77:
f5:fa:6d:de:c4:67:ca:f3:e1:98:51:b4:ba:b7:2a:7f:
06:db:33:5a:a6:bb:53:57:f4:18:93:16:9c:0e:43:d0:
46:e6:84:55:bb:ff:68:fe:fa:32:d5:23:2a:d5:65:9b:
d9:63:45:6b:53:71:64:dd:da:e1:40:fa:89:30:b1:73:
8b:f8:7c:3c:2f:72:24:ad:e8:5c:07:89:2f:3a:0d:37:
48:29:1f:0d:5f:9e:11:73:56:b8:d9:24:eb:2d:2e:18:
c7:9b:90:62:09:20:61:75:b9:a1:9a:3f:99:34:8e:06:
30:ce:7d:60:42:7d:e0:14:f2:88:f2:41:a0:46:4d:55:
17:d4:c2:15:64:c9:3e:f5:cc:0a:41:f7:c0:d0:94:96:
ea:64:e0:45:3a:e0:a3:d6:22:a9:d1:e3:c4:51:e8:96
Fingerprint (SHA-256):
F5:DF:15:79:5E:1E:41:84:96:8C:8C:CA:37:0C:A6:BB:C3:21:AE:3D:32:42:8C:63:C2:64:BA:0A:74:DC:37:F8
Fingerprint (SHA1):
CF:C6:B9:D4:3C:16:6F:31:91:2A:09:2F:FE:4C:57:89:0F:5A:F1:DB

Friendly Name: Удостоверяюший Центр

Key(shrouded):
Friendly Name: Тестовый сертификат

Encryption algorithm: PKCS #12 V2 PBE With SHA-1 And 3KEY Triple DES-CBC
Parameters:
Salt:
c4:fa:4a:6a:4f:54:a1:7a
Iteration Count: 2048 (0x800)
$

При создании контейнера PKCS#12 утилитой openssl мы воспользовались графической оболочной

Теперь самое время поговорить о хранилище NSS.

Создание объектов — на примере генерации ключевых пар

В первую очередь, напишем функцию, которая будет генерировать ключевую пару ГОСТ Р 34.10-2021 256 бит на указанном слоте:

int gen_gost_key_pair(CK_SESSION_HANDLE session)
{
    CK_KEY_TYPE keyTypeGostR3410_2021_256 = CKK_GOSTR3410;
    CK_BYTE keyPairIdGost2021_256[] = { "GOST R 34.10-2021 (256 bits) sample key pair ID (Aktiv Co.)" };
    CK_BYTE parametersGostR3410_2021_256[] = { 0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x23, 0x01 };
    CK_BYTE parametersGostR3411_2021_256[] = { 0x06, 0x08, 0x2a, 0x85, 0x03, 0x07, 0x01, 0x01, 0x02, 0x02 };
    CK_BBOOL attributeTrue = CK_TRUE;
    CK_BBOOL attributeFalse = CK_FALSE;

    CK_OBJECT_CLASS publicKeyObject = CKO_PUBLIC_KEY;

    CK_ATTRIBUTE publicKeyTemplate[] =
    {
        { CKA_CLASS, &publicKeyObject, sizeof(publicKeyObject)},                                        // Класс - открытый ключ
        { CKA_ID, &keyPairIdGost2021_256, sizeof(keyPairIdGost2021_256) - 1 },                          // Идентификатор ключевой пары (должен совпадать у открытого и закрытого ключей)
        { CKA_KEY_TYPE, &keyTypeGostR3410_2021_256, sizeof(keyTypeGostR3410_2021_256) },                // Тип ключа - ГОСТ Р 34.10-2021(256)
        { CKA_TOKEN, &attributeTrue, sizeof(attributeTrue)},                                            // Ключ является объектом токена
        { CKA_PRIVATE, &attributeFalse, sizeof(attributeFalse)},                                        // Ключ доступен без аутентификации на токене
        { CKA_GOSTR3410_PARAMS, parametersGostR3410_2021_256, sizeof(parametersGostR3410_2021_256) },   // Параметры алгоритма ГОСТ Р 34.10-2021(256)
        { CKA_GOSTR3411_PARAMS, parametersGostR3411_2021_256, sizeof(parametersGostR3411_2021_256) }    // Параметры алгоритма ГОСТ Р 34.11-2021(256)
    };

    CK_OBJECT_CLASS privateKeyObject = CKO_PRIVATE_KEY;

    CK_ATTRIBUTE privateKeyTemplate[] =
    {
        { CKA_CLASS, &privateKeyObject, sizeof(privateKeyObject)},                                      // Класс - закрытый ключ
        { CKA_ID, &keyPairIdGost2021_256, sizeof(keyPairIdGost2021_256) - 1 },                          // Идентификатор ключевой пары (должен совпадать у открытого и закрытого ключей)
        { CKA_KEY_TYPE, &keyTypeGostR3410_2021_256, sizeof(keyTypeGostR3410_2021_256) },                // Тип ключа - ГОСТ Р 34.10-2021(256)
        { CKA_TOKEN, &attributeTrue, sizeof(attributeTrue)},                                            // Ключ является объектом токена
        { CKA_PRIVATE, &attributeTrue, sizeof(attributeTrue)},                                          // Ключ доступен только после аутентификации на токене
        { CKA_GOSTR3410_PARAMS, parametersGostR3410_2021_256, sizeof(parametersGostR3410_2021_256) },   // Параметры алгоритма ГОСТ Р 34.10-2021(256)
        { CKA_GOSTR3411_PARAMS, parametersGostR3411_2021_256, sizeof(parametersGostR3411_2021_256) }    // Параметры алгоритма ГОСТ Р 34.11-2021(256)
    };

    CK_OBJECT_HANDLE privateKey;                      // Хэндл закрытого ключа ГОСТ (ключевая пара для подписи и шифрования)    
    CK_OBJECT_HANDLE publicKey;                       // Хэндл открытого ключа ГОСТ (ключевая пара для подписи и шифрования)    

    CK_MECHANISM gostR3410_2021_256KeyPairGenMech = { CKM_GOSTR3410_KEY_PAIR_GEN, NULL_PTR, 0 };

    CK_RV rv;   
    int errorCode = 1;

    /*************************************************************************
    * Генерация ключевой пары на токене                                      *
    *************************************************************************/
    rv = functionList->C_GenerateKeyPair(session, &gostR3410_2021_256KeyPairGenMech, 
    publicKeyTemplate, arraysize(publicKeyTemplate),
    privateKeyTemplate, arraysize(privateKeyTemplate),
    &publicKey, &privateKey);
    CHECK_AND_LOG(" C_GenerateKeyPair", rv == CKR_OK, rvToStr(rv), exit);

    errorCode = 0;
    printf("Gost key pair generated successfullyn");

exit:
    return errorCode;
}

В этом примере для нас много нового. Можно заметить, что здесь вызывается всего одна функция C_GenerateKeyPair. Эта функция является стандартной функцией генерации ключей, работающей внутри открытой сессии. Также стоит отметить, что пользователь должен быть аутентифицирован перед вызовом этой функции.

Теперь перейдём к объектам. Внутри функции gen_gost_key_pair происходит создание двух объектов на токене: открытого и закрытого ключей. Вот, что стандарт PKCS#11 говорит про объекты:

Cryptoki recognizes a number of classes of objects, as defined in the CK_OBJECT_CLASS data type. An object consists of a set of attributes, each of which has a given value. Each attribute that an object possesses has precisely one value.

То есть стандарт не даёт явное определение объекта, но из того, что там написано, мы знаем:

Также в стандарте представлена классификация объектов:

Иерархия PKCS#11 объектов
Иерархия PKCS#11 объектов

Заголовок диаграммы определяет класс объекта, а то что ниже — некоторые из его атрибутов. Видно, что объектом может являться некоторый механизм (о механизмах мы поговорим позже), встроенные функции токена (Hardware feature), некоторые данные на токене (Storage). В нашем случае мы выполнили действие с данными.

Название всех атрибутов начинается с префикса “CKA_”. Одним из самых важных атрибутов является CKA_ID. Он задаёт идентификатор объекта и используется для связи ключевых пар и сертификатов. Атрибут CKA_TOKEN является булевым и показывает, является ли объект — объектом токена.

Атрибут CKA_PRIVATE тоже является булевым и определяет нужна ли предварительная аутентификация для получения доступа к объекту. Атрибут CKA_ID — задаёт шестнадцатеричный идентификатор объекта. Также есть булевые атрибуты CKA_MODIFIABLE, CKA_COPYABLE, CKA_DESTROYABLE для более тонкой настройки доступа к объекту.

Объекты данных могут быть самыми разнообразными: асимметричные ключи, симметричные ключи, сертификаты, просто какая-либо информация на токене. В нашем примере мы создали два объекта, но сделали это неявно с помощью механизма генерации ключей. C_GenerateKeyPair приняла на вход механизм генерации ключевой пары, шаблоны открытого и закрытого ключа и с помощью механизма сгенерировала объекты ключевой пары (publicKey и privateKey).

Оцените статью
Мой сертификат
Добавить комментарий