GitHub — devind-team/CryptoPro-pycades: CryptoPro with Python extension — pycades

/certificate/private_key — установка приватного колюча

Для установки приватного колюча нужно передать архив в сервис.
В каталоге certificates/ содержатся различные комбинации тестового сертификата и закрытого ключа, с PIN кодом и без:

С пин-кодом:

Без пин-кодом:

/certificate/root — установка корневых сертификатов

Для установки коневых сертификатов нужно передать файл (с расширением cer или p7b) в сервис.

/certificates — все установленные сертификаты пользователя

Если сертификатов нет:

Если сертификаты есть:

{
  "data_certificates": {
    "certificate_1": {
      "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 256 бит",
        "val": "1.2.643.7.1.1.1.1"
      },
      "valid": {
        "from": "23.08.2021 12:07:25",
        "to": "23.08.2022 12:17:25"
      },
      "issuer": {
        "CN": "Test",
        "O": "Test",
        "OU": "Test",
        "STREET": "Test",
        "L": "Москва",
        "C": "RU",
        "raw": "CN=Test, O=Test, OU=Test, STREET=Test, L=Москва, S=77 Москва, C=RU, INN=Test, OGRN=Test"
      },
      "subject": {
        "E": "Test@Test.ru",
        "C": "RU",
        "L": "г Москва",
        "O": "Test",
        "CN": "Test",
        "STREET": "Test",
        "G": "Test",
        "SN": "Test ",
        "raw": "SNILS=Test, OGRN=Test, INN=Test, E=Test@Test.ru, C=RU, S=77 г. Москва, L=г Москва, O=Test, CN=Test, STREET=Test, T=Test, G=Test, SN=Test"
      },
      "thumbprint": "982AA9E713A2F99B10DAA07DCDC94A4BC32A1027",
      "serialNumber": "120032C3567443029CC358FCDF00000032C356",
      "hasPrivateKey": true
    }
  }
}

/license?serial_number= — установка серийной лицензии

Для установки серийного номера лицензии нужно передать номер.

/signer — подписание документов

Для подписания нужно передать файл в сервис.

С пин-кодом:

Без пин-кода:

Вернется JSON — документ, в signedContent будет содержаться подписанный файл и в filename новое имя файла.

/unsigner — получение исходного файла без подписей

Исходный файл вернется в поле content.

/verify — проверка подписанного документа

Для проверки подписи передаем подписанный и не подписанный файлы.

Если файл прошел проверку, вернется список подписантов signers.

Pycryptoprosdk,keyintegrity

Есть ли возможность получить сам файл публичного сертификата, не в виде объекта, а в виде file-like object или конкретно в виде файла. Из примеров вижу, что сертификат можно положить в хранилище, или удалить, а просто получить публичную часть возможности нет

Подскажите, каким образом привязать private.key к самоподписанному x509 сертификату, чтобы Cades смог подписывать контент?

from OpenSSL import crypto
from pycryptoprosdk import CryptoProSDK

CERT_FILE = "selfsigned.crt"
PRIVATE_KEY_FILE = "private.key"
PUBLIC_KEY_FILE = "public.key"
COMMON_NAME = 'Фамилия Имя Отчество'

def raw_cert(bytes_content):
    return ('n'.join(bytes_content.decode().split('n')[1:-2]) 'n').encode()

def create_self_signed_cert(common_name=COMMON_NAME, country='RU', state = 'Test state', city='Test city', organization='test organization', organizational_unit = 'test organizational unit'):
        # create a key pair
        k = crypto.PKey()
        k.generate_key(crypto.TYPE_RSA, 1024)
        # create a self-signed cert
        cert = crypto.X509()
        cert.get_subject().C = country
        cert.get_subject().ST = state
        cert.get_subject().L = city
        cert.get_subject().O = organization
        cert.get_subject().OU = organizational_unit
        cert.get_subject().CN = common_name
        cert.set_serial_number(1000)
        cert.gmtime_adj_notBefore(0)
        cert.gmtime_adj_notAfter(10*365*24*60*60)#10 years
        cert.set_issuer(cert.get_subject())
        cert.set_pubkey(k)
        cert.sign(k, 'sha1')

        open(CERT_FILE, "wb ").write(
            crypto.dump_certificate(crypto.FILETYPE_PEM, cert))
        open(PRIVATE_KEY_FILE, "wb ").write(
            crypto.dump_privatekey(crypto.FILETYPE_PEM, k))
        open(PUBLIC_KEY_FILE, "wb ").write(
            crypto.dump_publickey(crypto.FILETYPE_PEM, k))

