Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: work with cryptopro 5.0, config changes #1

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 6 additions & 28 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,21 +1,11 @@
# Базовый образ с КриптоПро
#FROM debian:stretch-slim as cryptopro-generic
FROM node:stretch-slim as cryptopro-generic

# Устанавливаем timezone
ENV TZ="Europe/Moscow" \
docker="1"

ARG LICENSE
ENV LICENSE ${LICENSE}

# prod или test
ARG ESIA_ENVIRONMENT='test'
ENV ESIA_CORE_CERT_FILE "/cryptopro/esia/esia_${ESIA_ENVIRONMENT}.cer"
ENV ESIA_PUB_KEY_FILE "/cryptopro/esia/esia_${ESIA_ENVIRONMENT}.pub"

ARG CERTIFICATE_PIN
ENV CERTIFICATE_PIN ${CERTIFICATE_PIN}
docker="1" \
CERTIFICATE_PIN=${CERTIFICATE_PIN:-} \
ESIA_ENVIRONMENT=${ESIA_ENVIRONMENT:-test}

RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && \
echo $TZ > /etc/timezone
Expand All @@ -41,21 +31,10 @@ RUN cd /tmp/src && \
RUN apt-get update && apt-get install -y --no-install-recommends expect libboost-dev unzip g++ curl

ADD cryptopro/scripts /cryptopro/scripts
ADD cryptopro/certificates /cryptopro/certificates
ADD cryptopro/esia cryptopro/esia
RUN chmod 755 /cryptopro/scripts/ -R

FROM cryptopro-generic as configured-cryptopro

# устанавливаем лицензию, если она указана
RUN ./cryptopro/scripts/setup_license ${LICENSE}

# Устанавливаем корневой сертификат есиа
RUN ./cryptopro/scripts/setup_root ${ESIA_CORE_CERT_FILE}

# Устанавливаем сертификат пользователя
RUN ./cryptopro/scripts/setup_my_certificate /cryptopro/certificates/certificate_bundle.zip ${CERTIFICATE_PIN}

FROM configured-cryptopro
FROM cryptopro-generic
COPY package.json .
COPY package-lock.json .
COPY tsconfig.json .
Expand All @@ -68,5 +47,4 @@ RUN npm run build
RUN npm prune --production

EXPOSE 3037
#CMD ["sleep", "100000000000"]
CMD npm start
CMD /cryptopro/scripts/start.sh
43 changes: 20 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,24 @@
# Сервис для создания подписей для ЕСИА на nodejs с КриптоПро 4.0 в докер контейнере
# Сервис для создания подписей для ЕСИА на nodejs с КриптоПро 5.0 в докер контейнере
https://github.com/waves-enterprise/cryptopro-sign
Основан на https://github.com/dbfun/cryptopro

## Принцип работы
* Устанавливает КриптоПро из установщика `cryptopro/install/linux-amd64_deb.tgz`
* Устанавливает лицензию КриптоПро
* Загружает корневой сертификат есиа
* Загружает пользовательский сертификат
* Запускает рест сервер со swagger и методом создания подписей
* При старте контейнера:
* Устанавливает лицензию КриптоПро
* Загружает корневой сертификат есиа
* Загружает пользовательский сертификат
* Запускает рест сервер со swagger и методом создания подписей

### Установка лицензии
Лицензия устанавливается из аргумента `LICENSE`, если не указана используется триал версия(работает 3 месяца).
Лицензия устанавливается из переменной окружения `LICENSE`, если не указана используется триал версия (работает 3 месяца).

