REST API: формирование и проверка электронной подписи данных (PKI) — документация Waves Enterprise 1.7.0

REST API: формирование и проверка электронной подписи данных (PKI) — документация Waves Enterprise 1.7.0 Электронная цифровая подпись
Содержание
  1. Что имеем на входе?
  2. Важное вступление
  3. Так, а что надо на выходе?
  4. Angular (es modules typescript)
  5. Cryptopro
  6. Done.
  7. Ii – сборка проекта со сборкой corefx для windows
  8. Post /pki/verify¶
  9. Вычисление хэш значения
  10. Вычисление хэш значения бинарных данных (его и используем в проекте)
  11. Запуск режима разработки
  12. Запуск тестов
  13. Зачем мне этот пакет?
  14. Интерфейс программирования приложений
  15. Лицензия
  16. Методы объекта сертификата
  17. Миграция с версии 1 на 2
  18. Настройка плагина для firefox (до версии 52)
  19. Немного покодим
  20. Первым делом создадим новую папку
  21. Поддерживаемые скзи
  22. Подписание pdf на js и вставка подписи на c#, используя крипто про
  23. Примеры
  24. Пробный запуск
  25. Проверка в криптоарм
  26. Проверка пакета перед публикацией в npm
  27. Проверка работы примеров с react и angular
  28. Расшифровка данных
  29. Соберем проект с поддержкой гост р 34.11-2021 256 bit
  30. Тем, кто хочет помочь
  31. Установка
  32. Установка криптопро csp в linux / osx
  33. Установка криптопро эцп browser plugin в linux
  34. Установка сертификатов в linux
  35. Шифроваение данных
  36. Это вообще законно?

Что имеем на входе?

  1. КриптоПро CSP версии 5.0 – для поддержки Российских криптографических алгоритмов (подписи, которые выпустили в аккредитованном УЦ в РФ)

  2. КриптоПро TSP Client 2.0 – нужен для штампа времени

  3. КриптоПро OCSP Client 2.0 – проверит не отозван ли сертификат на момент подписания

  4. КриптоПро .NET Client – таков путь

  5. Любой сервис по проверке ЭП – я использовал Контур.Крипто как основной сервис для проверки ЭП и КриптоАРМ как локальный. А еще можно проверить ЭП на сайте Госуслуг

  6. КЭП по ГОСТ Р 34.11-2021/34.10-2021 256 bit, которую выпустил любой удостоверяющий центр

Лицензирование ПО и версии

Важное вступление

В гайде описывается формирование отсоединенной подписи в формате PKCS7 (рядом с файлом появится файл в формате .sig). Такую подпись может запросить нотариус, ЦБ и любой кому нужно долгосрочное хранения подписанного документа. Удобство такой подписи в том, что при улучшении ее до УКЭП CAdES-X Long Type 1 (CMS Advanced Electronic Signatures [1]) в нее добавляется штамп времени, который генерирует TSA (Time-Stamp Protocol [2]) и статус сертификата на момент подписания (OCSP [3]) – подлинность такой подписи можно подтвердить по прошествии длительного периода (Усовершенствованная квалифицированная подпись [4]).

Читайте также:  Как настроить Яндекс браузер для работы с электронной подписью

Код основан на репозиториях corefx и DotnetCoreSampleProject – в последнем проще протестировать свои изменения перед переносом в основной проект и он будет отправной точкой по сборке corefx. Судя по записям с форума компании [5], решение для .

Так, а что надо на выходе?