create_self_signed_cert() #creates selfsigned.crt, public.key, private.key
sdk = CryptoProSDK()
sdk.install_certificate('MY', raw_cert(open(CERT_FILE, 'rb').read()))
content = "test content"
cert = sdk.get_cert_by_subject('MY',COMMON_NAME)
signature = sdk.sign(content, cert.thumbprint, 'MY', detached=True)

"""
Exception has occurred: ValueError       (note: full exception trace is shown but execution is paused at: _run_module_as_main)
CadesSignMessage failed (error 0x8009200b).
"""

Hello I try to use on with cryptopro 5
and get error
In file included from pycryptoprosdk/libpycades.cpp:8:
pycryptoprosdk/libpycades.cpp: In function ‘char* GetHashOidByKeyOid(char*)’:
/opt/cprocsp/include/cpcsp/WinCryptEx.h:1164:29: warning: ISO C forbids converting a string constant to ‘char*’ [-Wwrite-strings]
1164 | #define szOID_CP_GOST_R3411 «1.2.643.2.2.9»
| ^~~~~~~~~~~~~~~
pycryptoprosdk/libpycades.cpp:35:13: note: in expansion of macro ‘szOID_CP_GOST_R3411’
35 | return szOID_CP_GOST_R3411;
| ^~~~~~~~~~~~~~~~~~~
/opt/cprocsp/include/cpcsp/WinCryptEx.h:1165:36: warning: ISO C forbids converting a string constant to ‘char*’ [-Wwrite-strings]
1165 | #define szOID_CP_GOST_R3411_12_256 «1.2.643.7.1.1.2.2»
| ^~~~~~~~~~~~~~~~~~~
pycryptoprosdk/libpycades.cpp:38:13: note: in expansion of macro ‘szOID_CP_GOST_R3411_12_256’
38 | return szOID_CP_GOST_R3411_12_256;
| ^~~~~~~~~~~~~~~~~~~~~~~~~~
/opt/cprocsp/include/cpcsp/WinCryptEx.h:1166:36: warning: ISO C forbids converting a string constant to ‘char*’ [-Wwrite-strings]
1166 | #define szOID_CP_GOST_R3411_12_512 «1.2.643.7.1.1.2.3»
| ^~~~~~~~~~~~~~~~~~~
pycryptoprosdk/libpycades.cpp:41:13: note: in expansion of macro ‘szOID_CP_GOST_R3411_12_512’
41 | return szOID_CP_GOST_R3411_12_512;
| ^~~~~~~~~~~~~~~~~~~~~~~~~~
pycryptoprosdk/libpycades.cpp: At global scope:
pycryptoprosdk/libpycades.cpp:655:27: error: variable ‘PyModuleDef libpycades’ has initializer but incomplete type
655 | static struct PyModuleDef libpycades = {
| ^~~~~~~~~~
pycryptoprosdk/libpycades.cpp:656:5: error: ‘PyModuleDef_HEAD_INIT’ was not declared in this scope
656 | PyModuleDef_HEAD_INIT,
| ^~~~~~~~~~~~~~~~~~~~~
pycryptoprosdk/libpycades.cpp: In function ‘void PyInit_libpycades()’:
pycryptoprosdk/libpycades.cpp:666:9: error: ‘PyModule_Create’ was not declared in this scope; did you mean ‘PyModule_Check’?
666 | m = PyModule_Create(&libpycades);
| ^~~~~~~~~~~~~~~
| PyModule_Check
pycryptoprosdk/libpycades.cpp:668:43: warning: ISO C forbids converting a string constant to ‘char*’ [-Wwrite-strings]
668 | CertDoesNotExist = PyErr_NewException(«libpycades.CertDoesNotExist», NULL, NULL);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
pycryptoprosdk/libpycades.cpp:672:12: error: return-statement with a value, in function returning ‘void’ [-fpermissive]
672 | return m;
| ^
error: command ‘x86_64-linux-gnu-gcc’ failed with exit status 1
how to fix this?

В README.md есть кусок примера кода with open('certificate.cer'), 'rb') as f:. Тут лишняя закрывающая скобка.
И в Dockerfile используется /opt/cprocsp/bin/amd64/certmgr -inst -store uca -f /certs/kazna.cer, хотя данны сертификат отсутвует в certs/. Где его можно получить?

