Две версии README:
Попытка сделать нативную реализацию oAuth через ЕСИА с поддержкой криптографии по ГОСТ. Без готовых Docker сборок с пропатченным OpenSSL или внешних зависимостей от OpenSSL как такового.
- Что умеет
- Вводные
- Структура проекта
- Установка CLI
- Извлечение приватного ключа из контейнера КриптоПро
- Пример клиента ЕСИА
- Установка HTTP API сервера
- Пример клиента ЕСИА (через HTTP API)
- Подпись ГОСТ Р 34.10-2012 (256 бит)
- Хеш ГОСТ Р 34.11-2012 (Стрибог-256)
- Формирование CMS/PKCS#7 SignedData
- Работа с ключами из контейнера КриптоПро
- Для сборки из исходников нужен Go 1.21+
- Контейнер КриптоПро с приватным ключом и сертификатом. В данном случае "контейнер" - это директория (или архив изначально), которая создаётся при экспорте ключа из КриптоПро CSP.
esia-potato/
|--- cms/
| --- cms.go # CMS/PKCS#7 SignedData
|--- cryptopro/
| --- extract.go # Библиотека извлечения ключей
|--- httpapi/
| |--- handlers.go # HTTP хендлеры
| |--- archive.go # Распаковка архивов
| `--- types.go # Типы запросов/ответов
|--- utils/
| --- bytes.go # Вспомогательные функции
|--- cmd/
| |--- cryptopro_extract_service/
| | --- main.go # HTTP API сервер (точка входа)
| |--- cryptopro_extract/
| | --- main.go # CLI для извлечения ключей
| |--- example/
| | --- main.go # Пример клиента ЕСИА (библиотека)
| `--- example_api/
| --- main.go # Пример клиента ЕСИА (HTTP API)
`--- test_container/ # Тестовые ключи. В gitignore, так как ваши будут отличаться
go install github.com/LdDl/esia-potato/cmd/cryptopro_extract@latest
cryptopro_extract -hgit clone git@github.com:LdDl/esia-potato.git --depth 1
cd esia-potato
go run ./cmd/cryptopro_extract -hdocker pull dimahkiin/cryptopro-extract:latest
docker run --rm -v $(pwd)/container:/data dimahkiin/cryptopro-extract -p ПИН_КОД /dataКонтейнер КриптоПро хранит ключи в проприетарном формате с шифрованием ГОСТ 28147.
-
С помощью установленного CLI:
cryptopro_extract -p ПИН_КОД_ПАРОЛЬ ./container.000
-
Или из исходников:
go run ./cmd/cryptopro_extract -p ПИН_КОД_ПАРОЛЬ ./container.000
Если всё ОК, то в консоли будет что-то типа:
{"time":"2025-12-29T20:36:00.591340886+03:00","level":"INFO","msg":"container opened","path":"./test_container","curve_oid":"1.2.643.2.2.36.0"}
{"time":"2025-12-29T20:36:01.065829042+03:00","level":"INFO","msg":"primary key extracted","curve_oid":"1.2.643.2.2.36.0","fingerprint":"0123456789abcdef","private_key":"a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2"}
{"time":"2025-12-29T20:36:01.065854001+03:00","level":"WARN","msg":"secondary key found but not extracted","masks":"masks2.key","primary":"primary2.key"}
{"time":"2025-12-29T20:36:01.065858097+03:00","level":"INFO","msg":"done"}
Если отображается сообщение с предупреждением:
secondary key found but not extracted
, то это нормально — вторичный ключ не нужен для подписи, т.к. для oAuth в ЕСИА используется только первичный ключ.
Теперь у нас есть приватный ключ, который нужно использовать для подписи запросов к ЕСИА.
- Возьмите приватный ключ из вывода предыдущего шага и вставьте его в
cmd/example/main.goвkeyHex. - Запустите пример:
go run ./cmd/example/main.go
Если всё ОК, то в консоли будет что-то типа:
{"time":"2025-12-29T20:47:23.876107574+03:00","level":"INFO","msg":"message prepared","message":"openid2025.12.29 17:47:23 +0000775607_DP0f9439ef-3581-4de5-9b8c-d20135960331"}
{"time":"2025-12-29T20:47:23.878111012+03:00","level":"INFO","msg":"signature created","signature_bytes":2927,"base64_chars":3904}
{"time":"2025-12-29T20:47:23.8781677+03:00","level":"INFO","msg":"authorization URL prepared","url":"https://esia-portal1.test.gosuslugi.ru/aas/oauth2/ac?access_type=offline&client_id=775607_DP&client_secret=гигантский_jwt_токен&redirect_uri=https%3A%2F%2Fya.ru&response_type=code&scope=openid&state=0f9439ef-3581-4de5-9b8c-d20135960331×tamp=2025.12.29+17%3A47%3A23+%2B0000"}
{"time":"2025-12-29T20:47:23.878185114+03:00","level":"INFO","msg":"testing against ESIA"}
{"time":"2025-12-29T20:47:23.95390256+03:00","level":"INFO","msg":"response received","status":"302 ","location":"https://esia-portal1.test.gosuslugi.ru/login"}
{"time":"2025-12-29T20:47:23.953918261+03:00","level":"INFO","msg":"signature accepted by ESIA"}
Редирект на /login означает, что подпись прошла проверку и всё ок.
Для удобства интеграции в ряде случаев есть HTTP API сервер, который позволяет извлекать ключи и подписывать сообщения через REST API.
go install github.com/LdDl/esia-potato/cmd/cryptopro_extract_service@latest
cryptopro_extract_service -host 0.0.0.0 -port 8080go run ./cmd/cryptopro_extract_service/main.go -host 0.0.0.0 -port 8080docker pull dimahkiin/cryptopro-extract-service:latest
docker run -p 8080:8080 dimahkiin/cryptopro-extract-serviceИнтерактивная документация API доступна по адресу:
-
OpenAPI JSON: http://localhost:8080/docs/swagger.json
-
UI (RapiDoc): http://localhost:8080/docs
-
RapiDoc - UI для документации API
Проверка состояния сервера.
Пример:
curl http://localhost:8080/healthОтвет:
{"status":"ok"}Извлечение ключа из контейнера КриптоПро.
Запрос: multipart/form-data
file- архив контейнера (.zipили.tar.gz)pin- пин-код контейнера
Пример:
curl -X POST http://localhost:8080/api/v1/extract \
-F "file=@container.zip" \
-F "pin=12345"Ответ:
{
"private_key_hex": "a1b2c3d4...",
"public_key_hex": "e5f6a7b8...",
"fingerprint": "0123456789abcdef",
"curve_oid": "1.2.643.2.2.36.0",
"certificate_base64": "MIIBkTCB..."
}Поле certificate_base64 содержит сертификат из контейнера (если найден certificate.cer). Его можно использовать для подписи через /api/v1/sign.
Подпись сообщения с использованием приватного ключа.
Запрос: application/json
{
"private_key_hex": "a1b2c3d4...",
"certificate_base64": "MIIBkTCB...",
"message": "текст для подписи"
}Пример:
curl -X POST http://localhost:8080/api/v1/sign \
-H "Content-Type: application/json" \
-d '{
"private_key_hex": "a1b2c3d4...",
"certificate_base64": "MIIBkTCB...",
"message": "openid2025.01.01 12:00:00 +0000CLIENT_ID12345"
}'Ответ:
{
"signature_base64": "MIIBygYJKoZIhvcNAQc..."
}# 1. Извлекаем ключ и сертификат из контейнера
RESP=$(curl -s -X POST http://localhost:8080/api/v1/extract \
-F "file=@container.zip" \
-F "pin=12345")
# 2. Извлекаем нужные поля из ответа
KEY=$(echo $RESP | jq -r .private_key_hex)
CERT=$(echo $RESP | jq -r .certificate_base64)
# 3. Подписываем сообщение
curl -X POST http://localhost:8080/api/v1/sign \
-H "Content-Type: application/json" \
-d "{
\"private_key_hex\": \"$KEY\",
\"certificate_base64\": \"$CERT\",
\"message\": \"openid2025.01.01 12:00:00 +0000CLIENT_ID12345\"
}"Если нет возможности использовать этот проект как библиотеку, то можно воспользоваться его HTTP API версией.
-
Запустите HTTP API сервер (см. выше)
-
Затем:
go run ./cmd/example_api/main.go
Этот пример:
- Отправляет контейнер на
/api/v1/extractдля извлечения ключа - Отправляет сообщение на
/api/v1/signдля подписи - Использует полученную подпись для авторизации в ЕСИА и формирует URL для редиректа