А на выходе надо получить готовое решение, которое сделает отсоединенную ЭП в формате .sig со штампом времени на подпись и доказательством подлинности. Для этого зададим следующие критерии:

  1. ЭП проходит проверку на портале Госуслуг, через сервис для подтверждения подлинности ЭП формата PKCS#7 в электронных документах;

  2. КриптоАРМ после проверки подписи

    1. Заполнит поле “Время создания ЭП” – в конце проверки появится окно, где можно выбрать ЭП и кратко посмотреть ее свойства

      Стобец "Время создация ЭП"
      Стобец “Время создация ЭП”
    2. В информации о подписи и сертификате (двойной клик по записе в таблице) на вкладке “Штампы времени” в выпадающем списке есть оба значения и по ним заполнена информация:

      1. Подпись:

        REST API: формирование и проверка электронной подписи данных (PKI) — документация Waves Enterprise 1.7.0
      2. Доказательства подлинности:

        REST API: формирование и проверка электронной подписи данных (PKI) — документация Waves Enterprise 1.7.0
    3. В протоколе проверки подписи есть блоки “Доказательства подлинности”, “Штамп времени на подпись” и “Время подписания”. Для сравнения: если документ подписан просто КЭП, то отчет по проверке будет достаточно коротким в сравнении с УКЭП.

  3. Контур.Крипто при проверке подписи выдаст сообщение, что совершенствованная подпись подтверждена, сертификат на момент подписания действовал и указано время создания подпись:

    Усовершенствованная подпись подтверждена
    Усовершенствованная подпись подтверждена

Angular (es modules typescript)

Запуск в режиме разработки:

Запуск в продакшн режиме:

npm run build
npm run serve

Cryptopro

Единое, асинхронное API для взаимодействия с КриптоПРО ЭЦП Browser Plug-In

Done.

Гайд написан с исследовательской целью – проверить возможность подписания документов УКЭП с помощью самописного сервиса на .NET Core 3.1 с формированием штампов подлинности и времени подписания документов.

Безусловно это решение не стоит брать в работу “как есть” и нужны некоторые доработки, но в целом оно работает и подписывает документы подписью УКЭП.

Ii – сборка проекта со сборкой corefx для windows

  1. Выполните 1-3 и 6-й шаги из I этапа;

  2. Склонируйте репозиторий corefx в .

  3. Выполните сборку запустив .corefxbuild.cmd – на этом этапе потребуется предустановленный DIA SDK

  4. Выполните шаги 5, 7-9 из I этапа. Вместо условного пути .packages укажите .corefxartifactspackagesDebugNonShipping, а вместо .runtime укажите .corefxartifactsbinruntimenetcoreapp-Windows_NT-Debug-x64

На этом месте у вас должно получиться решение, которое поддерживает ГОСТ Р 34.11-2021 256 bit.

Post /pki/verify¶

Метод предназначен для проверки отсоединённой ЭП для данных, передаваемых в запросе.
Запрос состоит из следующих полей:

Пример запроса:

Ответ метода содержит поле sigStatus с булевым типом данных: true – подпись действительна, false – подпись скомпрометирована.

Пример ответа:

Вычисление хэш значения

