Этот кейс будет интересен, если:
- сотрудники должны подписывать документы удаленно
- нужно сократить расходы на сертификаты электронной подписи
- при подписании важно проверять полномочия сотрудников и МЧД
- нужно сохранить работу с документами в корпоративной системе
Возможность подписать документ удаленно зависит в том числе от того, как организована электронная подпись. При локальном подписании сотруднику нужен компьютер с настроенным КриптоПро. Если он работает удаленно, находится в другом городе или просто не имеет доступа к такому рабочему месту, поставить подпись становится сложнее.
В корпоративном сервисе электронного подписания мы добавили еще один сценарий - Госключ.
Задача
Госключ позволяет подписывать документы удаленно, но сам по себе не заменяет корпоративный сервис подписания. Внутри компании документ связан с конкретным сотрудником, его полномочиями и машиночитаемая доверенность (МЧД), проходит свои статусы и после подписания используется другими системами.
Поэтому нам нужно было добавить Госключ как еще один способ подписи, не вынося работу с документом из корпоративной системы. Сотрудник должен по-прежнему начинать подписание там, где работает с документом, а в Госключ переходить только для подтверждения подписи.
Отдельная задача была связана с самим характером интеграции. В отличие от локального КриптоПро, результат здесь нельзя получить сразу: документ уходит во внешний сервис, сотрудник подписывает его в мобильном приложении, а корпоративная система получает результат позже. Под этот сценарий нужно было выстроить отдельный процесс обмена с Госключом.
Перед отправкой проверяем документ и полномочия
Когда сотрудник выбирает Госключ, сервис выполняет предварительную проверку - precheck. Система проверяет, существует ли документ, готов ли он к подписанию, есть ли у сотрудника доступ и не работает ли с этим документом кто-то еще.
Затем ищет подходящую МЧД. Доверенность должна давать сотруднику полномочия на этот тип документа от имени нужной организации. Если такой МЧД нет, подписание через Госключ недоступно. Если она одна, система выбирает ее автоматически. Если несколько, сотрудник сам указывает нужную.
В этот же момент создается сессия подписания. Документ блокируется на время ее действия, по умолчанию примерно на 20 минут. Каждая сессия получает correlationId, который связывает выбор МЧД и последующие действия с одной попыткой подписания.
{ "success": true, "documentId": 12345, "correlationId": "a1b2-c3d4-....", "mode": "OPTION", "mchd": [{ "id": 10, "number": "..."},
{ "id": 22, "number": "..." } ]}Если сотрудник выбирает доверенность из нескольких вариантов, портал отправляет ее идентификатор вместе с correlationId. Сервис еще раз проверяет, что МЧД подходит, и связывает ее с созданной сессией.
Отправку вынесли в фоновый процесс
После подтверждения API повторно проверяет блокировку документа и МЧД, получает СНИЛС сотрудника из кадровой системы и создает заявку в таблице-очереди goskey_requests.
goskeyRequestRepository.save(new GoskeyRequest( documentId, userLdap, request.correlationId(), request.mchdId(), personSnils, ourCompany.getName(), ourCompany.getInn(), document.getDocumentName()));Портал сразу получает HTTP 204: заявка принята. Самого обращения к Госключу на этом этапе еще не было.
Дальше заявку забирает отдельный маршрутизатор сервиса подписания. Он группирует документы одного сотрудника и одной организации: файлы с одинаковыми СНИЛС и ИНН можно включить в один заказ. В результате несколько документов отправляются одним пакетом, а сотрудник получает их в одной сессии Госключа.
Собираем пакет для Госключа
Маршрутизатор формирует ZIP-архив piev_epgu.zip. В него входят XML-заявка и сами документы:
piev_epgu.zip├── req.xml├── req.xml.sig├── document_12345.pdf├── document_12345.pdf.sig├── document_12346.xml└── document_12346.xml.sigВ req.xml указываются СНИЛС сотрудника, срок действия заявки, описание, данные организации и перечень файлов. Названия документов в XML соответствуют файлам внутри архива.
Перед сборкой пакета req.xml и каждый документ заверяются сертификатом организации через КриптоПро. Это служебная подпись: она подтверждает целостность файлов и организацию, которая их отправляет.
Подпись открепленная, поэтому исходный файл не меняется, рядом с ним создается отдельный .sig. Подпись сотрудника появляется позже, уже в приложении Госключ. То есть сертификат организации используется для подготовки пакета к отправке, а сотрудник подписывает сам документ.
Для работы КриптоПро маршрутизатору нужны отпечаток сертификата организации, контейнер, PIN и путь к криптопровайдеру. Эти настройки хранятся в Vault и подключаются к сервисам через переменные окружения и файловую систему Kubernetes.
Авторизуемся через ЕСИА
Для обращения к API Госключа сервису нужен токен ЕСИА. Он получается от имени организации: сервис берет ее API-ключ, подписывает его сертификатом КриптоПро и обменивает на accessTkn. Полученный токен кэшируется в Redis и затем используется для отправки пакетов, проверки статусов и скачивания результата.
Получать новый токен перед каждым запросом не требуется.
ESIA_API_KEY + подпись КриптоПро
↓
ЕСИА
↓
accessTkn
↓
API Госключа
У вызовов Госключа есть общий лимит - по умолчанию до 2000 запросов в минуту. Поэтому для обращений к API добавили Rate Limiter.
Отправляем пакет и получаем подпись
Сначала маршрутизатор резервирует заказ в Госключе, затем загружает piev_epgu.zip частями через push/chunked. Полученный номер заказа сохраняется в goskey_order_id, по которому сервис дальше проверяет его состояние.
Основная последовательность в коде выглядит так:
String reqXml = signRequestBuilder.build(...); var signed = detachedSignatureService.signPackage( reqXml, documentsToSign); byte[] zipBytes = zipBuilder.build( signed.reqXml(), signed.reqXmlSignature(), signed.documents()); String accessToken = esiaClient.authorize().accessTkn();goskeyClient.push(accessToken, zipBytes);После отправки сотрудник получает документы в приложении Госключ. Маршрутизатор периодически проверяет состояние заказа.
Если документы подписаны, сервис скачивает файл подписи и сохраняет его в хранилище. Документ получает статус «Подписан», блокировка снимается, а информация о результате передается другим системам через Kafka. При ошибке или тайм-ауте заказ отменяется, блокировка снимается и документ снова становится доступен для подписания.
Что получилось
В корпоративном сервисе теперь доступны два варианта: локальное подписание через КриптоПро и подписание через Госключ. Во втором случае сотруднику не нужен компьютер с установленным КриптоПро или отдельный USB-токен: подтвердить подпись можно в мобильном приложении.