А есть ли возможность как то подписывать с использованием сертификатов из хранилищей, на которые установлены пароли? Посмотрел исходники, но не увидел никакой возможности передачи пароля в качестве аргумента

/opt/cprocsp/bin/amd64/certmgr -inst -store uroot -f certs/cert_cryptopro_test.cer 
Certmgr 1.0 (c) "CryptoPro",  2007-2022.
program for managing certificates, CRLs and stores

Install:
=============================================================================
1-------
Issuer              : [email protected], C=RU, L=Moscow, O=CRYPTO-PRO LLC, CN=CRYPTO-PRO Test Center 2
Subject             : [email protected], C=RU, L=Moscow, O=CRYPTO-PRO LLC, CN=CRYPTO-PRO Test Center 2
Serial              : 0x2B6E3351FD6EB2AD48200203CB5BA141
SHA1 Hash           : 0x046255290b0eb1cdd1797d9ab8c81f699e3687f3
SubjKeyID           : 15317cb08d1ade66d7159c4952971724b9017a83
Signature Algorithm : ГОСТ Р 34.11/34.10-2001
PublicKey Algorithm : ГОСТ Р 34.10-2001 (512 bits)
Not valid before    : 05/08/2022  13:44:24 UTC
Not valid after     : 05/08/2022  13:54:03 UTC
PrivateKey Link     : No                  
=============================================================================

[ErrorCode: 0x00000000]

Аналоги

Существует аналогичный пакет:

Без скачивания на диск

Примечание: по какой-то причине иногда «заедает», но при повторном запуске — срабатывает.

Возможные проблемы

В Dockerfile содержатся названия пакетов, например lsb-cprocsp-devel_5.0.12000-6_all.deb, которые могут заменить новой версией. Следует поправить названия пакетов в Dockerfile.

Запуск контейнера

Запустим контейнер под именем cryptopro, к которому будем обращаться в примерах:

docker run -it --rm -p 8095:80 --name cryptopro cryptopro_5

Использование контейнера на удаленной машине

В примерах выше команды выглядят так: cat … | docker … или curl … | docker …, то есть контейнер запущен на локальной машине. Если же докер контейнер запущен на удаленной машине, то команды нужно отправлять через ssh клиент. Например, команда подписания:

Опция -q отключает приветствие из файла /etc/banner (хотя оно все равно пишется в stderr). А /etc/motd при выполнении команды по ssh не выводится.

В качестве эксперимента можно отправить по ssh на свою же машину так:

Криптопро 5.0 в докер контейнере c расширением pycades

Инструкция по установке и сборке расширения для языка Python

Содержимое контейнера:

Лицензия

Установка серийного номера:

Просмотр:

Обработка ошибок

Успешные действия возвращают код 200 и «status»: «ok».

Действия с ошибками возвращают 4xx и 5xx коды и «status»: «fail», в полях errMsg содержится описание ошибки, в errCode — ее код.

Например, обращение с неправильным методом

выведет такую ошибку:

Подписание документа

Для примера установим этот тестовый сертификат:

Его SHA1 Hash равен dd45247ab9db600dca42cc36c1141262fa60e3fe (узнать: certmgr -list), который будем использовать как указатель нужного сертификата.

Теперь передадим на stdin файл, в качестве команды — последовательность действий, и на stdout получим подписанный файл:

Получилось довольно неудобно. Скрипт scripts/sign делает то же самое, теперь команда подписания будет выглядеть так:

Об ошибке можно узнать через стандартный $?.

Получение исходного файла из sig-файла

Возьмем файл из примера выше:

