Я расскажу о тонкостях внедрения электронной цифровой подписи (ЭЦП) в информационные системы (ИС) на базе веб технологий в контексте Национального Удостоверяющего Центра Республики Казахстан (НУЦ РК).
В центре внимания будет формирование ЭЦП под электронными документами и, соответственно, NCALayer — предоставляемое НУЦ РК криптографическое программное обеспечение. В частности уделю внимание вопросам связанным с UX и объемом поддерживаемого функционала NCALayer.
Процесс разделю на следующие этапы:
- формирование неизменного представления подписываемого документа (под подписываемым документом я буду подразумевать любые данные которые необходимо подписать, такие как: договор, бланк заказа, форма аутентификации и т.п.);
- подписание документа в веб интерфейсе с помощью NCALayer;
- проверка подписи на стороне сервера;
- (при необходимости) подготовка подписи к долгосрочному хранению.
Формирование неизменного представления подписываемого документа
Разработчикам важно понимать то, что любое изменение подписанного документа приведет к тому, что подпись под ним перестанет проходить проверку. При этом изменения могут быть вызваны не только редактированием самих данных документа, но и обновлением структуры документа или механизма его сериализации.
Рассмотрим простой пример — документы хранятся в виде записей в базе данных. Для подписания документа необходимо сформировать его представление в виде единого блока данных (конечно можно подписывать каждое поле записи поотдельности, но обычно так не делают), сделать это можно, к примеру, следующими способами:
- извлечь все поля записи, привести их к строкам и соединить в одну строку;
- сформировать XML или JSON представление;
- сформировать PDF документ на базе шаблона с каким-то оформлением содержащий данные из записи;
- и т.п.
Далее возможны два сценария каждый из которых имеет свои подводные камни:
- ИС не хранит сформированного представления и каждый раз для проверки подписи под документом (в частности проверки неизменности данных) формирует его;
- ИС хранит сформированное представление и использует его для проверки подписи.
В том случае, если ИС не хранит сформированного представления, разработчикам необходимо гарантировать что в будущем формируемые представления будут бинарно идентичны тем, которые были сформированы в первый раз не смотря на:
- изменения схемы базы данных;
- обновления механизма формирования представления (которое может так же зависеть от внешних библиотек которые, скорее всего, не стремятся к обеспечению бинарной идентичности).
В том случае, если на каком-то этапе бинарная идентичность перестанет соблюдаться, то проверки цифровых подписей перестанут выполняться и юридическая значимость документов в ИС будет утеряна.
Подход с хранением сформированного представления в базе данных ИС надежнее с точки зрения обеспечения сохранности юридической значимости, но и тут есть нюанс — необходимо гарантировать эквивалентность данных в подписанном документе данным в записи в базе данных иначе может возникнуть такая ситуация когда ИС принимает решение (выполняет расчет) на основании информации не соответствующей тому, что было подписано. То есть нужно либо так же, как и в предыдущем случае, обеспечивать бинарную идентичность формируемого представления, тогда при проверках в первую очередь следует формировать представление из текущего содержимого записи в базе данных и бинарно сравнивать новое представление с сохраненным. Либо необходим механизм позволяющий разобрать сохраненное сформированное представление и сравнить данные из него с текущим содержимым записи в базе данных. Этот механизм так же придется дорабатывать постоянно параллельно с доработками основного функционала ИС.
Подписание документа в веб интерфейсе с помощью NCALayer
Для формирования цифровых подписей на стороне клиента НУЦ РК предоставляет единственный инструмент — сертифицированное программное обеспечение NCALayer которое представляет из себя WebSocket сервер, запускаемый на 127.0.0.1
, которому можно отправлять запросы на выполнение криптографических (а так же некоторых смежных) операций. При выполнении некоторых операций NCALayer отображает диалоговые окна — то есть берет часть работы по получению пользовательского ввода на себя.
Описание API NCALayer доступно в составе комплекта разработчика. Для того, чтобы поэкспериментировать со взаимодействием с NCALayer по WebSocket можно воспользоваться страницей интерактивной документации KAZTOKEN mobile (KAZTOKEN mobile повторяет API NCALayer).
Взаимодействовать с NCALayer из браузера можно напрямую с помощью класса WebSocket, либо можно воспользоваться библиотекой ncalayer-js-client которая оборачивает отправку команд и получение ответов в современные async
вызовы.
Замечу что весь основной функционал NCALayer доступен в модуле kz.gov.pki.knca.commonUtils
, использовать модуль kz.gov.pki.knca.applet.Applet
(наследие Java аплета) не рекомендую, так как, на мой взгляд, это не даст никаких преимуществ, но шансов выстрелить себе в ногу с ним больше — к примеру можно случайно разработать интерфейс который не будет поддерживать аппаратных носителей (токенов или смарт-карт) с несколькими ключевыми парами.
Модуль kz.gov.pki.knca.commonUtils
берет на себя взаимодействие с пользователем связанное с выбором конкретного хранилища, которое нужно использовать для выполнения операции (так же он берет на себя выбор конкретного сертификата и соответствующего ключа, а так же ввод пароля или ПИН кода), но ему необходимо указать какой тип хранилищ нужно использовать. Типы хранилищ стоит разделить на два класса:
- файловые, поддерживается единственный тип заданный константой
'PKCS12'
, - аппаратные (токены и смарт-карты), для перечисления тех типов, экземпляры которых в данный момент подключены в ПК пользователя, следует использовать запрос
getActiveTokens
.
Таким образом для того, чтобы предоставить пользователю возможность работать с любым поддерживаемым NCALayer хранилищем, можно воспользоваться одним из следующих подходов:
- гибкий — отправить запрос
getActiveTokens
, в ответ получить массив имен доступных а данный момент типов хранилищ, добавить в него'PKCS12'
и спросить у пользователя каким он хотел бы в данный момент воспользоваться; - простой — отправить запрос
getActiveTokens
, в том случае, если он вернул массив несколькими именами, попросить пользователя отключить лишнее, если в массиве один элемент, использовать его, если массив пустой, использовать'PKCS12'
.
Для подписания данных рекомендую использовать следующие запросы (опять же чтобы снизить вероятность выстрела в ногу):
createCAdESFromBase64
— вычислить подпись под данными и сформировать CMS (CAdES);createCMSSignatureFromBase64
— вычислить подпись под данными, получить на подпись метку времени (TSP) и сформировать CMS (CAdES) с внедренной меткой времени;signXml
— вычислить подпись под XML документом, сформированную подпись добавить в результирующий документ (XMLDSIG);signXmls
— аналогичноsignXml
, но позволяет за один раз подписать несколько XML документов.
В качестве параметров каждому из них нужно будет передать тип хранилища, тип сертификата, подписываемые данные и дополнительные модификаторы.
Модуль kz.gov.pki.knca.commonUtils
поддерживает следующие типы сертификатов:
'AUTHENTICATION'
— сертификаты для выполнения аутентификации;'SIGNATURE'
— сертификаты для подписания данных.
NCLayer предоставит пользователю выбирать только из тех сертификатов, которые соответствуют указанному типу.
Упрощенный пример подписания произвольного блока данных с использованием ncalayer-js-client:
async function connectAndSign(base64EncodedData) {
const ncalayerClient = new NCALayerClient();
try {
await ncalayerClient.connect();
} catch (error) {
alert(`Не удалось подключиться к NCALayer: ${error.toString()}`);
return;
}
let activeTokens;
try {
activeTokens = await ncalayerClient.getActiveTokens();
} catch (error) {
alert(error.toString());
return;
}
const storageType = activeTokens[0] || NCALayerClient.fileStorageType;
let base64EncodedSignature;
try {
base64EncodedSignature = await ncalayerClient.createCAdESFromBase64(storageType, base64EncodedData);
} catch (error) {
alert(error.toString());
return;
}
return base64EncodedSignature;
}
Проверка подписи на стороне сервера
Тема проверки цифровых подписей обширна и я не планировал раскрывать ее в этот раз, но упомянуть о необходимости выполнения проверок как с технической, так и юридической точки зрения счел необходимым.
С технической точки зрения проверка цифровой подписи на стороне сервера в первую очередь гарантирует целостность полученного документа, во вторую — авторство, а так же неотказуемость. То есть даже в том случае, если ИС получает подписанный документ не напрямую от пользователя, а через какие-то дополнительные слои программного обеспечения, уверенность в том, что было получено именно то, что отправлял пользователь сохраняется. Поэтому в ситуации когда подписание на стороне клиента реализовано, а возможность проверки подписи на стороне сервера отсутствует, внедрение цифровой подписи теряет смысл.
С юридической точки зрения ориентироваться следует на Приказ Министра по инвестициям и развитию Республики Казахстан “Об утверждении Правил проверки подлинности электронной цифровой подписи”. В Приказе перечислены необходимые проверки которые должны выполнять информационные системы для обеспечения юридической значимости подписанных электронной цифровой подписью документов. Анализу этого документа, а так же некоторым техническим вопросам посвящена заметка Проверка цифровой подписи.
Выполнять проверки необходимо с применением сертифицированных средств, к примеру с помощью библиотек входящих в состав комплекта разработчика НУЦ РК, либо можно воспользоваться готовым решением SIGEX.
Подготовка подписи к долгосрочному хранению
Проверка подписи под электронным документом включает в себя проверку срока действия сертификата и проверку статуса отозванности сертификата. В том случае, когда необходимо обеспечить юридическую значимость подписанного документа по истечению срока действия сертификата или в том случае, когда сертификат был отозван через некоторое время после подписания, следует собирать дополнительный набор доказательств фиксирующий момент подписания и подтверждающий то, что на момент подписания сертификат не истек и не был отозван.
Для фиксации момента подписания принято использовать метки времени TSP. Метку времени на подпись можно получить либо на клиенте (запрос createCMSSignatureFromBase64
интегрирует метку времени в CMS), либо на сервере. Метка времени позволит удостовериться в том, что момент подписания попадает в срок действия сертификата.
Для того, чтобы удостовериться в том, что сертификат не был отозван в момент подписания, следует использовать CRL или OCSP ответ. Этот нюанс и рекомендации по реализации описаны в разделе APPENDIX B — Placing a Signature At a Particular Point in Time документа RFC 3161.