Специфика внедрения ЭЦП в веб-сервис

avatar cromlehg 2 weeks ago

Специфика внедрения ЭЦП в веб-сервис

На момент написания статьи ЭЦП уже активно используется во многих проектах государственного сектора. Однако на наш взгляд сама реализация очень сырая, требует специфических знаний, которые плохо описаны или вовсе не описаны в документации.

В этой статье пойдет речь именно о КриптоПро реализации инструментов работы с ЭЦП.

Наш опыт внедрения ЭЦП надо сказать не очень приятный. Поэтому цель этой статьи, постараться описать некоторые из проблемы, которые могут возникнуть при внедрении подписи ЭЦП в веб-сервис на примере КриптоПро.

Для начала опишем результат, которые мы хотели получить на выходе в виде алгоритма работы пользователя с сервисом.

  1. Пользователь заходит на страничку и видит документ для подписи и список ЭЦП, которые у него есть
  2. Выбирает ЭЦП из списка
  3. Нажимает кнопку “Подписать”
  4. Подпись формируется и отправляется на бэкенд.

Проблемы с которыми Вы можете столкнуться при реализации этого алгоритма

  1. Когда Вы будете отображать список доступных ЭЦП, Вам необходимо будет отобразить пользователю понятную информацию. Дабы пользователь смог выбрать нужную ему ЭЦП. Это может быть название организации, ФИО, должность, возможно название удостоверяющего центра. Вся эта информация есть в именах ЭЦП:
    • SN - Subject Name - собственно информация о самой ЭЦП и о том на кого она выдана
    • IN - Issue Name - информация об организации, выдавшей ЭЦП
    SN и IN - получаются из плагина КриптоПро в виде строк! А значит чтобы выделить оттуда поля, необходимо эти строки распарсить по соответствующим правилам. В интернете присутствует информация о том что эти правила описаны в RFC 1779 или 2556. И вообщем-то это так, но не для отечественных ЭЦП! Правила у наших ЭЦП такие, что SN и IN могут состоять из списка вида Name = Vaue разделенного запятой. При этом Value может помещаться в двойные кавычки, если так то внутри кавычек кавычка экарнируется задвоением. Например: O=”ООО “”Рога и Копыта”””. Из известных нам реализаций, чтобы распарсить содержимое SN и IN отправляют на бэкенд и там работает парсер. Наверное потому что парсить на JS не самое приятное занятие. Мы пошли по тому же пути. Отправляли SN и IN на бэкенд, там парсили и отправляли обратно на фронтэнд.
  2. В примерах скриптов КриптоПро сертификат может получаться по имени. Однако это не сработает если у Вас два сертификата с полностью одинаковыми именами. Это не гипотетическая ситуация. Мы с такое сталкивались. В этом случае в списке пользователь выберет одну подпись, а из хранилища по имени вернется другая. Поэтому! После того как Вы запросили сертификат из хранилища по имени, необходимо отыскать тот который нужен Вам по серийному номеру. Разумно было бы конечно выполнить поиск в хранилище по серийному номеру сразу, но мы такого не нашли.
  3. Если подпись отсоединенная, то данные нужно перед подписью кодировать в BASE64 и устанавливать опцию CADESCOM_BASE64_TO_BINARY иначе проверка подписи не пройдет.

Если детализировать реализацию алгоритма с учетом описанной специфике, то работать веб-сервис должен примерно так:

  1. Подготовка сертификатов для работы и отображения в интерфейсе
  2. Открываем хранилище
  3. Получаем список всех сертификатов из хранилища
  4. Фильтруем их по валидности (истек срок, валидность)
  5. У отфильтрованных дополнительно запрашиваем серийный номер, SN и IN
  6. Записываем сертификаты в ассоциативные массив: (серийный номер -> (сертификат, SN, IN))
  7. Закрываем хранилище
  8. Отправляем на сервер (серийный номер -> (SN, IN))
  9. На сервере парсим и отправляем распарсенные данные в виде (серийный номер -> (распарсенный SN , распарсенный IN))
  10. Получаем на фронтенде распарсенные данные и правим наш массив так, чтобы он выглядел так: (серийный номер -> (сертификат, SN, IN, распарсенный SN, распарсенный IN))
  11. Формируем информацию для отображения из распарсенных полей по каждому сертификату
  12. Создаем элементы выбора (select) c value равным серийному номер, а текст из предыдущего пункта
  13. Документ для подписи кодируется в Base64
  14. Пользователь в select выбирает сертификат и нажимает “подписать”
  15. Происходит подпись
  16. Данные отправляются на сервер