То же самое, но с использованием скрипта:

Примеры использования

>>>frompycryptoprosdkimportCryptoProSDK>>>sdk=CryptoProSDK()


# Создание и проверка отсоединенной подписи:>>>content='test content'>>>cert=sdk.get_cert_by_subject('MY', 'Ivan')
>>>signature=sdk.sign(content, cert.thumbprint, 'MY', detached=True)
>>>result=sdk.verify_detached(content, signature)

# статус проверки:>>>result.verification_status0# 0: Успешная проверка подписи.# 1: Отсутствуют или имеют неправильный формат атрибуты со ссылками и значениями доказательств подлинности.# 2: Сертификат, на ключе которого было подписано сообщение, не найден.# 3: В сообщении не найден действительный штамп времени на подпись.# 4: Значения ссылок на доказательства подлинности и сами доказательства, вложенные в сообщение, не соответствуют друг другу.# 5: Не удалось построить цепочку для сертификата, на ключе которого подписано сообщение.# 6: Ошибка проверки конечного сертификата на отзыв.# 7: Ошибка проверки сертификата цепочки на отзыв.# 8: Сообщение содержит неверную подпись.# 9: В сообщении не найден действительный штамп времени на доказательства подлинности подписи.# 10: Значение подписанного атрибута content-type не совпадает со значением, указанным в поле encapContentInfo.eContentType.# сертификат подписанта:>>>result.cert.as_dict()
{'CN': 'Ivan'}


# создание хэша файла алгоритмом ГОСТ Р 34.11-94:>>>sdk.create_hash('some text', alg='CALG_GR3411')
'046255290b0eb1cdd1797d9ab8c81f699e3687f3'# поиск сертификата в хранилище MY по отпечатку:>>>cert=sdk.get_cert_by_thumbprint('MY', '046255290b0eb1cdd1797d9ab8c81f699e3687f3')


# поиск сертификата по имени:>>>cert=sdk.get_cert_by_subject('MY', 'CRYPTO-PRO Test Center 2')


# установка сертификата в хранилище MY:>>>withopen('certificate.cer', 'rb') asf:
>>>cert_content=f.read()
>>>sdk.install_certificate('MY', cert_content)


# удаление сертификата из хранилища MY по отпечатку:>>>sdk.delete_certificate('MY', '9e78a331020e528c046ffd57704a21b7d2241cb3')


# извлечение сертификата подписанта из подписи:>>>withopen('signature.sig', 'rb') asf:
>>>signature_content=f.read()
>>>cert=sdk.get_signer_cert_from_signature(signature_content)

Проверка подписи

Подпишем файл из примера выше и сохраним его на диск:

Тогда проверка подписанного файла будет выглядеть так:

То же самое, но с использованием скрипта:

Просмотр установленных сертификатов

Сертификаты пользователя:

Корневые сертификаты:

Системные требования

  • Python >= 3.5
  • КриптоПро CSP >= 4

Установка

  • Установить КриптоПро CSP.
  • Установить пакеты lsb-cprocsp-devel-.noarch.rpm и cprocsp-pki-amd64-cades.rpm из состава КриптоПро ЭЦП SDK.
  • При необходимости, создать симлинк:
ln -s /opt/cprocsp/lib/amd64/libcades.so.2.0.0 /opt/cprocsp/lib/amd64/libcades.so

Пример установки пакетов можно посмотреть в pycryptoprosdk/compose/Dockerfile.

  • Установить pycryptoprosdk:
pip install pycryptoprosdk

Установка корневых сертификатов

Для установки корневых сертификатов нужно на stdin скрипта /scripts/root передать файл с сертификатами. Если в файле несколько сертификатов, все они будут установлены.

Установка сертификатов пользователя для проверки и подписания

Необходимо специальным образом сформировать zip-архив bundle.zip и отправить его на stdin скрипта /scripts/my. Пример такого zip-файла:

Как получить сертификат КриптоПро.

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

В каталоге certificates/ содержатся различные комбинации тестового сертификата и закрытого ключа, с PIN кодом и без:

Примеры:

Формат данных

Возвращаются данные в формате JSON.

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

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

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