- /certificates — все установленные сертификаты пользователя
- /cosign — добавление еще одной подписи к документу
- /sign — подписание документов
- /unsign — получение исходного файла без подписей
- /verify — проверка подписанного документа
- Аналоги
- Без скачивания на диск
- Возможные проблемы
- Где в реестре хранится эцп
- Где хранится сертификат эцп в ос windows xp
- Где хранится эцп в системах linux
- Естп — электронная торговая площадка
- Запуск контейнера
- Использование контейнера на удаленной машине
- Как посмотреть сертификат эцп
- Компиляция openssl библиотеки
- Компиляция privkey.c
- Криптопро 4.0 в докер контейнере
- Лицензия
- Обработка ошибок
- Подписание документа
- Получение исходного файла из sig-файла
- Пользуемся закрытым ключом private.key для подписывания файла file.txt
- Проблемы
- Проверка подписи
- Проверяем подпись
- Просмотр установленных сертификатов
- Сборка утилиты конвертирования ключа
- Установка корневых сертификатов
- Установка сертификатов пользователя для проверки и подписания
- Файл header.key
- Файл masks.key
- Файл primary.key
- Формат данных
- Формирование файла закрытого ключа private.key
- Через internet explorer
- Через консоль управления
- Через криптопро
- Через скачивание на диск
- Через certmgr
- Читаем закрытый ключ и конвертируем
/certificates — все установленные сертификаты пользователя
Если сертификатов нет:
Если сертификаты есть:
{
"certificates": [
{
"privateKey": {
"providerName": "Crypto-Pro GOST R 34.10-2022 KC1 CSP",
"uniqueContainerName": "HDIMAGE\\eb5f6857.000\D160",
"containerName": "eb5f6857-a08a-4510-8a96-df2f75b6d65a"
},
"algorithm": {
"name": "ГОСТ Р 34.10-2022",
"val": "1.2.643.7.1.1.1.1"
},
"valid": {
"to": "24.05.2022 08:13:16",
"from": "24.02.2022 08:03:16"
},
"issuer": {
"E": "support@cryptopro.ru",
"C": "RU",
"L": "Moscow",
"O": "CRYPTO-PRO LLC",
"CN": "CRYPTO-PRO Test Center 2",
"raw": "CN=CRYPTO-PRO Test Center 2, O=CRYPTO-PRO LLC, L=Moscow, C=RU, E=support@cryptopro.ru"
},
"subject": {
"C": "RU",
"L": "Test",
"O": "Test",
"OU": "Test",
"CN": "Test",
"E": "test@test.ru",
"raw": "E=test@test.ru, CN=Test, OU=Test, O=Test, L=Test, S=Test, C=RU"
},
"thumbprint": "982AA9E713A2F99B10DAA07DCDC94A4BC32A1027",
"serialNumber": "120032C3567443029CC358FCDF00000032C356",
"hasPrivateKey": true
}
],
"status": "ok"
}/cosign — добавление еще одной подписи к документу
Не реализовано, столкнулся с проблемой: не получается заставить работать функцию CPSignedData::CoSignCades().
/sign — подписание документов
Для выбора сертификата для подписания нужно указать один критерии поиска find_type:
sha1— по SHA1 сертификатаsubject— поsubjectподписанта
В query — параметры поиска, в pin — пин-код (если он установлен):
Вернется JSON — документ, в signedContent будет содержаться подписанный файл.
/unsign — получение исходного файла без подписей
Исходный файл вернется в поле content.
/verify — проверка подписанного документа
Подпишем файл и проверим его:
Если файл прошел проверку, вернется список подписантов signers.
Аналоги
Существует аналогичный пакет CryptoProCSP, он классный, но:
- давно не обновлялся, используется версия
PHP5.6 - для запуска пришлось подредактировать
Dockerfile
Без скачивания на диск
Примечание: по какой-то причине иногда «заедает», но при повторном запуске — срабатывает.
Возможные проблемы
В Dockerfile содержатся названия пакетов, например lsb-cprocsp-devel-4.0.9921-5.noarch.rpm, которые могут заменить новой версией. Следует поправить названия пакетов в Dockerfile.
Где в реестре хранится эцп
Иногда реестр используется как ключевой носитель, т.е. он подходит для импорта и экспорта сертификатов. Где находится сертификат ЭЦП зависит от битности системы:
Где хранится сертификат эцп в ос windows xp
К базовым компонентам ОС Windows XP относятся службы сертификации, а XP Professional уже поддерживает многоуровневые иерархии центра сертификации (ЦС) как с изолированными и интерактивными ЦС, так и сети ЦС с доверительными перекрестными отношениями.
Открытые ключи ЭЦП Windows XP хранит в личном хранилище, а т.к. они представляют собой общедоступную информацию, то хранятся в виде открытого текста. Сертификаты пользователя находятся по адресу:
Documents and Settings<имя_пользователя>ApplicationDataMicrosoft SystemCertificatesMyCertificates. Они автоматически вносятся в локальный реестр при каждом входе в систему. Если профиль перемещаемый, то открытые ключи хранятся обычно не на ПК, а следуют за пользователем при каждом входе в систему с внешнего носителя.
Такие поставщики услуг криптографии, как Enchanced CSP и Base CSP хранят закрытые ключи электронной подписи в папке %SystemRoot%Documents and Settings<имя_пользователя> Application DataMicrosoftCryptoRSA. Если профиль перемещаемый, то они расположены в папке RSA на контроллере домена.
В этом случае на ПК они загружаются только на время работы профиля. Все файлы в данной папке шифруются случайным симметричным ключом автоматически. Основной ключ пользователя имеет длину в 64 символа и создается проверенным генератором случайных чисел.
Где хранится эцп в системах linux
В Linux системах список контейнеров с закрытыми ключами можно найти при помощи утилиты csptest. Находится она в директории: /opt/cprocsp/bin/<архитектура>.
Список контейнеров компьютера: csptest -keyset -enum_cont -verifycontext -fqcn -machinekeys. Список контейнеров пользователя: csptest -keyset -enum_cont -verifycontext -fqcn. В списках имена контейнеров даются в виде, понятном для бинарных утилит, входящих в состав дистрибутива CSP.
Закрытые ключи хранятся в хранилище HDImageStore на жестком диске, и доступны они и для CSP, и для JCP.
Просмотреть список ключей электронной подписи на ОС Windows может только пользователь с правами администратора. Обычно ключи хранятся в системных папках и имеют расширение .cer или .csr. Чтобы посмотреть список, удалить ненужные элементы или скопировать их на внешний носитель можно воспользоваться программой КриптоПро, браузером Internet Explorer, консолью управления или специальной утилитой certmgr.
В Windows XP открытые и закрытые ключи хранятся в разных папках, а закрытые дополнительно шифруются паролем, состоящим из комбинации случайных чисел. Системы Linux хранят все сертификаты в отдельной директории, а пусть к ним задается вручную при помощи команд.
Естп — электронная торговая площадка
Описание процесса установки приведено на примере дистрибутива семейства Debian (x64).
Названия файлов и директорий могут варьироваться от системы к системе.
При установке личных сертификатов не нужны права суперпользователя, и наоборот, при установкесертификатов УЦ(корневых и промежуточных) могут потребоваться такие права.
Подключите USB носитель с ключевыми контейнерами и проверьте результат команды:
/opt/cprocsp/bin/amd64/csptest -keyset -enum_cont -fqcn -verifycCSP (Type:80) v4.0.9009 KC1 Release Ver:4.0.9797 OS:Linux CPU:AMD64 FastCode:READY:AVX.
AcquireContext: OK. HCRYPTPROV: 16188003
\.FLASHivanov
\.FLASHpetrov
\.FLASHsidorov
\.FLASHvasiliev
\.FLASHsmirnov
OK.
Total: SYS: 0,020 sec USR: 0,060 sec UTC: 0,180 secМожно сразу установить личные сертификатов из всех доступных контейнеров одной командой:
/opt/cprocsp/bin/amd64/csptestf -absorb -certs
Произойдет установка сертификатов, находящихся во всех доступных в момент запуска команды контейнерах (съемных флэш-носителях, жесткого диска и т. д.) в хранилище uMy.
При необходимости скопируйте ключевой контейнер\.FLASH.sidorov на жесткий диск:
/opt/cprocsp/bin/amd64/csptest -keycopy -contsrc '\.FLASHsidorov' -contdest '\.HDIMAGEsidor'CSP (Type:80) v4.0.9009 KC1 Release Ver:4.0.9797 OS:Linux CPU:AMD64 FastCode:READY:AVX.
CryptAcquireContext succeeded.HCRYPTPROV: 38556259
CryptAcquireContext succeeded.HCRYPTPROV: 38770755
Total: SYS: 0,000 sec USR: 0,100 sec UTC: 14,920 sec
[ErrorCode: 0x00000000]Наличие [ErrorCode: 0x00000000] в завершении каждой команды КриптоПРО говорит о ее успешном выполнении.
Проверьте наличие нового контейнера\.HDIMAGEsidor:
/opt/cprocsp/bin/amd64/csptest -keyset -enum_cont -fqcn -verifycCSP (Type:80) v4.0.9009 KC1 Release Ver:4.0.9797 OS:Linux CPU:AMD64 FastCode:READY:AVX.
AcquireContext: OK. HCRYPTPROV: 34554467
\.FLASHivanov
\.FLASHpetrov
\.FLASHsidorov
\.FLASHvasiliev
\.FLASHsmirnov
\.HDIMAGEsidor
OK.
Total: SYS: 0,010 sec USR: 0,050 sec UTC: 0,130 sec
[ErrorCode: 0x00000000]Установите личный сертификат:
/opt/cprocsp/bin/amd64/certmgr -inst -cont '.HDIMAGEsidor'Certmgr 1.0 (c) "CryptoPro", 2007-2022.
program for managing certificates, CRLs and stores
Install:
=============================================================================
1-------
Issuer : OGRN=1234567890123, INN=1234567890, STREET=Арбат, E=info@berkut.ru, C=RU, S=77 г.Москва, L=Москва, O=ООО 'Беркут', OU=Удостоверяющий центр, CN=UCESTP
Subject : SNILS=12345678901, OGRN=1234567890123, INN=0011234567890, E=sidorov@mail.ru, C=RU, S=10 Республика Карелия, L=Петрозаводск, O=ООО Ромашка, CN=Сидоров Николай Павлович, T=Администратор, G=Николай Павлович, SN=Сидоров
Serial : 0x12C40953000000000019
SHA1 Hash : 0xdd0ea8db46a372571c55315cd7e4d8e2de8fb9b6
SubjKeyID : 5fc37e578cce0abe739c4da227f68a2f9abcb128
Signature Algorithm : ГОСТ Р 34.11/34.10-2001
PublicKey Algorithm : ГОСТ Р 34.10-2001 (512 bits)
Not valid before : 09/08/2022 06:07:00 UTC
Not valid after : 09/11/2022 06:17:00 UTC
PrivateKey Link : No
CA cert URL : <mark>http://cert1.ucestp.ru/estp.crt</mark>
Extended Key Usage : 1.3.6.1.5.5.7.3.4
1.3.6.1.5.5.7.3.2
=============================================================================
[ErrorCode: 0x00000000]Скачайте корневой сертификат по ссылке выше (из поляCA cert URL): http://cert1.ucecp64.ru/estp.crt и перенесите его, например, в домашнюю папку:
cp ~/Загрузки/estp.crt ~/estp.crtУстановите корневой сертификат (возможно потребуются права суперпользователя):
/opt/cprocsp/bin/amd64/certmgr -inst -store uroot -file ~/estp.crtCertmgr 1.0 (c) "CryptoPro", 2007-2022.
program for managing certificates, CRLs and stores
Install:
=============================================================================
1-------
Issuer : OGRN=1234567890123, INN=1234567890, STREET=Арбат, E=info@berkut.ru, C=RU, S=77 г.Москва, L=Москва, O=ООО 'Беркут', OU=Удостоверяющий центр, CN=UCESTP
Subject : OGRN=1234567890123, INN=1234567890, STREET=Арбат, E=info@berkut.ru, C=RU, S=77 г.Москва, L=Москва, O=ООО 'Беркут', OU=Удостоверяющий центр, CN=UCESTP
Serial : 0x50D7BC0E4A3EC9994454EB83013EE5F5
SHA1 Hash : 0xd2144a3e098b6f2decb224257f48e2b7c6d85209
SubjKeyID : 07b753cd561dee8e5e83407be4575ecc05bfec14
Signature Algorithm : ГОСТ Р 34.11/34.10-2001
PublicKey Algorithm : ГОСТ Р 34.10-2001 (512 bits)
Not valid before : 17/06/2022 13:15:21 UTC
Not valid after : 17/06/2021 13:25:01 UTC
PrivateKey Link : No
=============================================================================
[ErrorCode: 0x00000000]Проверьте установку личного сертификата:
/opt/cprocsp/bin/amd64/certmgr -list -store uMyCertmgr 1.0 (c) "CryptoPro", 2007-2022.
program for managing certificates, CRLs and stores
=============================================================================
1-------
Issuer : OGRN=1234567890123, INN=1234567890, STREET=Арбат, E=info@berkut.ru, C=RU, S=77 г.Москва, L=Москва, O=ООО 'Беркут', OU=Удостоверяющий центр, CN=UCESTP
Subject : SNILS=12345678901, OGRN=1234567890123, INN=0011234567890, E=sidorov@mail.ru, C=RU, S=10 Республика Карелия, L=Петрозаводск, O=ООО Ромашка, CN=Сидоров Николай Павлович, T=Администратор, G=Николай Павлович, SN=Сидоров
Serial : 0x12C40953000000000019
SHA1 Hash : 0xdd0ea8db46a372571c55315cd7e4d8e2de8fb9b6
SubjKeyID : 5fc37e578cce0abe739c4da227f68a2f9abcb128
Signature Algorithm : ГОСТ Р 34.11/34.10-2001
PublicKey Algorithm : ГОСТ Р 34.10-2001 (512 bits)
Not valid before : 09/08/2022 06:07:00 UTC
Not valid after : 09/11/2022 06:17:00 UTC
PrivateKey Link : Yes
Container : HDIMAGE\sidor.0002B01
Provider Name : Crypto-Pro GOST R 34.10-2022 KC1 CSP
Provider Info : ProvType: 80, KeySpec: 1, Flags: 0x0
CA cert URL : http://cert1.ucestp.ru/estp.crt
Extended Key Usage : 1.3.6.1.5.5.7.3.4
1.3.6.1.5.5.7.3.2
=============================================================================
[ErrorCode: 0x00000000]Запуск контейнера
Запустим контейнер под именем cryptopro, к которому будем обращаться в примерах:
Использование контейнера на удаленной машине
В примерах выше команды выглядят так: cat … | docker … или curl … | docker …, то есть контейнер запущен на локальной машине. Если же докер контейнер запущен на удаленной машине, то команды нужно отправлять через ssh клиент. Например, команда подписания:
Опция -q отключает приветствие из файла /etc/banner (хотя оно все равно пишется в stderr). А /etc/motd при выполнении команды по ssh не выводится.
В качестве эксперимента можно отправить по ssh на свою же машину так:
Как посмотреть сертификат эцп
Посмотреть установленные сертификаты можно при помощи Internet Explorer, Certmgr, консоль управления или КриптоПро. Пользоваться другими компьютерными программами не рекомендуется: они могут иметь встроенную команду отправки ключа ЭЦП на сторонний сервер, что приведет к компрометации подписи и невозможности ее использования.
Компиляция openssl библиотеки
После
и распаковки исходных текстов openssl в целевой директории выполняем команды:
./config
make
Получаем готовую библиотеку libcrypto.a в текущей директории.
Также потребуются заголовочные файлы из директорий engines/ccgost и include.
Компиляция privkey.c
gcc -o privkey -Iengines/ccgost -Iinclude privkey.c libcrypto.a -pthread -ldl
Криптопро 4.0 в докер контейнере
Содержимое контейнера:
Лицензия
Установка серийного номера:
Просмотр:
Обработка ошибок
Успешные действия возвращают код 200 и «status»: «ok».
Действия с ошибками возвращают 4xx и 5xx коды и «status»: «fail», в полях errMsg содержится описание ошибки, в errCode — ее код.
Например, обращение с неправильным методом
выведет такую ошибку:
Подписание документа
Для примера установим этот тестовый сертификат:
Его SHA1 Hash равен dd45247ab9db600dca42cc36c1141262fa60e3fe (узнать: certmgr -list), который будем использовать как указатель нужного сертификата.
Теперь передадим на stdin файл, в качестве команды — последовательность действий, и на stdout получим подписанный файл:
Получилось довольно неудобно. Скрипт scripts/sign делает то же самое, теперь команда подписания будет выглядеть так:
Об ошибке можно узнать через стандартный $?.
Получение исходного файла из sig-файла
Возьмем файл из примера выше:
То же самое, но с использованием скрипта:
Пользуемся закрытым ключом private.key для подписывания файла file.txt
openssl cms -sign -inkey private.key -in file.txt -CAfile CA.cer -signer signer.cer -engine gost -out test.sign -outform DER -noattr -binary
Проблемы
Если pin-код не подходит, то в терминал выводится:
И подписание останавливается.
Проверка подписи
Подпишем файл из примера выше и сохраним его на диск:
Тогда проверка подписанного файла будет выглядеть так:
То же самое, но с использованием скрипта:
Проверяем подпись
openssl cms -verify -content file.txt -in test.sign -CAfile CA.cer -signer signer.cer -engine gost -inform DER -noattr -binary
Все работает просто замечательно!
Спасибо за внимание. Это была моя первая статья на хабре.
Просмотр установленных сертификатов
Сертификаты пользователя:
Корневые сертификаты:
Сборка утилиты конвертирования ключа
Далее сборка исходников описана для Linux версии.
Установка корневых сертификатов
Для установки корневых сертификатов нужно на stdin скрипта /scripts/root передать файл с сертификатами. Если в файле несколько сертификатов, все они будут установлены.
Установка сертификатов пользователя для проверки и подписания
Необходимо специальным образом сформировать zip-архив bundle.zip и отправить его на stdin скрипта /scripts/my. Пример такого zip-файла:
Как получить сертификат КриптоПро.
Первый найденный файл в корне архива будет воспринят как сертификат, а первый найденный каталог — как связка файлов закрытого ключа. Пароль от контейнера, если есть, передается первым параметром командной строки.
В каталоге certificates/ содержатся различные комбинации тестового сертификата и закрытого ключа, с PIN кодом и без:
Примеры:
Файл header.key
Из этого файла нам потребуется параметры электронной подписи CryptoProParamSet (подчеркнуто красным).
А также первые 8 байт открытого ключа (подчеркнуто) для контроля правильности чтения закрытого.
Файл masks.key
Содержит 32 байта маски ключа в формате Asn1, зашифрованного на ключе хранения pwd_key. Далее 12 байт «затравочной» информации для генерации ключа хранения pwd_key, если криптоконтейнер защищен паролем, то пароль также участвует в генерации ключа хранения.
Далее контрольная сумма (имитозащита) 4 байта. Контрольной информацией для простоты мы пользоваться не будем, общий контроль будет осуществляться путем генерации открытого ключа и сравнения первых 8 байт полученного ключа с соответствующим полем из файла header.key:
Файл primary.key
Содержит 32 байта ключа в формате Asn1. Это только половина ключа, полный ключ получается при делении этого числа по модулю Q на маску. Поле, хранящее модуль Q в библиотеке OpenSSL имеет название order. Маска лежит в файле masks.key:
Формат данных
Для POST данные должны поступать в теле запроса в формате x-www-form-urlencoded.
Возвращаются данные в формате JSON.
Формирование файла закрытого ключа private.key
./privkey /mnt/usbflash/lp-9a0fe.000
Тестовый закрытый ключ в криптоконтейнере lp-9a0fe.000, сертификат открытого ключа signer.cer и другие файлы для тестирования можно
Получаем результат работы:
-----BEGIN PRIVATE KEY-----
MEYCAQAwHAYGKoUDAgITMBIGByqFAwICJAAGByqFAwICHgEEIwIhAKzsrv/l1Uwk
uzph/LQN9mux0Jz0yaW21kOYEFv0Xyut
-----END PRIVATE KEY-----
Cохраняем в private.key
Через internet explorer
Интернет-браузер IE входит в комплектацию всех ОС семейства Windows XP и выше и позволяет также найти сертификаты ЭЦП на компьютере. Для просмотра нужно:
- запустить браузер;
- через «Меню» перейти в «Свойства браузера»;
- в новом окне выбрать вкладку «Содержание»;
- выбрать «Сертификаты».
Далее откроется окно с перечнем всех сертификатов, установленных пользователем и сторонними поставщиками ПО. В данном меню возможен перенос ключей и сертификатов на внешний носитель, удаление открытых ключей. Удаление корневых сертификатов УЦ через меню IE невозможно.
Через консоль управления
Просмотр сертификатов в ОС Windows через консоль управления запускается в несколько этапов:
- пользователь открывает командную строку;
- вводит команду mmc и нажимает Enter;
- нажимает на «Файл» и «Добавить/удалить оснастку cryptopro»;
- выбирает последовательно «Добавить» и «Добавить изолированную оснастку»;
- выбирает «Сертификаты».
Дополнительно можно просмотреть ключи ЭЦП по конкретной учетной записи. Способ также доступен только пользователям с правами администратора ПК. Через консоль можно не только просматривать информация, но и удалять, копировать, добавлять контейнеры с ключами ЭЦП.
Через криптопро
Как найти сертификат ЭЦП на компьютере при помощи КриптоПро:
- открыть «Пуск»;
- через вкладку «Все программы» перейти в «КриптоПро»;
- выбрать вкладку «Сертификаты».
Этим способом могут воспользоваться пользователи или администраторы ПК (если пользователю будет отказано в доступе, необходимо дополнительно запросить права администратора). В открывшемся окне будет список всех сертификатов, установленных на данном ПК.
Через скачивание на диск
Скачаем сертификат на диск с помощью curl и передадим полученный файл на stdin с запуском команды его установки:
Через certmgr
Найти файл сертификата можно и при помощи встроенного менеджера, который присутствует во всех ОС Windows. Через него можно не только посмотреть все личные сертификаты, но и сертификаты УЦ и партнеров Microsoft.
Метод может использоваться только администратором ПК. Для просмотра нужно:
- открыть «Пуск»;
- ввести команду certmgr.msc в строку поиска и нажать Enter;
- в левой колонке открывшегося окна будет список личных и корневых сертификатов.
С шифрованными сертификатами приложение работает ограниченно и не всегда корректно отражает все электронные подписи, установленные пользователем.
Читаем закрытый ключ и конвертируем
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/pem.h>
#include <openssl/cms.h>
#include <openssl/err.h>
#include "gost_lcl.h"
/* Convert little-endian byte array into bignum */
BIGNUM *reverse32bn(char *b, BN_CTX *ctx)
{
BIGNUM *res;
char buf[32];
BUF_reverse(buf, b, 32);
res = BN_bin2bn(buf, 32, BN_CTX_get(ctx));
OPENSSL_cleanse(buf, sizeof(buf));
return res;
}
void xor_material(char *buf36, char *buf5C, char *src)
{
int i;
for(i = 0; i < 32; i )
{
buf36[i] = src[i] ^ 0x36;
buf5C[i] = src[i] ^ 0x5C;
}
}
int make_pwd_key(char *result_key, char *start12, int start12_len, char *passw)
{
int result;
int i;
char pincode4[1024];
int pin_len;
char current[32];
char material36[32];
char material5C[32];
char hash_result[32];
gost_hash_ctx ctx;
init_gost_hash_ctx(&ctx, &GostR3411_94_CryptoProParamSet);
memset(pincode4, 0, sizeof(pincode4));
pin_len = strlen(passw);
if (pin_len*4 > sizeof(pincode4)) { result = 1; goto err; }
for(i = 0; i < pin_len; i )
pincode4[i*4] = passw[i];
start_hash(&ctx);
hash_block(&ctx, start12, start12_len);
if (pin_len)
hash_block(&ctx, pincode4, pin_len * 4);
finish_hash(&ctx, hash_result);
memcpy(current, (char*)"DENEFH028.760246785.IUEFHWUIO.EF", 32);
for(i = 0; i < (pin_len?2000:2); i )
{
xor_material(material36, material5C, current);
start_hash(&ctx);
hash_block(&ctx, material36, 32);
hash_block(&ctx, hash_result, 32);
hash_block(&ctx, material5C, 32);
hash_block(&ctx, hash_result, 32);
finish_hash(&ctx, current);
}
xor_material(material36, material5C, current);
start_hash(&ctx);
hash_block(&ctx, material36, 32);
hash_block(&ctx, start12, start12_len);
hash_block(&ctx, material5C, 32);
if (pin_len)
hash_block(&ctx, pincode4, pin_len * 4);
finish_hash(&ctx, current);
start_hash(&ctx);
hash_block(&ctx, current, 32);
finish_hash(&ctx, result_key);
result = 0; //ok
err:
return result;
}
BIGNUM *decode_primary_key(char *pwd_key, char *primary_key, BN_CTX *bn_ctx)
{
BIGNUM *res;
char buf[32];
gost_ctx ctx;
gost_init(&ctx, gost_cipher_list->sblock);
gost_key(&ctx, pwd_key);
gost_dec(&ctx, primary_key, buf, 4);
res = reverse32bn(buf, bn_ctx);
OPENSSL_cleanse(buf, sizeof(buf));
return res;
}
BIGNUM *remove_mask_and_check_public(char *oid_param_set8, BIGNUM *key_with_mask, BIGNUM *mask, char *public8, BN_CTX *ctx)
{
int result;
EC_KEY *eckey = NULL;
const EC_POINT *pubkey;
const EC_GROUP *group;
BIGNUM *X, *Y, *order, *raw_secret, *mask_inv;
char outbuf[32], public_X[32];
ASN1_OBJECT *obj;
int nid;
order = BN_CTX_get(ctx);
mask_inv = BN_CTX_get(ctx);
raw_secret = BN_CTX_get(ctx);
X = BN_CTX_get(ctx);
Y = BN_CTX_get(ctx);
if (!order || !mask_inv || !raw_secret || !X || !Y) { result = 1; goto err; }
obj = ASN1_OBJECT_create(0, oid_param_set8 1, *oid_param_set8, NULL, NULL);
nid = OBJ_obj2nid(obj);
ASN1_OBJECT_free(obj);
if (!(eckey = EC_KEY_new())) { result = 1; goto err; }
if (!fill_GOST2001_params(eckey, nid)) { result = 1; goto err; }
if (!(group = EC_KEY_get0_group(eckey))) { result = 1; goto err; }
if (!EC_GROUP_get_order(group, order, ctx)) { result = 1; goto err; }
if (!BN_mod_inverse(mask_inv, mask, order, ctx)) { result = 1; goto err; }
if (!BN_mod_mul(raw_secret, key_with_mask, mask_inv, order, ctx)) { result = 1; goto err; }
if (!EC_KEY_set_private_key(eckey, raw_secret)) { result = 1; goto err; }
if (!gost2001_compute_public(eckey)) { result = 1; goto err; }
if (!(pubkey = EC_KEY_get0_public_key(eckey))) { result = 1; goto err; }
if (!EC_POINT_get_affine_coordinates_GFp(group, pubkey, X, Y, ctx)) { result = 1; goto err; }
store_bignum(X, outbuf, sizeof(outbuf));
BUF_reverse(public_X, outbuf, sizeof(outbuf));
if (memcmp(public_X, public8, 8) != 0) { result = 1; goto err; }
result = 0; //ok
err:
if (eckey) EC_KEY_free(eckey);
if (result == 0) return raw_secret;
return NULL;
}
int file_length(char *fname)
{
int len;
FILE *f = fopen(fname, "rb");
if (f == NULL) return -1;
fseek(f, 0, SEEK_END);
len = ftell(f);
fclose(f);
return len;
}
int read_file(char *fname, int start_pos, char *buf, int len)
{
int read_len;
FILE *f = fopen(fname, "rb");
if (f == NULL) return 1;
if (start_pos) fseek(f, start_pos, SEEK_SET);
read_len = fread(buf, 1, len, f);
fclose(f);
if (read_len != len) return 1;
return 0; //ok
}
int get_asn1_len(unsigned char *buf, int *size_hdr)
{
int n, i, res;
int pos = 0;
if ((buf[pos]&0x80) == 0) {
*size_hdr = 1;
return buf[pos];
}
n = buf[pos ]&0x7f;
res = 0;
for(i = 0; i < n; i ) {
res = res*256 buf[pos ];
}
*size_hdr = n 1;
return res;
}
#define MAX_HEADER 20000
int read_container(char *fpath, int flag2, char *salt12, char *primary_key, char *masks_key, char *public8, char *oid_param_set8)
{
int result;
char primary_path[1024 30];
char masks_path[1024 30];
char header_path[1024 30];
char header_buf[MAX_HEADER];
int header_len;
int i, len, pos, size_hdr;
if (strlen(fpath)>1024) { result = 1; goto err; }
sprintf(header_path, "%s/header.key", fpath);
if (flag2 == 0)
{
sprintf(primary_path, "%s/primary.key", fpath);
sprintf(masks_path, "%s/masks.key", fpath);
}
else
{
sprintf(primary_path, "%s/primary2.key", fpath);
sprintf(masks_path, "%s/masks2.key", fpath);
}
if (read_file(primary_path, 4, primary_key, 32)) { result = 1; goto err; }
if (read_file(masks_path, 4, masks_key, 32)) { result = 1; goto err; }
if (read_file(masks_path, 0x26, salt12, 12)) { result = 1; goto err; }
header_len = file_length(header_path);
if (header_len < 0x42 || header_len > MAX_HEADER) { result = 1; goto err; }
if (read_file(header_path, 0, header_buf, header_len)) { result = 1; goto err; }
//------------- skip certificate ---------------------------
pos = 0;
for(i = 0; i < 2; i )
{
get_asn1_len(header_buf pos 1, &size_hdr);
pos = size_hdr 1;
if (pos > header_len-8) { result = 2; goto err; }
}
//------------------ get oid_param_set8 -----------------------
#define PARAM_SET_POS 34
if (memcmp(header_buf pos PARAM_SET_POS, "x6x7", 2) != 0) { result = 2; goto err; }
memcpy(oid_param_set8, header_buf pos PARAM_SET_POS 1, 8);
//------------------ get public8 -----------------------
result = 2; //not found
pos = 52;
for(i = 0; i < 3; i )
{
len = get_asn1_len(header_buf pos 1, &size_hdr);
if (len == 8 && memcmp(header_buf pos, "x8ax8", 2) == 0)
{
memcpy(public8,header_buf pos 2,8);
result = 0; //ok
break;
}
pos = len size_hdr 1;
if (pos > header_len-8) { result = 2; goto err; }
}
err:
OPENSSL_cleanse(header_buf, sizeof(header_buf));
return result;
}
#define START_OID 0x12
#define START_KEY 0x28
unsigned char asn1_private_key[72] = {
0x30,0x46,2,1,0,0x30,0x1c,6,6,0x2a,0x85,3,2,2,0x13,0x30,0x12,6,7,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,6,7,0x2a,0x85,3,2,2,0x1e,1,4,0x23,2,0x21,0
};
int main(int argc, char **argv)
{
int result;
char *container_path;
char *passw;
char salt12[12];
char primary_key[32];
char masks_key[32];
char public8[8];
char oid_param_set8[8];
BN_CTX *ctx;
BIGNUM *key_with_mask;
BIGNUM *mask;
BIGNUM *raw_key;
char pwd_key[32];
char outbuf[32];
ctx = BN_CTX_new();
if (argc == 2)
{
container_path = argv[1];
passw = "";
}
else
if (argc == 3)
{
container_path = argv[1];
passw = argv[2];
}
else
{
printf("get_private container_path [passw]n");
result = 1;
goto err;
}
if (read_container(container_path, 0, salt12, primary_key, masks_key, public8, oid_param_set8) != 0 &&
read_container(container_path, 1, salt12, primary_key, masks_key, public8, oid_param_set8) != 0)
{
printf("can not read container from %sn", container_path);
result = 2;
goto err;
}
make_pwd_key(pwd_key, salt12, 12, passw);
key_with_mask = decode_primary_key(pwd_key, primary_key, ctx);
OPENSSL_cleanse(pwd_key, sizeof(pwd_key));
mask = reverse32bn(masks_key, ctx);
raw_key = remove_mask_and_check_public(oid_param_set8, key_with_mask, mask, public8, ctx);
if (raw_key)
{
BIO *bio;
store_bignum(raw_key, outbuf, sizeof(outbuf));
memcpy(asn1_private_key START_OID, oid_param_set8, 8);
memcpy(asn1_private_key START_KEY, outbuf, 32);
//bio = BIO_new_file("private.key", "w");
bio = BIO_new_fp(stdout, BIO_NOCLOSE | BIO_FP_TEXT);
PEM_write_bio(bio, "PRIVATE KEY", "", asn1_private_key, sizeof(asn1_private_key));
BIO_free(bio);
OPENSSL_cleanse(outbuf, sizeof(outbuf));
OPENSSL_cleanse(asn1_private_key, sizeof(asn1_private_key));
result = 0; //ok
}
else
{
printf("Error check public keyn");
result = 3;
}
err:
BN_CTX_free(ctx);
OPENSSL_cleanse(salt12, sizeof(salt12));
OPENSSL_cleanse(primary_key, sizeof(primary_key));
OPENSSL_cleanse(masks_key, sizeof(masks_key));
return result;
}
