Читаем контейнер закрытого ключа КриптоПро средствами OpenSSL / Хабр

Читаем контейнер закрытого ключа КриптоПро средствами OpenSSL / Хабр ЭЦП
Содержание
  1. /certificates — все установленные сертификаты пользователя
  2. /cosign — добавление еще одной подписи к документу
  3. /sign — подписание документов
  4. /unsign — получение исходного файла без подписей
  5. /verify — проверка подписанного документа
  6. Аналоги
  7. Без скачивания на диск
  8. Возможные проблемы
  9. Где в реестре хранится эцп
  10. Где хранится сертификат эцп в ос windows xp
  11. Где хранится эцп в системах linux
  12. Естп — электронная торговая площадка
  13. Запуск контейнера
  14. Использование контейнера на удаленной машине
  15. Как посмотреть сертификат эцп
  16. Компиляция openssl библиотеки
  17. Компиляция privkey.c
  18. Криптопро 4.0 в докер контейнере
  19. Лицензия
  20. Обработка ошибок
  21. Подписание документа
  22. Получение исходного файла из sig-файла
  23. Пользуемся закрытым ключом private.key для подписывания файла file.txt
  24. Проблемы
  25. Проверка подписи
  26. Проверяем подпись
  27. Просмотр установленных сертификатов
  28. Сборка утилиты конвертирования ключа
  29. Установка корневых сертификатов
  30. Установка сертификатов пользователя для проверки и подписания
  31. Файл header.key
  32. Файл masks.key
  33. Файл primary.key
  34. Формат данных
  35. Формирование файла закрытого ключа private.key
  36. Через internet explorer
  37. Через консоль управления
  38. Через криптопро
  39. Через скачивание на диск
  40. Через certmgr
  41. Читаем закрытый ключ и конвертируем

/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 и выше и позволяет также найти сертификаты ЭЦП на компьютере. Для просмотра нужно:

  1. запустить браузер;
  2. через «‎Меню» перейти в «‎Свойства браузера»;
  3. в новом окне выбрать вкладку «‎Содержание»;
  4. выбрать «‎Сертификаты».

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

Через консоль управления

Просмотр сертификатов в ОС Windows через консоль управления запускается в несколько этапов:

  1. пользователь открывает командную строку;
  2. вводит команду mmc и нажимает Enter;
  3. нажимает на «‎Файл» и «‎Добавить/удалить оснастку cryptopro»;
  4. выбирает последовательно «‎Добавить» и «Добавить изолированную оснастку»;
  5. выбирает «Сертификаты».

Дополнительно можно просмотреть ключи ЭЦП по конкретной учетной записи. Способ также доступен только пользователям с правами администратора ПК. Через консоль можно не только просматривать информация, но и удалять, копировать, добавлять контейнеры с ключами ЭЦП.

Через криптопро

Как найти сертификат ЭЦП на компьютере при помощи КриптоПро:

  1. открыть «‎Пуск»;
  2. через вкладку «‎Все программы» перейти в «‎КриптоПро»;
  3. выбрать вкладку «‎Сертификаты».

Этим способом могут воспользоваться пользователи или администраторы ПК (если пользователю будет отказано в доступе, необходимо дополнительно запросить права администратора). В открывшемся окне будет список всех сертификатов, установленных на данном ПК.

Через скачивание на диск

Скачаем сертификат на диск с помощью curl и передадим полученный файл на stdin с запуском команды его установки:

Через certmgr

Найти файл сертификата можно и при помощи встроенного менеджера, который присутствует во всех ОС Windows. Через него можно не только посмотреть все личные сертификаты, но и сертификаты УЦ и партнеров Microsoft.

Метод может использоваться только администратором ПК. Для просмотра нужно:

  1. открыть «‎Пуск»;
  2. ввести команду certmgr.msc в строку поиска и нажать Enter;
  3. в левой колонке открывшегося окна будет список личных и корневых сертификатов.

С шифрованными сертификатами приложение работает ограниченно и не всегда корректно отражает все электронные подписи, установленные пользователем.

Читаем закрытый ключ и конвертируем

Файл privkey.c
#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;
}

Оцените статью
ЭЦП64
Добавить комментарий