var CADESCOM_HASH_ALGORITHM_CP_GOST_3411 = 100; function run() { // Создаем объект CAdESCOM.HashedData var oHashedData = cadesplugin.CreateObject("CAdESCOM.HashedData"); // Алгоритм хэширования нужно указать до того, как будут переданы данные oHashedData.Algorithm = CADESCOM_HASH_ALGORITHM_CP_GOST_3411; // Передаем данные oHashedData.Hash("Some data here."); // Вычисляем хэш-значение var sHashValue1 = oHashedData.Value; // Хэш-значение будет вычислено от данных в кодировке UCS2-LE // Для алгоритма SHA-1 хэш-значение будет совпадать с вычисленным при помощи CAPICOM document.getElementById("hashVal1").innerHTML = sHashValue1; // Получение значения свойства oHashedData.Value сбрасывает // состояние объекта (алгоритм хэширования остается прежним). // Но само значение свойства можно получить несколько раз: var sHashValue2 = oHashedData.Value; document.getElementById("hashVal2").innerHTML = sHashValue2; // То же самое хэш-значение можно получить, если передать данные по частям oHashedData.Hash("Some "); oHashedData.Hash("data "); oHashedData.Hash("here."); var sHashValue3 = oHashedData.Value; document.getElementById("hashVal3").innerHTML = sHashValue3; }

Вычисление хэш значения бинарных данных (его и используем в проекте)

var CADESCOM_HASH_ALGORITHM_CP_GOST_3411 = 100; var CADESCOM_BASE64_TO_BINARY = 1; function run() { // Создаем объект CAdESCOM.HashedData var oHashedData = cadesplugin.CreateObject("CAdESCOM.HashedData"); // Алгоритм хэширования нужно указать до того, как будут переданы данные oHashedData.Algorithm = CADESCOM_HASH_ALGORITHM_CP_GOST_3411; // Указываем кодировку данных // Кодировка должна быть указана до того, как будут переданы сами данные oHashedData.DataEncoding = CADESCOM_BASE64_TO_BINARY; // Предварительно закодированные в BASE64 бинарные данные // В данном случае закодирован файл со строкой "Some Data." var dataInBase64 = "U29tZSBEYXRhLg=="; // Передаем данные oHashedData.Hash(dataInBase64); // Получаем хэш-значение var sHashValue = oHashedData.Value; // Это значение будет совпадать с вычисленным при помощи, например, // утилиты cryptcp от тех же исходных _бинарных_ данных. // В данном случае - от файла со строкой "Some Data." document.getElementById("hashVal").innerHTML = sHashValue; }

Запуск режима разработки

Устанавливаем зависимости:

Во время работы с кодом необходим запущенный сборщик:

И пример, на котором можно тестировать изменения.
Удобнее всего тестировать на примере с тэгом script, тк он отвязан от фреймворков
и использует сборку в формате UMD из папки dist/, постоянно обновляемую пока работает
сборщик. Запускаем его таким образом:

После выполнения npm link ../../ в директории examples/script-tag/node_modules папка crypto-pro станет ярлыком,
указывающим на папку содержащую локально собранный пакет.

Запуск тестов

Тесты написаны с использованием Jest:

Зачем мне этот пакет?

КриптоПРО ЭЦП Browser Plug-In доступен в разных браузерах в двух версиях.
Асинхронной (в современных браузерах) и синхронной (в браузерах постарше).
С помощью этого пакета можно не писать реализацию под каждую версию плагина дважды.
И вместо этого
и этого
написать это (UMD):

или это (ES Modules Typescript):

Интерфейс программирования приложений

Предназначен для создания и проверки сообщений, подписанных усовершенствованной подписью и удовлетворяющих стандарту CAdES (ETSI TS 101 733). На настоящий момент интерфейс поддерживает создание подписей типа CAdES BES, CADES-T и CAdES-X Long Type 1.

Интерфейс клиентских приложений имеет следующие особенности:

  • Поддерживаемые платформы:
  • Содержит интерфейс языка С, спроектированный таким образом, чтобы дополнять или замещать функции Crypto API для работы с подписанными сообщениями.
  • Содержит компоненту COM, расширяющую интерфейс Microsoft CAPICOM.
  • Содержит КриптоПро ЭЦП Browser plug-in, предоставляющий интерфейс языка JavaScript для реализации криптографических операций в браузерах. Поддерживает создание и проверку подписи, шифрование и создание запросов на сертификат.
  • Расширение для языка PHP, предоставляющее интерфейс, аналогичный КриптоПро ЭЦП Browser plug-in, для использования в серверных приложениях на языке PHP.
  • Для настройки клиентских модулей в пределах организации или подразделения можно использовать групповые политики.

Данный интерфейс позволяет реализовать механизм работы с усовершенствованной подписью из прикладного программного обеспечения. Простота интерфейса дает возможность быстро расширить функциональность существующей системы, а возможность настройки с помощью групповых политик значительно упрощает процесс встраивания, развёртывания и администрирования клиентских модулей.

Подробное описание КриптоПро ЭЦП SDK приведено в Руководстве разработчика КриптоПро ЭЦП SDK.

Лицензия

MIT

Методы объекта сертификата

Сертификат предоставляет следущее API:

Миграция с версии 1 на 2

Внесены следующие изменения:

  • Пакет собран в форматах:
    • UMD, в папке dist/, для подключения через тэг script. Объект window.cryptoPro доступен глобально.
    • ES Modules, в папке lib/, для использования с разными системами сборки.
      Методы API импортируются напрямую из npm пакета.
  • В UMD версии переименован глобальный объект с window.CryptoPro на window.cryptoPro
  • Переименованы общие методы:
  • Убран метод signDataXML
  • Переименованы методы сертификата:
  • Результат методов сертификата getOwnerInfo и getIssuerInfo
    изменился с { descr, title, translated } на { description, title, isTranslated }
  • Принципиальная реализация методов, обращающихся к Крипто ПРО не изменилась.
    Получение сертификатов, создание подписи, проверка корректности настроек работают по-прежнему.
  • Убрана поддержка IE8 (Крипто ПРО его больше не поддерживает)
  • Убрана поддержка КриптоПРО CSP версий ниже 4.0
  • Убрана поддержка КриптоПРО ЭЦП browser plug-in версий ниже 2.0
  • Доработана обработка ошибок, выбрасываемых из Крипто ПРО
  • При написании кода будут работать автодополнения и подсказки
  • Исправлена проблема работы библиотеки с UglifyJs
  • Методы API доступны напрямую:

В версии 1:

В версии 2 (UMD):

В версии 2 (ES Modules):

Настройка плагина для firefox (до версии 52)

После настройки плагина на страницах, запрашивающих работу с ЭП в панели навигации, рядом с url будет кнопка,
позволяющая “разрешить и запомнить” использование установленного плагина.

Перезапустите Firefox, и убедитесь в наличии CryptoPRO Cades plugin (см. Menu -> Addons).

Немного покодим

Потребуется 2 COM библиотеки: “CAPICOM v2.1 Type Library” и “Crypto-Pro CAdES 1.0 Type Library”. Они содержат необходимые объекты для создания УКЭП.

В этом примере будет подписываться BASE64 строка, содержащая в себе PDF-файл. Немного доработав код можно будет подписать hash-значение этого фала.

Первым делом создадим новую папку

… и положим туда все необходимое.

Инструкция делится на 2 этапа – мне пришлось выполнить оба, чтобы решение заработало. В папку добавьте подпапки .runtime и .packages

Поддерживаемые скзи

КриптоПРО CSP (v4.0 ) рекомендуется использование только сертифицированных версий. Инструкция по установке:

КриптоПРО ЭЦП browser plug-in (v2.0.12438 ).

Инструкция по установке плагина в Linux. В Windows и OSX следуйте указаниям программы-установщика.

Инструкция по установке сертификатов в систему для Linux / OSX.

Подписание pdf на js и вставка подписи на c#, используя крипто про

Итак. Пришла задача. Используя браузер предложить пользователю подписать PDF электронной подписью (далее ЭП). У пользователя должен быть токен, содержащий сертификат, открытый и закрытый ключ. Далее на сервере надо вставить подпись в PDF документ. После этого надо проверить подпись на валидность. В качестве back-end используем ASP.NET и соответственно C#.

Вся соль в том, что надо использовать подпись в формате CAdES-X Long Type 1, и российские ГОСТ Р 34.10-2001, ГОСТ Р 34.10-2021 и т.п. Кроме того подписей может быть более одной, то есть пользователи могут по очереди подписывать файл. При этом предыдущие подписи должны оставаться валидными.

В процессе решения задачу решили усложнить для нас, и уменьшить объем передаваемых данных на клиент. Передавать только hash документа, но не сам документ.

В исходниках буду опускать малозначимые для темы моменты, оставлю только то что касается криптографии. Код на JS приведу только для нормальных браузеров, JS-движки которых поддерживают Promise и function generator. Думаю кому нужно для IE напишут сами (мне пришлось «через не хочу»).

Что нужно:

  1. Пользователь должен получить пару ключей и сертификат.
  2. Пользователь должен установить plug-in от Крипто ПРО. Без этого средствами JS мы не сможем работать с криптопровайдером.

Замечания:

  1. Для тестов у меня был сертификат выданный тестовым ЦС Крипто ПРО и нормальный токен, полученный одним из наших сотрудников (на момент написания статьи ~1500р с годовой лицензией на Крипто ПРО и двумя сертификатами: но «новому» и «старому» ГОСТ)
  2. Говорят, plug-in умеет работать и с ViPNet, но я не проверял.

Теперь будем считать что у нас на сервере есть готовый для подписывания PDF.

Добавляем на страницу скрипт от Крипто ПРО:

<script src="/Scripts/cadesplugin_api.js" type="text/javascript"></script>

Дальше нам надо дождаться пока будет сформирован объект cadesplugin

window.cadespluginLoaded = false;
cadesplugin.then(function () { window.cadespluginLoaded = true;
});

Запрашиваем у сервера hash. Предварительно для этого нам ещё надо знать каким сертификатом, а значит и алгоритмом пользователь будет подписывать. Маленькая ремарка: все функции и «переменные» для работы с криптографией на стороне клиента я объединил в объект CryptographyObject.

Метод заполнения поля certificates объекта CryptographyObject:

 fillCertificates: function (failCallback) { cadesplugin.async_spawn(function*() { try { let oStore = yield cadesplugin.CreateObjectAsync("CAPICOM.Store"); oStore.Open(cadesplugin.CAPICOM_CURRENT_USER_STORE, cadesplugin.CAPICOM_MY_STORE, cadesplugin.CAPICOM_STORE_OPEN_MAXIMUM_ALLOWED); let certs = yield oStore.Certificates; certs = yield certs.Find(cadesplugin.CAPICOM_CERTIFICATE_FIND_TIME_VALID); let certsCount = yield certs.Count; for (let i = 1; i <= certsCount; i ) { let cert = yield certs.Item(i); CryptographyObject.certificates.push(cert); } oStore.Close(); } catch (exc) { failCallback(exc); } }); }

Комментарий: пробуем открыть хранилище сертификатов. В этот момент система пользователя выдаст предупреждение, что сайт пытается что-то сделать с сертификатами, криптографией и прочей магической непонятной ерундой. Пользователю тут надо будет нажать кнопку «Да»

Далее получаем сертификаты, валидные по времени (не просроченные) и складываем их в массив certificates. Это надо сделать из-за асинхронной природы cadesplugin (для IE всё иначе 😉 ).

Метод получения hash:

getHash: function (certIndex, successCallback, failCallback, какие-то ещё параметры) { try { cadesplugin.async_spawn(function*() { let cert = CryptographyObject.certificates[certIndex]; let certPublicKey = yield cert.PublicKey(); let certAlgorithm = yield certPublicKey.Algorithm; let algorithmValue = yield certAlgorithm.Value; let hashAlgorithm; //определяем алгоритм подписания по данным из сертификата и получаем алгоритм хеширования if (algorithmValue === "1.2.643.7.1.1.1.1") { hashAlgorithm = "2021256"; } else if (algorithmValue === "1.2.643.7.1.1.1.2") { hashAlgorithm = "2021512"; } else if (algorithmValue === "1.2.643.2.2.19") { hashAlgorithm = "3411"; } else { failCallback("Реализуемый алгоритм не подходит для подписания документа."); return; } $.ajax({ url: "/Services/SignService.asmx/GetHash", method: "POST", contentType: "application/json; charset=utf-8 ", dataType: "json", data: JSON.stringify({ //какие-то данные для определения документа //не забудем проверить на сервере имеет ли пользователь нужные права hashAlgorithm: hashAlgorithm, }), complete: function (response) { //получаем ответ от сервера, подписываем и отправляем подпись на сервер if (response.status === 200) { CryptographyObject.signHash(response.responseJSON, function(data) { $.ajax({ url: CryptographyObject.signServiceUrl, method: "POST", contentType: "application/json; charset=utf-8", dataType: "json", data: JSON.stringify({ Signature: data.Signature, //какие-то данные для определения файла //не забудем про серверную валидацию и авторизацию }), complete: function(response) { if (response.status === 200) successCallback(); else failCallback(); } }); }, certIndex); } else { failCallback(); } } }); }); } catch (exc) { failCallback(exc); } }

Комментарий: обратите внимание на cadesplugin.async_spawn, в нее передаётся функция-генератор, на которой последовательно вызывается next(), что приводит к переходу к yield.

Таким образом получается некий аналог async-await из C#. Всё выглядит синхронно, но работает асинхронно.

Теперь что происходит на сервере, когда у него запросили hash.

Во-первых необходимо установить nuget-пакет iTextSharp (на момент написания стать актуальная версия 5.5.13)

Во-вторых нужен CryptoPro.Sharpei, он идёт в нагрузку к Крипто ПРО .NET SDK

Теперь можно получать hash

 //определим hash-алгоритм HashAlgorithm hashAlgorithm; switch (hashAlgorithmName) { case "3411": hashAlgorithm = new Gost3411CryptoServiceProvider(); break; case "2021256": hashAlgorithm = new Gost3411_2021_256CryptoServiceProvider(); break; case "2021512": hashAlgorithm = new Gost3411_2021_512CryptoServiceProvider(); break; default: GetLogger().AddError("Неизвестный алгоритм хеширования", $"hashAlgorithmName: {hashAlgorithmName}"); return HttpStatusCode.BadRequest; } //получим hash в строковом представлении, понятном cadesplugin string hash; using (hashAlgorithm) //downloadResponse.RawBytes - просто массив байт исходного PDF файла using (PdfReader reader = new PdfReader(downloadResponse.RawBytes)) { //ищем уже существующие подписи int existingSignaturesNumber = reader.AcroFields.GetSignatureNames().Count; using (MemoryStream stream = new MemoryStream()) { //добавляем пустой контейнер для новой подписи using (PdfStamper st = PdfStamper.CreateSignature(reader, stream, '', null, true)) { PdfSignatureAppearance appearance = st.SignatureAppearance; //координаты надо менять в зависимости от существующего количества подписей, чтоб они не наложились друг на друга appearance.SetVisibleSignature(new Rectangle(36, 100, 164, 150), reader.NumberOfPages, //задаём имя поля, оно потом понадобиться для вставки подписи $"{SignatureFieldNamePrefix}{existingSignaturesNumber 1}"); //сообщаем, что подпись придёт извне ExternalBlankSignatureContainer external = new ExternalBlankSignatureContainer(PdfName.ADOBE_PPKLITE, PdfName.ADBE_PKCS7_DETACHED); //третий параметр - сколько места в байтах мы выделяем под подпись //я выделяю много, т.к. CAdES-X Long Type 1 содержит все сертификаты по цепочке до самого корневого центра MakeSignature.SignExternalContainer(appearance, external, 65536); //получаем поток, который содержит последовательность, которую мы хотим подписывать using (Stream contentStream = appearance.GetRangeStream()) { //вычисляем hash и переводим его в строку, понятную cadesplugin hash = string.Join(string.Empty, hashAlgorithm.ComputeHash(contentStream).Select(x => x.ToString("X2"))); } } //сохраняем stream куда хотим, он нам пригодиться, что бы вставить туда подпись } }

На клиенте, получив hash от сервера подписываем его

 //certIndex - индекс в массиве сертификатов. На основании именно этого сертификата мы получали алгоритм и формировали hash на сервере signHash: function (data, callback, certIndex, failCallback) { try { cadesplugin.async_spawn(function*() { certIndex = certIndex | 0; let oSigner = yield cadesplugin.CreateObjectAsync("CAdESCOM.CPSigner"); let cert = CryptographyObject.certificates[certIndex]; oSigner.propset_Certificate(cert); oSigner.propset_Options(cadesplugin.CAPICOM_CERTIFICATE_INCLUDE_WHOLE_CHAIN); //тут надо указать нормальный адрес TSP сервера. Это тестовый от Крипто ПРО oSigner.propset_TSAAddress("https://www.cryptopro.ru/tsp/"); let hashObject = yield cadesplugin.CreateObjectAsync("CAdESCOM.HashedData"); let certPublicKey = yield cert.PublicKey(); let certAlgorithm = yield certPublicKey.Algorithm; let algorithmValue = yield certAlgorithm.Value; if (algorithmValue === "1.2.643.7.1.1.1.1") { yield hashObject.propset_Algorithm(cadesplugin.CADESCOM_HASH_ALGORITHM_CP_GOST_3411_2021_256); oSigner.propset_TSAAddress(CryptographyObject.tsaAddress2021); } else if (algorithmValue === "1.2.643.7.1.1.1.2") { yield hashObject.propset_Algorithm(cadesplugin.CADESCOM_HASH_ALGORITHM_CP_GOST_3411_2021_512); oSigner.propset_TSAAddress(CryptographyObject.tsaAddress2021); } else if (algorithmValue === "1.2.643.2.2.19") { yield hashObject.propset_Algorithm(cadesplugin.CADESCOM_HASH_ALGORITHM_CP_GOST_3411); oSigner.propset_TSAAddress(CryptographyObject.tsaAddress2001); } else { alert("Невозможно подписать документ этим сертификатом"); return; } //в объект описания hash вставляем уже готовый hash с сервера yield hashObject.SetHashValue(data.Hash); let oSignedData = yield cadesplugin.CreateObjectAsync("CAdESCOM.CadesSignedData"); oSignedData.propset_ContentEncoding(cadesplugin.CADESCOM_BASE64_TO_BINARY); //результат подписания в base64 let signatureHex = yield oSignedData.SignHash(hashObject, oSigner, cadesplugin.CADESCOM_CADES_X_LONG_TYPE_1); data.Signature = signatureHex; callback(data); }); } catch (exc) { failCallback(exc); } }

Комментарий: полученную подпись отправляем на сервер (см. выше)

Ну и наконец вставляем подпись в документ на стороне сервера

//всякие нужные проверки //downloadResponse.RawBytes - ранее созданный PDF с пустым контейнером для подписи using (PdfReader reader = new PdfReader(downloadResponse.RawBytes)) { using (MemoryStream stream = new MemoryStream()) { //requestData.Signature - собственно подпись от клиента IExternalSignatureContainer external = new SimpleExternalSignatureContainer(Convert.FromBase64String(requestData.Signature)); //lastSignatureName - имя контейнера, которое мы определили при формировании hash MakeSignature.SignDeferred(reader, lastSignatureName, stream, external); //сохраняем подписанный файл } }

Комментарий: SimpleExternalSignatureContainer — это простейший класс, реализующий интерфейс IExternalSignatureContainer

 /// <summary> /// Простая реализация контейнера внешней подписи /// </summary> private class SimpleExternalSignatureContainer : IExternalSignatureContainer { private readonly byte[] _signedBytes; public SimpleExternalSignatureContainer(byte[] signedBytes) { _signedBytes = signedBytes; } public byte[] Sign(Stream data) { return _signedBytes; } public void ModifySigningDictionary(PdfDictionary signDic) { } }

Собственно с подписанием PDF на этом всё. Проверка будет описана в продолжении статьи. Надеюсь, она будет…

Внёс исправления из комментария о получении Oid алгоритма подписи. Спасибо

Примеры

Для их запуска необходим NodeJS LTS.

Пробный запуск

Для подписания возьмем PDF-документ, который содержит надпись “Тестовое заявление.”:

Больше для теста нам ничего не надо
Больше для теста нам ничего не надо

Далее запустим программу и дождемся подписания файла:

Готово. Теперь можно приступать к проверкам.

Проверка в криптоарм

Время создания ЭП заполнено:

Штамп времени на подпись есть:

Доказательства подлинности также заполнены:

В протоколе проверки есть блоки “Доказательства подлинности”, “Штамп времени на подпись” и “Время подписания”:

Проверка пакета перед публикацией в npm

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

npm run package
mv package ..

Важно переместить папку package куда-нибудь выше для избежания конфликтов при линковке с текущим package.json.

Переходим в любую директорию с примером и создаем там ссылку на только что собранный пакет:

Проверяем работу примеров в режимах разработки и продакшн.

После завершения экспериментов можно удалить глобальную ссылку из директории ../../../package таким образом:

Проверка работы примеров с react и angular

React и Angular используют версию сборки пакета в формате ES модулей из директории lib/.
Для их запуска необходимо сначала собрать пакет выполнив:

После этого из папки examples/angular или examples/react залинковать пакет:

И запустить пример в одном из двух режимов. В режиме разработки:

или в режиме продакшн:

npm run build
npm run serve

Расшифровка данных

/**
* Функция расшифровки бинарных данных
* @param EncryptedData шифр
*/
encrypt: function(EncryptedData)
{ var self = this; //Синтаксис языка javascript проверки ошибок, если не удастся создать объект cadescom.CPEnvelopedData try { // Создаем объект cadescom.CPEnvelopedData var oEnvelop = cadesplugin.CreateObject("cadescom.CPEnvelopedData"); } catch (err) { alert('Failed to create CAdESCOM.CPEnvelopedData: ' err.number); return; } // Указываем кодировку данных // Кодировка должна быть указана до того, как будут переданы сами данные oEnvelop.ContentEncoding = CADESCOM_BASE64_TO_BINARY; // Запуск процесса расшифровки данных oEnvelop.Decrypt(EncryptedData); //В результате расшифровки объект oEnvelop заполнит свойство Content - которое и будет содержать расшифрованные данные var sDecriptedData = oEnvelop.Content; return sDecriptedData;
},

Соберем проект с поддержкой гост р 34.11-2021 256 bit

Гайд разделен на несколько этапов. Основная инструкция по сборке опубликована вместе с репозиторием DotnetCoreSampleProject – периодически я буду на нее ссылаться.

Тем, кто хочет помочь

Буду благодарен за расширение/улучшение/доработку API.
Вам будут полезны примеры,
предоставляемые Крипто ПРО.

Установка

Для NPM:

Для Yarn:

Для Bower:

Подключение пакета как UMD модуля через тэг script:

Подключение пакета как ES модуля с Typescript или JavaScript:

Список требуемых полифиллов (если необходимы, подключаются самостоятельно):

  • Promise
  • Array.prototype.find

Установка криптопро csp в linux / osx

Процесс установки в OSX незначительно отличается от Linux, поэтому описание приведено на примере дистрибутива семейства Debian (x64).

Некоторые команды могут потребовать запуска с sudo.
Названия файлов и директорий могут отличаться из-за различий в версиях.

После загрузки КриптоПРО CSP для нужной платформы, распакуйте архив:

tar -xzvf linux-amd64_deb.tgz
chmod 777 -R linux-amd64_deb/

Запустите скрипт установки:

linux-amd64_deb/install.sh

Проверьте отсутствиеcprocsp-rdr-gui:

Установите дополнительно cprocsp-rdr-gui-gtk:

dpkg -i linux-amd64_deb/cprocsp-rdr-gui-gtk-64_4.0.0-4_amd64.deb

Дополнительная информация по установке

Установка криптопро эцп browser plugin в linux

Загрузите КриптоПРО ЭЦП browser plug-in и распакуйте его:

mkdir cades_linux_amd64
tar -xzvf cades_linux_amd64.tar.gz -C cades_linux_amd64

Сконвертируйте rpm в deb пакеты при помощи утилиты alien:

Установите пакеты:

dpkg -i cprocsp-pki-cades_2.0.0-2_amd64.deb
dpkg -i cprocsp-pki-plugin_2.0.0-2_amd64.deb

Проверьте наличие файлов плагина:

Установка сертификатов в linux

В OSX процесс схож с Linux.

Подключите USB носитель с ключевыми контейнерами и проверьте результат команды:

Скопируйте ключевой контейнер \.FLASH.sidorov на жесткий диск:

Наличие [ErrorCode: 0x00000000] в завершении каждой команды КриптоПРО говорит о ее успешном выполнении.

Проверьте наличие нового контейнера \.HDIMAGEsidor:

Установите личный сертификат:

Возможно в выводе вы ссылку на сертификат УЦ

При необходимости загрузите сертификат удостоверяющего центра и установите его командой:

После чего, при проверке установленного личного сертификата вы увидите PrivateKey Link: Yes:

/opt/cprocsp/bin/amd64/certmgr -list -store uMy

Шифроваение данных

/**
* Функция шифрования бинарных данных
* @param oCertificate выбранный сертификат
* @param data бинарные данные закодированные в base64 строку
*/
encrypt: function(oCertificate, data)
{ var self = this; //Синтаксис языка javascript проверки ошибок, если не удастся создать объект cadescom.CPEnvelopedData try { // Создаем объект cadescom.CPEnvelopedData var oEnvelop = cadesplugin.CreateObject("cadescom.CPEnvelopedData"); } catch (err) { alert('Failed to create CAdESCOM.CPEnvelopedData: ' err.number); return; } // Указываем кодировку данных // Кодировка должна быть указана до того, как будут переданы сами данные oEnvelop.ContentEncoding = CADESCOM_BASE64_TO_BINARY; // Предварительно закодированные в BASE64 бинарные данные oEnvelop.Content = data; //Установка сертификата для шифрования oEnvelop.Recipients.Add(self.Certificate); //Получаем зашифрованные данные return oEnvelop.Encrypt();
},

Это вообще законно?

С удовольствием узнаю ваше мнение в комментариях.

Оцените статью
ЭЦП Эксперт