### Загрузка корневого сертификата
в зависимости от аргумента `ESIA_ENVIRONMENT` загружается сертификат нужного окружения есиа:
* Если не указан или указан `test` - сертификат тестового контура есиа (https://esia-portal1.test.gosuslugi.ru)
* Если указан `prod` - сертификат от основного есиа (https://esia.gosuslugi.ru)
В зависимости от переменной окружения `ESIA_ENVIRONMENT`, загружается сертификат нужного окружения ЕСИА:
* Если не указан или указан `test` - сертификат тестового контура ЕСИА (https://esia-portal1.test.gosuslugi.ru)
* Если указан `prod` - сертификат от основного ЕСИА (https://esia.gosuslugi.ru)

Корневые сертификаты есиа лежат в папке `cryptopro/esia`
Корневые сертификаты ЕСИА лежат в папке `cryptopro/esia`

### Загрузка пользовательского сертификата
Необходимо специальным образом сформировать zip-архив `certificate_bundle.zip` и положить его в папку `/cryptopro/certificates`.
Expand All @@ -31,13 +34,14 @@
├── primary2.key
└── primary.key
```
Первый найденный файл в корне архива будет воспринят как сертификат, а первый найденный каталог - как связка файлов закрытого ключа. Пароль от контейнера, если есть, передается аргументом `CERTIFICATE_PIN`
Первый найденный файл в корне архива будет воспринят как сертификат, а первый найденный каталог - как связка файлов закрытого ключа. Пароль от контейнера, если есть, передается переменной окружения `CERTIFICATE_PIN`.
Пароль от архива, если есть, передаётся переменной окружения при запуске

### Как запустить
1. Скачать [КриптоПро CSP 4.0 для Linux (x64, deb)](https://www.cryptopro.ru/products/csp/downloads) и положить по пути `install/linux-amd64_deb.tgz`
2. Подложить архив с сертификатом как `/cryptopro/certificates/certificate_bundle.zip`
3. Создаем образ `docker build --tag cryptopro-sign --build-arg CERTIFICATE_PIN=12345678 .`
4. Запускаем `docker run -it --rm -p 3037:3037 --name cryptopro-sign cryptopro-sign`
1. Скачать [КриптоПро CSP 5.0 для Linux (x64, deb)](https://www.cryptopro.ru/products/csp/downloads) и положить по пути `install/linux-amd64_deb.tgz`
2. Подготовить архив с сертификатом на хосте и назвать его `certificate_bundle.zip`
3. Создаем образ `docker build --tag cryptopro-sign .`
4. Запускаем `docker run -p 3037:3037 -v <Путь к директории на хосте с certificate_bundle.zip>:/cryptopro/certificates/:ro --env BUNDLE_PASSWORD=<Пароль от zip архива> --env LICENCE=<Лицензия CryptoPro> --env CERTIFICATE_PIN=<Пароль от контейнера> --env ESIA_ENVIRONMENT=<test/prod> --name cryptopro-sign cryptopro-sign`

### Рест сервер
Если следовать инструкции выше, то свагер будет находиться по адресу `http://localhost:3037/docs/#/`
Expand All @@ -59,16 +63,9 @@
* Переносим на нашу машину приватные ключи `docker cp cryptopro-sign:var/opt/cprocsp/keys/root/test2.000 cryptopro/certificates/test2.000`
* В папке проекта `cryptopro/certificates` создаем архив. В архив кладем папку `test2.000` и файл `certnew.cer`
* Архив называем `certificate_bundle.zip`, пересобираем докер образ и запускаем.

Черпал вдохновение и взял многие вещи из [этого репозитория](https://github.com/dbfun/cryptopro)

### Авторизация
Для использования в продакшене лучше, чтобы сервис был не доступен снаружи инфраструктуры, т.к тут через рест можно что угодно подписать зашитым сертификатом. Если все же инфраструктура открытая, то в сервис следует встроить проверку авторизации.

### Возможные проблемы:
Если получаете код ошибки `0x80090010` при вызове метода sign - вероятно срок действия вашего сертификата истек. Попробуйте создать новый по инструкции.

### Env контейнера
```
PORT - numer. Порт рест сервера. Дефолт: 3037
```
Если получаете код ошибки `0x80090010` при вызове метода sign - вероятно срок действия вашего сертификата истек. Попробуйте создать новый по инструкции.
Binary file modified cryptopro/esia/esia_prod.cer
Binary file not shown.
Binary file modified cryptopro/esia/esia_test.cer
Binary file not shown.
30 changes: 16 additions & 14 deletions cryptopro/scripts/setup_my_certificate
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,14 @@ dir=`mktemp -d`
cd "$dir"
#cat - > "bundle.zip"
echo "bundle file $1"
unzip -q "$1"
#rm bundle.zip
if [ -n "$BUNDLE_PASSWORD" ]; then
info "use bundle password to unzip"
unzip -q -P "$BUNDLE_PASSWORD" "$1"
else
info "no bundle password"
unzip -q "$1"
fi

# info "Temp dir: $dir"

# проверка всех необходимых файлов в закрытом ключе
function testprivk {
Expand All @@ -29,18 +33,17 @@ function testprivk {

# для связки закрытого ключа с сертификатом необходимо указать полное наименование контейнера, которое находится в name.key
# таким способом также можно выяснить название, но с cp1251 работает не устойчиво
# function findContFullName {
# contFullName=`csptest -keyset -enum_cont -verifycontext -unique | grep --fixed-strings "$contShortName" | cut -d '|' -f1`
# if [ ! -n "$contFullName" ]; then
# error "Proper container not found by short name: $contShortName"
# exit 1
# fi
# }
function findContFullName {
contFullName=`csptest -keyset -enum_cont -verifycontext -unique | grep --fixed-strings "$contShortName" | cut -d '|' -f2`
if [ -z "$contFullName" ]; then
error "Proper container not found by short name: $contShortName"
exit 1
fi
}

# xargs удаляет `./` из названия файла
certFileName=`find -maxdepth 1 -type f \! -name . | head -n1 | xargs -I{} basename {}`
contShortName=`find -maxdepth 1 -type d \! -name . | head -n1 | xargs -I{} basename {}`
contFullName=`tail -c+5 "$contShortName/name.key"`

# Есть контейнер, устанавливаем
if [ -n "$contShortName" ]; then
Expand All @@ -55,7 +58,7 @@ fi
# Есть сертификат
if [ -n "$certFileName" ]; then

if [ ! -n "$contShortName" ]; then
if [ -z "$contShortName" ]; then
# устанавливается только сертификат
certmgr -inst -file "$certFileName"

Expand All @@ -67,6 +70,7 @@ if [ -n "$certFileName" ]; then
else

# устанавливается сертификат + контейнер, нужно их связать ('PrivateKey Link')
findContFullName

info "Key container full name: $contFullName" | iconv -fcp1251 -tutf-8

Expand All @@ -81,8 +85,6 @@ if [ -n "$certFileName" ]; then
fi

fi


fi

rm -rf "$dir"
18 changes: 18 additions & 0 deletions cryptopro/scripts/start.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#!/bin/bash
# Скрипт выполняется однократно при запуске контейнера
source lib/colors.sh
source lib/functions.sh

info "Configuring cryptopro"

CERTIFICATE_PIN=$([ -n "$CERTIFICATE_PIN" ] && echo "$CERTIFICATE_PIN" || echo "")
ESIA_ENVIRONMENT=$([ -n "$ESIA_ENVIRONMENT" ] && echo "$ESIA_ENVIRONMENT" || echo "test")
export CERTIFICATE_PIN="$CERTIFICATE_PIN"
export ESIA_ENVIRONMENT="$ESIA_ENVIRONMENT"

/cryptopro/scripts/setup_license $LICENSE
/cryptopro/scripts/setup_root "/cryptopro/esia/esia_${ESIA_ENVIRONMENT}.cer"
/cryptopro/scripts/setup_my_certificate /cryptopro/certificates/certificate_bundle.zip ${CERTIFICATE_PIN}

info "Configuration finished, starting service..."
exec npm start
2 changes: 1 addition & 1 deletion src/utils/cryptoProUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ let contrainerHash: string | null = null
const getContainerHash = async () => {
if (!contrainerHash) {
const response = await execute('certmgr -list')
const match = response.match(/SHA1 Hash\s*: (\w+)$/m)
const match = response.match(/SHA1 Thumbprint\s*: (\w+)$/m)
if (!match) {
throw new InternalException('Cannot get container hash. It seems that service is not correctly configured')
}
Expand Down