Инструкция по установке и сборке расширения для языка Python
Содержимое контейнера:
- python3.9.10 с установленным расширением
pycades
(CPStore
,CPSigner
,CPSignedData
) - инструменты КриптоПро:
certmgr
,cpverify
,cryptcp
,csptest
,csptestf
,der2xer
,inittst
,wipefile
,cpconfig
- вспомогательные скрипты командой строки
- HTTP REST-сервер
Есть 3 варианта использования контейнера:
- через интерфейс командной строки (и ssh-клиент для удаленных машин)
- через HTTP REST-сервер
- добавить свои обработчики внутрь контейнера
├── assets - материалы для README.md
├── devel - devel скрипты
├── certificates - тестовые сертификаты
├── dist - пакеты КриптоПро (необходимо скачать с официального сайта)
├── Dockerfile - файл сборки образа
├── README.md - этот файл
└── scripts - вспомогательные скрипты командой строки
└── www - HTTP REST-сервер
Скачать с официального сайта в dist/
(необходимо быть залогиненым в системе):
- КриптоПро CSP 5.0 для Linux (x64, deb) =>
dist/linux-amd64_deb.tgz
- КриптоПро ЭЦП SDK версия 2.0 для пользователей =>
dist/cades-linux-amd64.tar.gz
- Linux версию - Архив с исходниками =>
dist/pycades.zip
Запустить:
docker build --tag armrus/cryptopro-pycades:latest .
В Dockerfile
содержатся названия пакетов, например lsb-cprocsp-devel_5.0.12000-6_all.deb
, которые могут заменить новой версией. Следует поправить названия пакетов в Dockerfile
.
Запустим контейнер под именем cryptopro
, к которому будем обращаться в примерах:
docker run -it --rm -p 8095:80 --name cryptopro cryptopro_5
Установка серийного номера:
docker exec -i cryptopro cpconfig -license -set <серийный_номер>
Просмотр:
docker exec -i cryptopro cpconfig -license -view
Для установки корневых сертификатов нужно на stdin
скрипта /scripts/root
передать файл с сертификатами. Если в файле несколько сертификатов, все они будут установлены.
Скачаем сертификат на диск с помощью curl
и передадим полученный файл на stdin
с запуском команды его установки:
curl -sS http://cpca.cryptopro.ru/cacer.p7b > certificates/cacer.p7b
cat certificates/cacer.p7b | docker exec -i cryptopro /scripts/root
# сертификаты УЦ
curl -sS http://cpca.cryptopro.ru/cacer.p7b | docker exec -i cryptopro /scripts/root
# сертификаты тестового УЦ
curl -sS http://testca2012.cryptopro.ru/cert/rootca.cer | docker exec -i cryptopro /scripts/root
curl -sS http://testca2012.cryptopro.ru/cert/subca.cer | docker exec -i cryptopro /scripts/root
Примечание: по какой-то причине иногда "заедает", но при повторном запуске - срабатывает.
Необходимо специальным образом сформировать zip-архив bundle.zip
и отправить его на stdin
скрипта /scripts/my
. Пример такого zip-файла:
├── certificate.cer - файл сертификата (не обязательно)
└── le-09650.000 - каталог с файлами закрытого ключа (не обязательно)
├── header.key
├── masks2.key
├── masks.key
├── name.key
├── primary2.key
└── primary.key
Как получить сертификат КриптоПро.
Первый найденный файл в корне архива будет воспринят как сертификат, а первый найденный каталог - как связка файлов закрытого ключа. Пароль от контейнера, если есть, передается первым параметром командной строки.
В каталоге certificates/
содержатся различные комбинации тестового сертификата и закрытого ключа, с PIN кодом и без:
├── bundle-cert-only.zip - только сертификат
├── bundle-cosign.zip - сертификат + закрытый ключ БЕЗ пин-кода (для добавления второй подписи)
├── bundle-cyrillic.zip - сертификат + закрытый ключ, название контейнера "тестовое название контейнера" (кириллица)
├── bundle-no-pin.zip - сертификат + закрытый ключ БЕЗ пин-кода
├── bundle-pin.zip - сертификат + закрытый ключ с пин-кодом 12345678
└── bundle-private-key-only.zip - только закрытый ключ
Примеры:
# сертификат + закрытый ключ с пин-кодом
cat certificates/bundle-pin.zip | docker exec -i cryptopro /scripts/my 12345678
# сертификат + закрытый ключ БЕЗ пин-кода
cat certificates/bundle-no-pin.zip | docker exec -i cryptopro /scripts/my
# только сертификат
cat certificates/bundle-cert-only.zip | docker exec -i cryptopro /scripts/my
# только закрытый ключ
cat certificates/bundle-private-key-only.zip | docker exec -i cryptopro /scripts/my
# сертификат + закрытый ключ, название контейнера "тестовое название контейнера" (кириллица)
cat certificates/bundle-cyrillic.zip | docker exec -i cryptopro /scripts/my
Сертификаты пользователя:
docker exec -i cryptopro certmgr -list
Корневые сертификаты:
docker exec -i cryptopro certmgr -list -store root
Для примера установим этот тестовый сертификат:
# сертификат + закрытый ключ с пин-кодом
cat certificates/bundle-pin.zip | docker exec -i cryptopro /scripts/my 12345678
Его SHA1 Hash равен dd45247ab9db600dca42cc36c1141262fa60e3fe
(узнать: certmgr -list
), который будем использовать как указатель нужного сертификата.
Теперь передадим на stdin
файл, в качестве команды - последовательность действий, и на stdout
получим подписанный файл:
cat README.md | docker exec -i cryptopro sh -c 'tmp=`mktemp`; cat - > "$tmp"; cryptcp -sign -thumbprint dd45247ab9db600dca42cc36c1141262fa60e3fe -nochain -pin 12345678 "$tmp" "$tmp.sig" > /dev/null 2>&1; cat "$tmp.sig"; rm -f "$tmp" "$tmp.sig"'
Получилось довольно неудобно. Скрипт scripts/sign
делает то же самое, теперь команда подписания будет выглядеть так:
cat README.md | docker exec -i cryptopro /scripts/sign dd45247ab9db600dca42cc36c1141262fa60e3fe 12345678
Об ошибке можно узнать через стандартный $?
.
Подпишем файл из примера выше и сохраним его на диск:
cat README.md | docker exec -i cryptopro /scripts/sign dd45247ab9db600dca42cc36c1141262fa60e3fe 12345678 > certificates/README.md.sig
Тогда проверка подписанного файла будет выглядеть так:
cat certificates/README.md.sig | docker exec -i cryptopro sh -c 'tmp=`mktemp`; cat - > "$tmp"; cryptcp -verify -norev -f "$tmp" "$tmp"; rm -f "$tmp"'
То же самое, но с использованием скрипта:
cat certificates/README.md.sig | docker exec -i cryptopro scripts/verify
Возьмем файл из примера выше:
cat certificates/README.md.sig | docker exec -i cryptopro sh -c 'tmp=`mktemp`; cat - > "$tmp"; cryptcp -verify -nochain "$tmp" "$tmp.origin" > /dev/null 2>&1; cat "$tmp.origin"; rm -f "$tmp" "$tmp.origin"'
То же самое, но с использованием скрипта:
cat certificates/README.md.sig | docker exec -i cryptopro scripts/unsign
В примерах выше команды выглядят так: cat ... | docker ...
или curl ... | docker ...
, то есть контейнер запущен на локальной машине. Если же докер контейнер запущен на удаленной машине, то команды нужно отправлять через ssh клиент. Например, команда подписания:
cat README.md | ssh -q user@host 'docker exec -i cryptopro /scripts/sign dd45247ab9db600dca42cc36c1141262fa60e3fe 12345678'
Опция -q
отключает приветствие из файла /etc/banner
(хотя оно все равно пишется в stderr
). А /etc/motd
при выполнении команды по ssh не выводится.
В качестве эксперимента можно отправить по ssh на свою же машину так:
# копируем публичный ключ на "удаленную машину" (на самом деле - localhost)
ssh-copy-id $(whoami)@localhost
# пробуем подписать
cat README.md | ssh -q $(whoami)@localhost 'docker exec -i cryptopro /scripts/sign dd45247ab9db600dca42cc36c1141262fa60e3fe 12345678'
Установка сертификатов осуществляется через командую строку. Все остальные действия доступны по HTTP.
/certificates
- все установленные сертификаты пользователя (GET
)/certificate/root
- установка корневых сертификатов (POST
)/certificate/private_key
- установка приватного колюча (POST
)/license?serial_number=
- установка серийной лицензии (POST
)/signer
- подписание документов (POST
)/verify
- проверка подписанного документа (POST
)/unsigner
- получение исходного файла без подписей (POST
)
Возвращаются данные в формате JSON
.
Успешные действия возвращают код 200
и "status": "ok"
.
Действия с ошибками возвращают 4xx
и 5xx
коды и "status": "fail"
, в полях errMsg
содержится описание ошибки, в errCode
- ее код.
Например, обращение с неправильным методом
curl -sS -X POST --data-binary "bindata" http://localhost:8095/healthchecks
выведет такую ошибку:
{"status":"fail","errMsg":"Method must be one of: GET","errCode":405}
curl -X 'GET' \
'http://localhost:8095/certificate' \
-H 'accept: application/json'
Если сертификатов нет:
{"status":"fail","errMsg":"No certificates in store 'My'","errCode":404}
Если сертификаты есть:
{
"data_certificates": {
"certificate_1": {
"privateKey": {
"providerName": "Crypto-Pro GOST R 34.10-2012 KC1 CSP",
"uniqueContainerName": "HDIMAGE\\\\eb5f6857.000\\D160",
"containerName": "eb5f6857-a08a-4510-8a96-df2f75b6d65a"
},
"algorithm": {
"name": "ГОСТ Р 34.10-2012 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
}
}
}
Для установки коневых сертификатов нужно передать файл (с расширением cer или p7b) в сервис.
curl -X 'POST' \
'http://localhost:8095/certificate/root' \
-H 'accept: application/json' \
-H 'Content-Type: multipart/form-data' \
-F 'file=@filename.p7b;type=application/x-pkcs7-certificates'
Для установки приватного колюча нужно передать архив в сервис.
В каталоге certificates/
содержатся различные комбинации тестового сертификата и закрытого ключа, с PIN кодом и без:
├── bundle-cert-only.zip - только сертификат
├── bundle-cosign.zip - сертификат + закрытый ключ БЕЗ пин-кода (для добавления второй подписи)
├── bundle-cyrillic.zip - сертификат + закрытый ключ, название контейнера "тестовое название контейнера" (кириллица)
├── bundle-no-pin.zip - сертификат + закрытый ключ БЕЗ пин-кода
├── bundle-pin.zip - сертификат + закрытый ключ с пин-кодом 12345678
└── bundle-private-key-only.zip - только закрытый ключ
С пин-кодом:
curl -X 'POST' \
'http://localhost:8095/certificate/private_key?pin=1234' \
-H 'accept: application/json' \
-H 'Content-Type: multipart/form-data' \
-F 'file=@bundle-pin.zip;type=application/zip'
Без пин-кодом:
curl -X 'POST' \
'http://localhost:8095/certificate/private_key' \
-H 'accept: application/json' \
-H 'Content-Type: multipart/form-data' \
-F 'file=@bundle-no-pin.zip;type=application/zip'
Для установки серийного номера лицензии нужно передать номер.
curl -X 'POST' \
'http://localhost:8095/license?serial_number=12345-12345-12345-12345-12345' \
-H 'accept: application/json' \
-d ''
/signer
- подписание документов, прикрепленная подпись (возращает, новый подписанный файл с документом)
Для подписания нужно передать файл в сервис.
С пин-кодом:
curl -X 'POST' \
'http://localhost:8095/signer?pin=123' \
-H 'accept: application/json' \
-H 'Content-Type: multipart/form-data' \
-F 'file=@filename.pdf;type=application/pdf'
Без пин-кода:
curl -X 'POST' \
'http://localhost:8095/signer' \
-H 'accept: application/json' \
-H 'Content-Type: multipart/form-data' \
-F 'file=@filename.pdf;type=application/pdf'
Вернется JSON
- документ, в signedContent
будет содержаться подписанный файл и в filename
новое имя файла.
Для подписания нужно передать файл в сервис.
С пин-кодом:
curl -X 'POST' \
'http://localhost:8095/signer?pin=123' \
-H 'accept: application/json' \
-H 'Content-Type: multipart/form-data' \
-F 'file=@filename.pdf;type=application/pdf'
Без пин-кода:
curl -X 'POST' \
'http://localhost:8095/signer' \
-H 'accept: application/json' \
-H 'Content-Type: multipart/form-data' \
-F 'file=@filename.pdf;type=application/pdf'
Вернется JSON
- документ, в signedContent
будет содержаться подписанный файл и в filename
новое имя файла.
Для проверки подписи передаем подписанный и не подписанный файлы.
curl -X 'POST' \
'http://localhost:8095/verify' \
-H 'accept: application/json' \
-H 'Content-Type: multipart/form-data' \
-F 'original_file=@filename1.pdf;type=application/pdf' \
-F 'signed_file=@filename2.pdf;type=application/pdf'
Если файл прошел проверку, вернется список подписантов signers
.
Исходный файл вернется в поле content
.
curl -X 'POST' \
'http://localhost:8095/unsigner' \
-H 'accept: application/json' \
-H 'Content-Type: multipart/form-data' \
-F 'file=@filename.sig;type=application/sig'
- Исходный репозиторий
- Страница расширения для Python
- Тестовый УЦ, его сертификаты: корневой, промежуточный
Существует аналогичный пакет:
- PHP CryptoPro Service (docker) with HTTP API, идеальный вариант с PHP расширением;
- cryptopro, хороший вариант с PHP расширением и HTTP REST-сервером;
- CryptoProCSP, он классный, но:
- давно не обновлялся, используется версия
PHP5.6
- для запуска пришлось подредактировать
Dockerfile
- давно не обновлялся, используется версия