HTTP API for extracting keys from CryptoPro containers and GOST R 34.10-2012 CMS signing
553
HTTP API that extracts a GOST private key, public key and certificate from a CryptoPro CSP
key container, and signs arbitrary messages as CMS/PKCS#7 SignedData using GOST R 34.10-2012
(256-bit) with Streebog-256 hashing. Implemented in pure Go - no CryptoPro CSP installation
and no patched OpenSSL required. Intended for ESIA (Russian government SSO) OAuth clients
that must sign the client_secret string.
Source code: https://github.com/LdDl/esia-potato
No config file and no application environment variables - the server is configured by two command-line flags appended after the image name:
| Flag | Default | Meaning |
|---|---|---|
-host | 0.0.0.0 | Listen address |
-port | 8080 | Listen port |
The default 0.0.0.0 is already correct for a container; if you pass -host 127.0.0.1,
the published port will not be reachable from the host.
Minimal working example:
docker run -d --name cryptopro-extract-service -p 8080:8080 --tmpfs /tmp \
dimahkiin/cryptopro-extract-service:latest
--tmpfs /tmp is required. The image is built FROM scratch, so it has no /tmp
directory; POST /api/v1/extract unpacks the uploaded archive into a temp directory and
fails with 500 {"error":"failed to create temp dir"} without a writable temp location.
Alternatively point the temp directory at the (writable) working directory with
-e TMPDIR=/app.
Logs are structured JSON on stdout.
docker run -d --name cryptopro-extract-service \
-p 8080:8080 \
--tmpfs /tmp \
--read-only \
dimahkiin/cryptopro-extract-service:latest
The service keeps no state and writes nothing outside the temp directory, so --read-only
works as long as --tmpfs /tmp is present. No volume is needed: containers are uploaded
over HTTP, not mounted.
Health check:
curl http://localhost:8080/health
# {"status":"ok"}
Extract a key. container.zip must contain the CryptoPro container directory with
header.key, masks.key and primary.key; certificate.cer, if present, is returned as
base64. Accepted archive formats are .zip, .tar.gz and .tgz; the upload limit is 10 MB.
curl -X POST http://localhost:8080/api/v1/extract \
-F "[email protected]" \
-F "pin=12345"
{
"private_key_hex": "a1b2c3d4...",
"public_key_hex": "e5f6a7b8...",
"fingerprint": "0123456789abcdef",
"curve_oid": "1.2.643.2.2.35.1",
"certificate_base64": "MIIBkTCB..."
}
Sign a message with the values returned above:
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..."}
Run on a different port inside the container (the flag must match the published port):
docker run -d -p 9000:9000 --tmpfs /tmp \
dimahkiin/cryptopro-extract-service:latest -port 9000
| Port | Protocol | Purpose |
|---|---|---|
| 8080 | HTTP/TCP | REST API, OpenAPI docs and health check (EXPOSE 8080; overridable with -port) |
| Path | Purpose | Notes |
|---|---|---|
/app | Working directory | Contains the single static binary |
/app/cryptopro_extract_service | Entrypoint binary | - |
/tmp | Temp unpacking of uploaded archives | Does not exist in the image - mount --tmpfs /tmp or set TMPDIR |
| Method | Path | Request | Response |
|---|---|---|---|
| GET | /health | - | {"status":"ok"} |
| POST | /api/v1/extract | multipart/form-data: file (.zip/.tar.gz/.tgz, ≤10 MB), pin | private_key_hex, public_key_hex, fingerprint, curve_oid, certificate_base64 |
| POST | /api/v1/sign | application/json: private_key_hex, certificate_base64, message | signature_base64 (CMS/PKCS#7 SignedData) |
| GET | /docs | - | RapiDoc UI (HTML) |
| GET | /docs/swagger.json | - | OpenAPI specification (JSON) |
Errors are returned as {"error":"..."} with status 400, 405 or 500.
The image defines no application environment variables. TMPDIR is honoured by the Go
runtime and can be used to relocate the temp directory (see Configuration).
Multi-stage build: docker.io/golang:alpine compiles a fully static binary
(CGO_ENABLED=0, -ldflags "-s -w"), the final stage is FROM scratch and contains
nothing but that binary. Consequences: no shell, no package manager, no CA certificates,
no /tmp. Platform: linux/amd64 only. Size: ~6.4 MB.
POTATO LICENSE, Version 2 - the code is released into the public domain. https://github.com/LdDl/esia-potato/blob/master/LICENSE
(Note: the OpenAPI metadata in the source still declares MIT; the repository LICENSE
file above is authoritative.)
Content type
Image
Digest
sha256:fc7b257e9…
Size
2.6 MB
Last updated
9 months ago
docker pull dimahkiin/cryptopro-extract-service