- Что имеем на входе?
- Важное вступление
- Так, а что надо на выходе?
- Angular (es modules typescript)
- Cryptopro
- Done.
- Ii – сборка проекта со сборкой corefx для windows
- Post /pki/verify¶
- Вычисление хэш значения
- Вычисление хэш значения бинарных данных (его и используем в проекте)
- Запуск режима разработки
- Запуск тестов
- Зачем мне этот пакет?
- Интерфейс программирования приложений
- Лицензия
- Методы объекта сертификата
- Миграция с версии 1 на 2
- Настройка плагина для firefox (до версии 52)
- Немного покодим
- Первым делом создадим новую папку
- Поддерживаемые скзи
- Подписание pdf на js и вставка подписи на c#, используя крипто про
- Примеры
- Пробный запуск
- Проверка в криптоарм
- Проверка пакета перед публикацией в npm
- Проверка работы примеров с react и angular
- Расшифровка данных
- Соберем проект с поддержкой гост р 34.11-2021 256 bit
- Тем, кто хочет помочь
- Установка
- Установка криптопро csp в linux / osx
- Установка криптопро эцп browser plugin в linux
- Установка сертификатов в linux
- Шифроваение данных
- Это вообще законно?
Что имеем на входе?
КриптоПро CSP версии 5.0 – для поддержки Российских криптографических алгоритмов (подписи, которые выпустили в аккредитованном УЦ в РФ)
КриптоПро TSP Client 2.0 – нужен для штампа времени
КриптоПро OCSP Client 2.0 – проверит не отозван ли сертификат на момент подписания
КриптоПро .NET Client – таков путь
Любой сервис по проверке ЭП – я использовал Контур.Крипто как основной сервис для проверки ЭП и КриптоАРМ как локальный. А еще можно проверить ЭП на сайте Госуслуг
КЭП по ГОСТ Р 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 со штампом времени на подпись и доказательством подлинности. Для этого зададим следующие критерии:
ЭП проходит проверку на портале Госуслуг, через сервис для подтверждения подлинности ЭП формата PKCS#7 в электронных документах;
КриптоАРМ после проверки подписи
Заполнит поле “Время создания ЭП” – в конце проверки появится окно, где можно выбрать ЭП и кратко посмотреть ее свойства
В информации о подписи и сертификате (двойной клик по записе в таблице) на вкладке “Штампы времени” в выпадающем списке есть оба значения и по ним заполнена информация:
Подпись:
Доказательства подлинности:
В протоколе проверки подписи есть блоки “Доказательства подлинности”, “Штамп времени на подпись” и “Время подписания”. Для сравнения: если документ подписан просто КЭП, то отчет по проверке будет достаточно коротким в сравнении с УКЭП.
Контур.Крипто при проверке подписи выдаст сообщение, что совершенствованная подпись подтверждена, сертификат на момент подписания действовал и указано время создания подпись:
Angular (es modules typescript)
Запуск в режиме разработки:
Запуск в продакшн режиме:
npm run build
npm run serve
Cryptopro
Единое, асинхронное API для взаимодействия с КриптоПРО ЭЦП Browser Plug-In
Done.
Гайд написан с исследовательской целью – проверить возможность подписания документов УКЭП с помощью самописного сервиса на .NET Core 3.1 с формированием штампов подлинности и времени подписания документов.
Безусловно это решение не стоит брать в работу “как есть” и нужны некоторые доработки, но в целом оно работает и подписывает документы подписью УКЭП.
Ii – сборка проекта со сборкой corefx для windows
Выполните 1-3 и 6-й шаги из I этапа;
Склонируйте репозиторий corefx в .
Выполните сборку запустив .corefxbuild.cmd – на этом этапе потребуется предустановленный DIA SDK
Выполните шаги 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, в папке
- В 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 напишут сами (мне пришлось «через не хочу»).
Что нужно:
- Пользователь должен получить пару ключей и сертификат.
- Пользователь должен установить plug-in от Крипто ПРО. Без этого средствами JS мы не сможем работать с криптопровайдером.
Замечания:
- Для тестов у меня был сертификат выданный тестовым ЦС Крипто ПРО и нормальный токен, полученный одним из наших сотрудников (на момент написания статьи ~1500р с годовой лицензией на Крипто ПРО и двумя сертификатами: но «новому» и «старому» ГОСТ)
- Говорят, 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();
},
Это вообще законно?
С удовольствием узнаю ваше мнение в комментариях.