Генерация и просмотр пары
Для генерации закрытого ключа по алгоритму ГОСТ Р 34.10 2021 используется следующая команда:
$certtool --generate-privkey --key-type [gost12-256 | gost12-512] --curve <значение параметра> [--no-text] [--outder] [--outfile <имя файла для ключа>]
$Криптопараметры (–curve) при генерации ключевой пары задаются OID-ами. В настоящее время
следующие oid-ы для криптопараметров алгоритма подписи ГОСТ Р 34.10-2021 с ключом 256:
1.2.643.7.1.2.1.1.1 (id-tc26-gost-3410-12-256-paramSetA) [TC26-256-A];
1.2.643.7.1.2.1.1.2 (id-tc26-gost-3410-12-256-paramSetB) [TC26-256-B];
1.2.643.7.1.2.1.1.3 (id-tc26-gost-3410-12-256-paramSetC) [TC26-256-C];
1.2.643.7.1.2.1.1.4 (id-tc26-gost-3410-12-256-paramSetD) [TC26-256-D].
При этом продолжают действовать так называемые OID-ы параметров от КриптоПро:
1.2.643.2.2.35.1 (id-GostR3410-2001-CryptoPro-A-ParamSet) [CryptoPro-A];
1.2.643.2.2.35.2 (d-GostR3410-2001-CryptoPro-B-ParamSet) [CryptoPro-B];
1.2.643.2.2.35.3 (id-GostR3410-2001-CryptoPro-C-ParamSet) [CryptoPro-C];
1.2.643.2.2.36.0 (id-GostR3410-2001-CryptoPro-XchA-ParamSet) [CryptoPro-XchA];
1.2.643.2.2.36.1 (id-GostR3410-2001-CryptoPro-XchB-ParamSet) [CryptoPro-XchB].
Напомним, что параметры КриптоПро с OID-ами 1.2.643.2.2.35.1, 1.2.643.2.2.35.2, 1.2.643.2.2.35.3 соответствуют параметрам ТК-26 с OID-ами 1.2.643.7.1.2.1.1.1, 1.2.643.7.1.2.1.1.2, 1.2.643.7.1.2.1.1.3 соответственно.
С криптопараметрами для алгоритма подписи ГОСТ Р 34.10-2021 с ключом 512 проще:
1.2.643.7.1.2.1.2.1 (id-tc26-gost-3410-2021-512-paramSetA) [TC26-512-A];
1.2.643.7.1.2.1.2.2 (id-tc26-gost-3410-2021-512-paramSetB) [TC26-512-B];
1.2.643.7.1.2.1.2.3 (id-tc26-gost-3410-2021-512-paramSetC) [TC26-512-C];
В GnuTLS введены свои обозначения для криптопараметров и они указаны в квадратных скобках, например [TC26-256-B]. К сожалению, при генерации ключа криптопараметры можно задать только их символьным обозначением. OID-ы в точечно-цифровой форме отвергаются.
Но это не самый большой недостаток. В настоящее время в GnuTLS поддерживаются только два криптопараметра. Для ключей ГОСТР 34.10-2021-256 (опция –key-type gost12-256) это параметр с oid-ом 1.2.643.7.1.2.1.1.2 (опция –curve TC26-256-B), а для ключей длиною 512 бит это параметр с oid-ом 1.2.643.7.1.2.1.2.1 (опция –curve TC26-512-A).
Итак, создаем закрытый ключ (флаг –generate-privkey) и просматриваем информацию о ключе (флаг –key-info):
#Создаём закрытый ключ --key-type gost12-256 --curve TC26-256-B
$certtool --generate-privkey --key-type gost12-256 --curve TC26-256-B --no-text --outder --outfile key256.der
Generating a 256 bit GOST R 34.10-2021-256 private key (TC26-256-B)...
#Смотрим ключевую пару
$certtool --key-info --inder --infile key256.der
Public Key Info:
Public Key Algorithm: GOST R 34.10-2021-256
Key Security Level: High (256 bits)
curve: TC26-256-B
digest: STREEBOG-256
paramset: TC26-Z
private key:
4b:df:cb:9e:cc:49:c5:a2:70:36:c9:d8:df:55:97:f5
8b:be:ae:06:7b:34:76:39:b6:aa:57:af:3f:2d:98:36
x:
0d:71:29:56:d2:39:59:6f:14:d3:4b:75:44:85:91:a9
5d:fa:83:4a:93:9a:2c:20:b0:6b:5c:74:8c:76:5f:a5
y:
2d:ac:da:23:f3:2f:45:d9:47:c4:a3:c1:d7:65:bd:46
1d:ba:12:cd:15:e1:b0:8f:5a:99:f2:35:ea:fc:33:fc
Public Key PIN:
pin-sha256:nrftlmdCrSf11N ivohfuGXSQixa4Scnhl7GTsUTE2E=
Public Key ID:
sha256:9eb7ed966742ad27f5d4dfa2be885fb865d2422c5ae12727865ec64ec5131361
sha1:25927018f3775ed86996c625a99b7db86b2d0a7e
-----BEGIN PRIVATE KEY-----
MEACAQAwFwYIKoUDBwEBAQEwCwYJKoUDBwECAQECBCIEIDaYLT vV6q2OXY0ewau
vov1l1Xf2Mk2cKLFScyey99L
-----END PRIVATE KEY-----
$
В информации о ключе мы имеем полную информацию как о приватном (закрытом) ключе, включая его asn1-структуру, так и публичном ключе, включая его значение (x и y).
Теперь посмотрим на asn1-структуру приватного ключа. Для этого воспользуемся утилитой
В этой структуре вы не найдете значения открытого ключа. Значение открытого ключа вычисляется через закрытый ключ. А вот закрытый ключ по открытому вычислить, естественно, нельзя!!! И всё же досадно, что asn1-структура публичного ключа не доступна при просмотре приватного ключа (флаг –key-info).
Конечно, приватный ключ надо хранить надежно, по крайней мере зашифрованным на пароле. Для безопасного хранения приватного ключа используется контейнер PKCS8, который предоставляет возможность шифрования закрытого ключа с помощью шифрования на основе пароля пользователя.
$certtool --generate-privkey --pkcs8 --pkcs-cipher gost28147-tc26z [--password <пароль>] --key-type [gost12-256 | gost12-512] --curve <значение параметра> [--no-text] [--outder] [--outfile <имя файла для ключа>]
$Если опция “–password” не задана, то пароль для шифрования закрытого ключа будет запрошен в командной строке:
bash-5.1$ ./generate_key_parse_password.sh
#Создаём закрытый ключ --key-type gost12-256 --curve TC26-256-B
$certtool --generate-privkey --pkcs8 --pkcs-cipher gost28147-tc26z --password 01234567 --key-type gost12-256 --curve TC26-256-B --no-text --outder --outfile key256_pkcs8.der
Generating a 256 bit GOST R 34.10-2021-256 private key (TC26-256-B)...
#Смотрим информацию о ключе
$certtool --key-info --inder --infile key256.der
Encrypted structure detected...
Enter password: <ВВОД ПАРОЛЯ>
PKCS #8 information:
Cipher: GOST28147-TC26Z-CFB
Schema: PBES2-GOST28147-89-TC26Z (1.2.643.7.1.2.5.1.1)
Salt: 32b2798c23a5d0ab8c3144daf273745bdb
Salt size: 17
Iteration count: 5333
Public Key Info:
Public Key Algorithm: GOST R 34.10-2021-256
Key Security Level: High (256 bits)
curve: TC26-256-B
digest: STREEBOG-256
paramset: TC26-Z
private key:
05:20:c3:7a:93:a2:e1:b4:64:50:a4:fb:db:cc:74:43
1a:14:d9:00:c4:82:dc:f5:94:8a:8a:65:a4:76:47:76
x:
1b:3e:0b:e0:3b:fc:1d:ee:62:10:63:66:72:fc:66:a6
d5:b8:94:b1:fe:a4:ec:d6:d8:f1:18:63:95:a9:30:15
y:
fb:c0:24:10:41:12:98:7f:aa:15:cd:3b:7e:e7:64:bd
c4:97:3d:18:04:82:f0:80:61:8e:ff:48:eb:d6:97:d5
Public Key PIN:
pin-sha256:eD6s4BQl4R0WY6H8KJZKDA6zuzd6A7JcNCh4FpUH1Rg=
Public Key ID:
sha256:783eace01425e11d1663a1fc28964a0c0eb3bb377a03b25c342878169507d518
sha1:62afc2d7c8bf77f1ac0820324170cd2262a63091
-----BEGIN PRIVATE KEY-----
MEACAQAwFwYIKoUDBwEBAQEwCwYJKoUDBwECAQECBCIEIHZHdqRlioqU9dyCxADZ
FBpDdMzb 6RQZLThopN6wyAF
-----END PRIVATE KEY-----
#Смотрим закрытый ключ средствами openssl и сравниваем
/usr/local/lirssl_csp_64/bin/lirssl pkcs8 -inform DER -in key256_pkcs8.der -outform PEM
-----BEGIN PRIVATE KEY-----
MEACAQAwFwYIKoUDBwEBAQEwCwYJKoUDBwECAQECBCIEIHZHdqRlioqU9dyCxADZ
FBpDdMzb 6RQZLThopN6wyAF
-----END PRIVATE KEY-----
bash-5.1$ Как добавить корневой сертификат в доверенные в linux на уровне системы
Сертификат с расширением .crt можно открыть двойным кликом и просмотреть его содержимое:
Если вы работаете в системе от обычного пользователя (не root), то кнопка «Импортировать» будет недоступна.
Чтобы разблокировать кнопку «Импортировать», выполните следующую команду:
sudo gcr-viewer /ПУТЬ/ДО/СЕРТИФИКАТА.crt
Например:
sudo gcr-viewer ./HackWareCA.crt
Данный способ может не сработать, поэтому рассмотрим, как добавить доверенные корневые центры сертификации в командной строке.
Суть метода очень проста:
- Добавить свой корневой CA сертификат в папку, предназначенную для таких сертификатов.
- Запустить программу для обновления общесистемного списка сертификатов.
Пути и команды в разных дистрибутивах Linux чуть различаются.
Просмотреть Subject всех корневых CA сертификатов можно уже знакомой командой:
awk -v cmd='openssl x509 -noout -subject' ' /BEGIN/{close(cmd)};{print | cmd}' < /etc/ssl/certs/ca-certificates.crtДля демонстрации я добавлю сертификат с Common Name, включающим «HackWare», тогда для проверки, имеется ли сертификат с таким именем среди корневых CA, я могу использовать команду:
awk -v cmd='openssl x509 -noout -subject' ' /BEGIN/{close(cmd)};{print | cmd}' < /etc/ssl/certs/ca-certificates.crt | grep -i HackWare
Для добавления своего корневого CA в доверенные в Debian, Kali Linux, Linux Mint, Ubuntu и их производных:
1. Проверьте, существует ли директория /usr/local/share/ca-certificates:
ls -l /usr/local/share/ca-certificates
Если её ещё нет, то создайте:
sudo mkdir /usr/local/share/ca-certificates
Сертификат должен быть в формате PEM (обычно так и есть) и иметь расширение .crt — если расширение вашего сертификата .pem, то достаточно просто поменять на .crt.
2. Скопируйте ваш сертификат командой вида:
sudo cp СЕРТИФИКАТ.crt /usr/local/share/ca-certificates/
Например:
sudo cp ./HackWareCA.crt /usr/local/share/ca-certificates/
3. Запустите следующую команду для обновления общесистемного списка:
sudo update-ca-certificates
Пример вывода:
Updating certificates in /etc/ssl/certs... 1 added, 0 removed; done. Running hooks in /etc/ca-certificates/update.d... Adding debian:HackWareCA.pem done. done.
Проверим наличие нашего CA сертификата среди доверенных:
awk -v cmd='openssl x509 -noout -subject' ' /BEGIN/{close(cmd)};{print | cmd}' < /etc/ssl/certs/ca-certificates.crt | grep -i HackWareСертификат успешно найден:
Чтобы его удалить:
sudo rm /usr/local/share/ca-certificates/СЕРТИФИКАТ.crt sudo update-ca-certificates
Для добавления своего корневого CA в доверенные в Arch Linux, BlackArch и их производных:
1. Выполните команду вида:
sudo cp ./СЕРТИФИКАТ.crt /etc/ca-certificates/trust-source/anchors/
Например:
sudo cp ./HackWareCA.crt /etc/ca-certificates/trust-source/anchors/
2. Обновите общесистемный список доверенных CA:
sudo update-ca-trust
Чтобы удалить этот сертификат:
sudo rm /etc/ca-certificates/trust-source/anchors/СЕРТИФИКАТ.crt sudo update-ca-trust
Добавление сертификатов в базу данных NSS
Некоторые приложения используют базу данных NSS, и у вас может быть необходимость добавить доверенные CA в неё.
Последующие изменения повлияют только на приложения, использующие базу данных NSS и учитывающие файл /etc/pki/nssdb.
1. Сначала создайте структуру каталогов для системных файлов базы данных NSS:
sudo mkdir -p /etc/pki/nssdb
Затем создайте новый набор файлов базы данных. Пароль нужен для того, чтобы базу данных могли редактировать только люди, которые его знают. Если все пользователи в системе (и с доступом к резервным копиям) заслуживают доверия, этот пароль можно оставить пустым.
sudo certutil -d sql:/etc/pki/nssdb -N
2. Убедитесь, что файлы базы данных доступны для чтения всем:
sudo chmod go r /etc/pki/nssdb/*
3. Теперь, когда доступны файлы базы данных NSS, добавьте сертификат в хранилище следующим образом:
sudo certutil -d sql:/etc/pki/nssdb -A -i ФАЙЛ-СЕРТИФИКАТА.crt -n "ИМЯ-СЕРТИФИКАТА" -t "C,,"
Например:
sudo certutil -d sql:/etc/pki/nssdb -A -i ./HackWareCA.crt -n "HackWare CA" -t "C,,"
Биты доверия, используемые в приведённом выше примере, помечают сертификат как надёжный для подписи сертификатов, используемых для связи SSL/TLS. Имя (указывается после опции -n), используемое в команде, можно выбрать любое, но убедитесь, что его легко отличить от других сертификатов в магазине.
Для проверки:
certutil -L -d /etc/pki/nssdb
Аналогичные инструкции можно использовать для включения сертификата только в базу данных NSS конкретного пользователя:
certutil -d sql:$HOME/.pki/nssdb -A -i ФАЙЛ-СЕРТИФИКАТА.crt -n "ИМЯ-СЕРТИФИКАТА" -t "C,,"
Удаление из файлов базы данных NSS
Чтобы удалить сертификат из любой базы данных NSS, используйте команду certutil следующим образом. В этом примере используется общесистемное расположение базы данных NSS, но его можно легко изменить на пользовательское ~/.pki/nssdb местоположение.
sudo certutil -d sql:/etc/pki/nssdb -D -n "certificateName"
Криптографический арм на базе контейнера pkcs#12. создание электронной подписи cades-x long type 1. часть 3

Прошло время и утилита, начатая как
просмотрщик сертификатов
, дополненная функциями работы с криптографическими
токенами PKCS#11
и создания запросов (PKCS#10) на квалифицированный сертификат, пополнилась, как и было заявлено, функциями работы с контейнерами PKCS#12.
Загружаем, запускаем утилиту cryptoarmpkcs и нажимаем кнопку «PKCS12»:

Скриншот наглядно демонстрирует, что позволяет делать утилита, имея на руках контейнер PKCS#12:
Две последние операции возможны только при подключенном токене (выбрана библиотека PKCS#11 и подключен токен). Отметим также, что не все токены позволяют импортировать закрытые ключи. Но эта операция важна, если мы хотим работать
с облачными токенами PKCS#11
.
В отличие от криптографического токена PKCS#11, защищенный контейнер PKCS#12 не является криптографической машиной, т.е. он только хранит в защищенном виде (зашифрованном на пароле) сертификат и закрытый ключ к нему и не выполняет никаких криптографических операций. Требования к защищенному контейнеру на базе российской криптографии сформулированы ТК-26 в документе «Р 50.1.112-2021. Транспортный ключевой контейнер.», который утвержден и введен в действие Приказом Федерального агентства по техническому регулированию и метрологии от 23 ноября 2021 г. No 1753-ст.
Как получить контейнер PKCS#12 из криптопровайдера MS CSP хорошо описано в одной из статей на Хабр.
Для работы с PKCS#12 пришлось разработать два новых пакета tcl, помимо ранее созданного TclPKCS11. Первый пакет Lcc, для поддержки российской криптографии с учетом рекомендаций ТК-26.
// Digest commands
// gost3411_2021
Tcl_CreateObjCommand(interp, "lcc_gost3411_2021", gost3411_2021_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_gost3411_2021_ctx_create", gost3411_2021_ctx_create_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_gost3411_2021_ctx_update", gost3411_2021_ctx_update_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_gost3411_2021_ctx_final", gost3411_2021_ctx_final_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_gost3411_2021_ctx_delete", gost3411_2021_ctx_delete_Cmd, NULL, NULL);
// gost3411_94
Tcl_CreateObjCommand(interp, "lcc_gost3411_94", gost3411_94_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_gost3411_94_ctx_create", gost3411_94_ctx_create_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_gost3411_94_ctx_update", gost3411_94_ctx_update_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_gost3411_94_ctx_final", gost3411_94_ctx_final_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_gost3411_94_ctx_delete", gost3411_94_ctx_delete_Cmd, NULL, NULL);
// sha1
Tcl_CreateObjCommand(interp, "lcc_sha1", sha1_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_sha1_ctx_create", sha1_ctx_create_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_sha1_ctx_update", sha1_ctx_update_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_sha1_ctx_final", sha1_ctx_final_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_sha1_ctx_delete", sha1_ctx_delete_Cmd, NULL, NULL);
// HMAC commands
// gost3411hmac
Tcl_CreateObjCommand(interp, "lcc_gost3411_2021_hmac", gost3411_2021_hmac_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_gost3411_2021_hmac_ctx_create", gost3411_2021_hmac_ctx_create_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_gost3411_2021_hmac_ctx_update", gost3411_2021_hmac_ctx_update_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_gost3411_2021_hmac_ctx_final", gost3411_2021_hmac_ctx_final_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_gost3411_2021_hmac_ctx_delete", gost3411_2021_hmac_ctx_delete_Cmd, NULL, NULL);
// gost3411_94_hmac
Tcl_CreateObjCommand(interp, "lcc_gost3411_94_hmac", gost3411_94_hmac_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_gost3411_94_hmac_ctx_create", gost3411_94_hmac_ctx_create_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_gost3411_94_hmac_ctx_update", gost3411_94_hmac_ctx_update_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_gost3411_94_hmac_ctx_final", gost3411_94_hmac_ctx_final_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_gost3411_94_hmac_ctx_delete", gost3411_94_hmac_ctx_delete_Cmd, NULL, NULL);
// PKCS#5 commands
Tcl_CreateObjCommand(interp, "lcc_gost3411_2021_pkcs5", gost3411_2021_pkcs5_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_gost3411_94_pkcs5", gost3411_94_pkcs5_Cmd, NULL, NULL);
// PKCS#12 PBA
Tcl_CreateObjCommand(interp, "lcc_gost3411_94_pkcs12_pba", gost3411_94_pkcs12_pba_Cmd, NULL, NULL);
// gost3411_2021 KDF
Tcl_CreateObjCommand(interp, "lcc_gost3411_2021_256_kdf", gost3411_2021_256_kdf_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_gost3411_2021_256_kdf_tree", gost3411_2021_256_kdf_tree_Cmd, NULL, NULL);
// PRF TLS
Tcl_CreateObjCommand(interp, "lcc_gost3411_94_prf_tls", gost3411_94_prf_tls_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_gost3411_2021_256_prf_tls", gost3411_2021_256_prf_tls_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_gost3411_2021_512_prf_tls", gost3411_2021_512_prf_tls_Cmd, NULL, NULL);
// gost3410-2021 commands
// 256 bits
Tcl_CreateObjCommand(interp, "lcc_gost3410_2021_256_getGroupByOid", gost3410_2021_256_getGroupByOid_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_gost3410_2021_256_getGroupByDerOid", gost3410_2021_256_getGroupByDerOid_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_gost3410_2021_256_getGroupById", gost3410_2021_256_getGroupById_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_gost3410_2021_256_createPrivateKey", gost3410_2021_256_createPrivateKey_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_gost3410_2021_256_createPublicKey", gost3410_2021_256_createPublicKey_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_gost3410_2021_256_sign", gost3410_2021_256_sign_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_gost3410_2021_256_verify", gost3410_2021_256_verify_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_gost3410_2021_256_vko", gost3410_2021_256_vko_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_gost3410_2021_256_keg", gost3410_2021_256_keg_Cmd, NULL, NULL);
// 512 bits
Tcl_CreateObjCommand(interp, "lcc_gost3410_2021_512_getGroupByOid", gost3410_2021_512_getGroupByOid_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_gost3410_2021_512_getGroupByDerOid", gost3410_2021_512_getGroupByDerOid_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_gost3410_2021_512_getGroupById", gost3410_2021_512_getGroupById_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_gost3410_2021_512_createPrivateKey", gost3410_2021_512_createPrivateKey_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_gost3410_2021_512_createPublicKey", gost3410_2021_512_createPublicKey_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_gost3410_2021_512_sign", gost3410_2021_512_sign_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_gost3410_2021_512_verify", gost3410_2021_512_verify_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_gost3410_2021_512_vko", gost3410_2021_512_vko_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_gost3410_2021_512_keg", gost3410_2021_512_keg_Cmd, NULL, NULL);
// gost3410-2001-vko (with 3411-94)
Tcl_CreateObjCommand(interp, "lcc_gost3410_2001_vko", gost3410_2001_vko_Cmd, NULL, NULL);
// Magma commands
// ECB
Tcl_CreateObjCommand(interp, "lcc_magma_ecb_ctx_create", magma_ecb_ctx_create_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_magma_ecb_ctx_update", magma_ecb_ctx_update_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_magma_ecb_ctx_delete", magma_ecb_ctx_delete_Cmd, NULL, NULL);
// CBC
Tcl_CreateObjCommand(interp, "lcc_magma_cbc_ctx_create", magma_cbc_ctx_create_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_magma_cbc_ctx_update", magma_cbc_ctx_update_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_magma_cbc_ctx_delete", magma_cbc_ctx_delete_Cmd, NULL, NULL);
// CTR
Tcl_CreateObjCommand(interp, "lcc_magma_ctr_ctx_create", magma_ctr_ctx_create_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_magma_ctr_ctx_update", magma_ctr_ctx_update_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_magma_ctr_ctx_delete", magma_ctr_ctx_delete_Cmd, NULL, NULL);
// OFB
Tcl_CreateObjCommand(interp, "lcc_magma_ofb_ctx_create", magma_ofb_ctx_create_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_magma_ofb_ctx_update", magma_ofb_ctx_update_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_magma_ofb_ctx_delete", magma_ofb_ctx_delete_Cmd, NULL, NULL);
// CFB
Tcl_CreateObjCommand(interp, "lcc_magma_cfb_ctx_create", magma_cfb_ctx_create_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_magma_cfb_ctx_update", magma_cfb_ctx_update_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_magma_cfb_ctx_delete", magma_cfb_ctx_delete_Cmd, NULL, NULL);
// OMAC
Tcl_CreateObjCommand(interp, "lcc_magma_omac_ctx_create", magma_omac_ctx_create_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_magma_omac_ctx_update", magma_omac_ctx_update_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_magma_omac_ctx_final", magma_omac_ctx_final_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_magma_omac_ctx_delete", magma_omac_ctx_delete_Cmd, NULL, NULL);
// CTR_ACPKM
Tcl_CreateObjCommand(interp, "lcc_magma_ctr_acpkm_ctx_create", magma_ctr_acpkm_ctx_create_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_magma_ctr_acpkm_ctx_update", magma_ctr_acpkm_ctx_update_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_magma_ctr_acpkm_ctx_delete", magma_ctr_acpkm_ctx_delete_Cmd, NULL, NULL);
// OMAC_ACPKM
Tcl_CreateObjCommand(interp, "lcc_magma_omac_acpkm_ctx_create", magma_omac_acpkm_ctx_create_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_magma_omac_acpkm_ctx_update", magma_omac_acpkm_ctx_update_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_magma_omac_acpkm_ctx_final", magma_omac_acpkm_ctx_final_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_magma_omac_acpkm_ctx_delete", magma_omac_acpkm_ctx_delete_Cmd, NULL, NULL);
// key export/import
Tcl_CreateObjCommand(interp, "lcc_magma_key_export", magma_key_export_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_magma_key_import", magma_key_import_Cmd, NULL, NULL);
// Kuznyechik commands
// ECB
Tcl_CreateObjCommand(interp, "lcc_kuznyechik_ecb_ctx_create", kuznyechik_ecb_ctx_create_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_kuznyechik_ecb_ctx_update", kuznyechik_ecb_ctx_update_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_kuznyechik_ecb_ctx_delete", kuznyechik_ecb_ctx_delete_Cmd, NULL, NULL);
// CBC
Tcl_CreateObjCommand(interp, "lcc_kuznyechik_cbc_ctx_create", kuznyechik_cbc_ctx_create_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_kuznyechik_cbc_ctx_update", kuznyechik_cbc_ctx_update_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_kuznyechik_cbc_ctx_delete", kuznyechik_cbc_ctx_delete_Cmd, NULL, NULL);
// CTR
Tcl_CreateObjCommand(interp, "lcc_kuznyechik_ctr_ctx_create", kuznyechik_ctr_ctx_create_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_kuznyechik_ctr_ctx_update", kuznyechik_ctr_ctx_update_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_kuznyechik_ctr_ctx_delete", kuznyechik_ctr_ctx_delete_Cmd, NULL, NULL);
// OFB
Tcl_CreateObjCommand(interp, "lcc_kuznyechik_ofb_ctx_create", kuznyechik_ofb_ctx_create_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_kuznyechik_ofb_ctx_update", kuznyechik_ofb_ctx_update_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_kuznyechik_ofb_ctx_delete", kuznyechik_ofb_ctx_delete_Cmd, NULL, NULL);
// CFB
Tcl_CreateObjCommand(interp, "lcc_kuznyechik_cfb_ctx_create", kuznyechik_cfb_ctx_create_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_kuznyechik_cfb_ctx_update", kuznyechik_cfb_ctx_update_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_kuznyechik_cfb_ctx_delete", kuznyechik_cfb_ctx_delete_Cmd, NULL, NULL);
// OMAC
Tcl_CreateObjCommand(interp, "lcc_kuznyechik_omac_ctx_create", kuznyechik_omac_ctx_create_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_kuznyechik_omac_ctx_update", kuznyechik_omac_ctx_update_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_kuznyechik_omac_ctx_final", kuznyechik_omac_ctx_final_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_kuznyechik_omac_ctx_delete", kuznyechik_omac_ctx_delete_Cmd, NULL, NULL);
// CTR_ACPKM
Tcl_CreateObjCommand(interp, "lcc_kuznyechik_ctr_acpkm_ctx_create", kuznyechik_ctr_acpkm_ctx_create_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_kuznyechik_ctr_acpkm_ctx_update", kuznyechik_ctr_acpkm_ctx_update_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_kuznyechik_ctr_acpkm_ctx_delete", kuznyechik_ctr_acpkm_ctx_delete_Cmd, NULL, NULL);
// OMAC_ACPKM
Tcl_CreateObjCommand(interp, "lcc_kuznyechik_omac_acpkm_ctx_create", kuznyechik_omac_acpkm_ctx_create_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_kuznyechik_omac_acpkm_ctx_update", kuznyechik_omac_acpkm_ctx_update_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_kuznyechik_omac_acpkm_ctx_final", kuznyechik_omac_acpkm_ctx_final_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_kuznyechik_omac_acpkm_ctx_delete", kuznyechik_omac_acpkm_ctx_delete_Cmd, NULL, NULL);
// key export/import
Tcl_CreateObjCommand(interp, "lcc_kuznyechik_key_export", kuznyechik_key_export_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_kuznyechik_key_import", kuznyechik_key_import_Cmd, NULL, NULL);
// gost28147 commands
Tcl_CreateObjCommand(interp, "lcc_gost28147_getParamsByOid", gost28147_getParamsByOid_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_gost28147_getParamsByDerOid", gost28147_getParamsByDerOid_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_gost28147_getParamsById", gost28147_getParamsById_Cmd, NULL, NULL);
// ECB
Tcl_CreateObjCommand(interp, "lcc_gost28147_ecb_ctx_create", gost28147_ecb_ctx_create_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_gost28147_ecb_ctx_update", gost28147_ecb_ctx_update_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_gost28147_ecb_ctx_delete", gost28147_ecb_ctx_delete_Cmd, NULL, NULL);
// CBC
Tcl_CreateObjCommand(interp, "lcc_gost28147_cbc_ctx_create", gost28147_cbc_ctx_create_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_gost28147_cbc_ctx_update", gost28147_cbc_ctx_update_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_gost28147_cbc_ctx_delete", gost28147_cbc_ctx_delete_Cmd, NULL, NULL);
// CNT
Tcl_CreateObjCommand(interp, "lcc_gost28147_cnt_ctx_create", gost28147_cnt_ctx_create_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_gost28147_cnt_ctx_update", gost28147_cnt_ctx_update_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_gost28147_cnt_ctx_delete", gost28147_cnt_ctx_delete_Cmd, NULL, NULL);
// CFB
Tcl_CreateObjCommand(interp, "lcc_gost28147_cfb_ctx_create", gost28147_cfb_ctx_create_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_gost28147_cfb_ctx_update", gost28147_cfb_ctx_update_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_gost28147_cfb_ctx_delete", gost28147_cfb_ctx_delete_Cmd, NULL, NULL);
// OMAC
Tcl_CreateObjCommand(interp, "lcc_gost28147_omac_ctx_create", gost28147_omac_ctx_create_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_gost28147_omac_ctx_update", gost28147_omac_ctx_update_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_gost28147_omac_ctx_final", gost28147_omac_ctx_final_Cmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "lcc_gost28147_omac_ctx_delete", gost28147_omac_ctx_delete_Cmd, NULL, NULL);
// KDF
Tcl_CreateObjCommand(interp, "lcc_gost28147_kdf", gost28147_kdf_Cmd, NULL, NULL);Как видно, в пакете есть поддержка не только ГОСТ Р 34.10-2021 и ГОСТ Р 34.11-2021, но и поддержка алгоритмов шифрования Кузнечик и Магма.
Второй пакет GostPfx предназначен для распарсивания контейнера PKCS#11. Эти пакеты создавались на базе библиотек из состава сертифицированного СКЗИ. Эта скрупулезная работа была проделана моим коллегой и товарищем, автором многих иллюстрацийк нашим публикациям Блажновым В.Ю. Именно реализация этих пакетов позволила создать по настоящему платформонезависимую утилиту для использования ее в электронном документеобороте. При работе с контейнером PKCS#12 на скриптовом языке Tcl выяснилась интересная особенность. Контейнер, создаваемый с использованием openssl с поддержкой российской криптографии, создается строго в DER-кодировке, а контейнер, создаваемый с использованием пакета NSS, создается с использованием BER-кодировки, в которой используются tag-и с неопределенной длиной (в поле длины tag-а находится значение 0x80). И при использовании стандартного пакета asn языка Tcl для разбора контейнера PKCS#12 выяснолось, что он не понимает asn-конструкций в кодировке BER:
if {$length == 0x080} {
return -code error "Indefinite length BER encoding not yet supported"
}Проведенный анализ пакета asn показал, что достаточно подменить функцию ::asn::asnGetLength:
package require asn
#Переименовываем оригинальную функцию
rename ::asn::asnGetLength ::asn::asnGetLength.orig
#Добавляем доработанную функцию
proc ::asn::asnGetLength {data_var length_var} {
upvar 1 $data_var data $length_var length
asnGetByte data length
if {$length == 0x080} {
#Вычисление длины тэга неопределенной длины
# Indefinite length BER encoding yet supported
set lendata [string length $data]
set tvl 1
set length 0
set data1 $data
while {$tvl != 0} {
::asn::asnGetByte data1 peek_tag
::asn::asnPeekByte data1 peek_tag1
#Конец тэга неопределенной длины
if {$peek_tag == 0x00 && $peek_tag1 == 0x00} {
incr tvl -1
::asn::asnGetByte data1 tag
incr length 2
continue
}
#Начало тэга неопределенной длины
if {$peek_tag1 == 0x80} {
incr tvl
if {$tvl > 0} {
incr length 2
}
::asn::asnGetByte data1 tag
} else {
set l1 [string length $data1]
::asn::asnGetLength data1 ll
set l2 [string length $data1]
set l3 [expr $l1 - $l2]
incr length $l3
incr length $ll
incr length
::asn::asnGetBytes data1 $ll strt
}
}
return
# return -code error "Indefinite length BER encoding not yet supported"
}
if {$length > 0x080} {
# The retrieved byte is a prefix value, and the integer in the
# lower nibble tells us how many bytes were used to encode the
# length data following immediately after this prefix.
set len_length [expr {$length & 0x7f}]
if {[string length $data] < $len_length} {
return -code error
"length information invalid, not enough octets left"
}
asnGetBytes data $len_length lengthBytes
switch $len_length {
1 { binary scan $lengthBytes cu length }
2 { binary scan $lengthBytes Su length }
3 { binary scan x00$lengthBytes Iu length }
4 { binary scan $lengthBytes Iu length }
default {
binary scan $lengthBytes H* hexstr
scan $hexstr %llx length
}
}
}
return
}Такая подмена позволяет обрабатывать tag-и неопределенной длины.
Итак, мы запустили утилиту crytoarmpkcs, загрузили контейнер PKCS#12 с сертификатом всесильного Хабра,
полученным ранее
, и просматриваем сертификат:

Теперь можно подписывать документы не задействуя никакого стороннего СКЗИ, главное, чтобы была связь с интернетом, для получения цепочки сертификатов, штампов времени, списков отозванных сертификатов, ответов OCSP.
Процесс подписания ничем не отличается от описанного ранее:

Полученную подпись также можно смело проверять на сайте Госуслуг или другом сервисе.
Как уже отмечалось, сертификат из контейнера можно экспортировать как в файл, так и на токен. При экспорте сертификата на токен будет проверена его подпись:

Также можно импортировать закрытый ключ на токен:

Надо иметь ввиду, что не все токены позволяют импортировать на них закрытые ключи. С нашей точки зрения, это функция прежде всего необходима при хранении личного сертификата в облачном токене PKCS#11.
А о том, как работать с токенами PKCS#11, было рассказано в предыдущей статье.
И осталось самая малость (у нас есть еще кнопка «Резерв»), — это шифрование документов на сертификате получателя (на открытом ключе). Но об этом расскажем в следующий раз.
Общие сведения об утилите certtool
Как уже было отмечено, утилита certtool имеет много общего с утилитой openssl.
Первым параметром утилиты certtool, как правило, идет флаг, который определяет какую функцию необходимо выполнить. Например, флаг “–certificate-info”, указывает на необходимость разобрать сертификат, а флаг “–generate-privkey” предписывает провести генерацию закрытого ключа.
Если данные будут поступать в формате DER, то необходимо задать опцию “–inder”.
Результатом выполнения соответствующей функции является ASN1-структура (тот же сертификат, например). По умолчанию, выходная ASN1-структура представляется в PEM-формате. Если её необходимо получить в DER-формате, то добавляется опция “–outder”. Вместе с ASN1-структурой по умолчанию (опция “–text”) выводится и её содержимое в текстовом виде. Если нет необходимости выводить текстовый вид, то задается опция “–no-text”.
Все это можно продемонстрировать на примере конвертации файлов с сертификатами из формата PEM в DER и наоборот:
#Конвертация файла с сертификатом из формата PEM в DER
#Если установлена опция --outder, то опция --no-text устанавливается по умолчанию.
$certtool --certificate-info --infile certPEM.pem --outder --outfile certDER.der
#Конвертация файла с сертификатом из формата DER в PEM
$certtool --certificate-info --inder --infile certDER.der --no-text --outfile certPEM_new.pem
$
Для получения справки по утилите certtool выполните команду:
$certtool –help
certtool - GnuTLS certificate tool
Usage: certtool [ -<flag> [<val>] | --<name>[{=| }<val>] ]...
-d, --debug=num Enable debugging
- it must be in the range:
0 to 9999
-V, --verbose More verbose output
- may appear multiple times
--infile=file Input file
- file must pre-exist
--outfile=str Output file
Certificate related options:
-i, --certificate-info Print information on the given certificate
--pubkey-info Print information on a public key
-s, --generate-self-signed Generate a self-signed certificate
-c, --generate-certificate Generate a signed certificate
--generate-proxy Generates a proxy certificate
-u, --update-certificate Update a signed certificate
--fingerprint Print the fingerprint of the given certificate
--key-id Print the key ID of the given certificate
--v1 Generate an X.509 version 1 certificate (with no extensions)
--sign-params=str Sign a certificate with a specific signature algorithm
Certificate request related options:
--crq-info Print information on the given certificate request
-q, --generate-request Generate a PKCS #10 certificate request
- prohibits the option 'infile'
--no-crq-extensions Do not use extensions in certificate requests
PKCS#12 file related options:
--p12-info Print information on a PKCS #12 structure
--p12-name=str The PKCS #12 friendly name to use
--to-p12 Generate a PKCS #12 structure
Private key related options:
-k, --key-info Print information on a private key
--p8-info Print information on a PKCS #8 structure
--to-rsa Convert an RSA-PSS key to raw RSA format
-p, --generate-privkey Generate a private key
--key-type=str Specify the key type to use on key generation
--bits=num Specify the number of bits for key generation
--curve=str Specify the curve used for EC key generation
--sec-param=str Specify the security level [low, legacy, medium, high, ultra]
--to-p8 Convert a given key to a PKCS #8 structure
-8, --pkcs8 Use PKCS #8 format for private keys
--provable Generate a private key or parameters from a seed using a provable method
--verify-provable-privkey Verify a private key generated from a seed using a provable method
--seed=str When generating a private key use the given hex-encoded seed
CRL related options:
-l, --crl-info Print information on the given CRL structure
--generate-crl Generate a CRL
--verify-crl Verify a Certificate Revocation List using a trusted list
- requires the option 'load-ca-certificate'
Certificate verification related options:
-e, --verify-chain Verify a PEM encoded certificate chain
--verify Verify a PEM encoded certificate (chain) against a trusted set
--verify-hostname=str Specify a hostname to be used for certificate chain verification
--verify-email=str Specify a email to be used for certificate chain verification
- prohibits the option 'verify-hostname'
--verify-purpose=str Specify a purpose OID to be used for certificate chain verification
--verify-allow-broken Allow broken algorithms, such as MD5 for verification
--verify-profile=str Specify a security level profile to be used for verification
PKCS#7 structure options:
--p7-generate Generate a PKCS #7 structure
--p7-sign Signs using a PKCS #7 structure
--p7-detached-sign Signs using a detached PKCS #7 structure
--p7-include-cert The signer's certificate will be included in the cert list.
- disabled as '--no-p7-include-cert'
- enabled by default
--p7-time Will include a timestamp in the PKCS #7 structure
- disabled as '--no-p7-time'
--p7-show-data Will show the embedded data in the PKCS #7 structure
- disabled as '--no-p7-show-data'
--p7-info Print information on a PKCS #7 structure
--p7-verify Verify the provided PKCS #7 structure
--smime-to-p7 Convert S/MIME to PKCS #7 structure
Other options:
--get-dh-params List the included PKCS #3 encoded Diffie-Hellman parameters
--dh-info Print information PKCS #3 encoded Diffie-Hellman parameters
--load-privkey=str Loads a private key file
--load-pubkey=str Loads a public key file
--load-request=str Loads a certificate request file
--load-certificate=str Loads a certificate file
--load-ca-privkey=str Loads the certificate authority's private key file
--load-ca-certificate=str Loads the certificate authority's certificate file
--load-crl=str Loads the provided CRL
--load-data=str Loads auxiliary data
--password=str Password to use
--null-password Enforce a NULL password
--empty-password Enforce an empty password
--hex-numbers Print big number in an easier format to parse
--cprint In certain operations it prints the information in C-friendly format
--hash=str Hash algorithm to use for signing
--salt-size=num Specify the RSA-PSS key default salt size
--inder Use DER format for input certificates, private keys, and DH parameters
- disabled as '--no-inder'
--inraw an alias for the 'inder' option
--outder Use DER format for output certificates, private keys, and DH parameters
- disabled as '--no-outder'
--outraw an alias for the 'outder' option
--template=str Template file to use for non-interactive operation
--stdout-info Print information to stdout instead of stderr
--ask-pass Enable interaction for entering password when in batch mode.
--pkcs-cipher=str Cipher to use for PKCS #8 and #12 operations
--provider=str Specify the PKCS #11 provider library
--text Output textual information before PEM-encoded certificates, private
keys, etc
- disabled as '--no-text'
- enabled by default
Version, usage and configuration options:
-v, --version[=arg] output version information and exit
-h, --help display extended usage information and exit
-!, --more-help extended usage information passed thru pager
Options are specified by doubled hyphens and their name or by a single
hyphen and the flag character.
Tool to parse and generate X.509 certificates, requests and private keys.
It can be used interactively or non interactively by specifying the
template command line option.
The tool accepts files or supported URIs via the --infile option. In case
PIN is required for URI access you can provide it using the environment
variables GNUTLS_PIN and GNUTLS_SO_PIN.
Please send bug reports to: <bugs@gnutls.org>
Теперь переходим к основным функциям утилиты certtool
