Привет, Хабр! Меня зовут Олеся Лазарева, я работаю старшим разработчиком в команде PIlot.
В этой статье мы рассмотрим создание плагина криптопровайдера для веб-редакции системы управления инженерными данными Pilot-ICE Enterprise. Веб-редакция Pilot-ICE Enterprise работает с плагинами, которые используют формат электронной подписи CADES-BES. Данные плагины поставляются в составе Ascon.Pilot.Web.SDK. Это покрывает потребности большинства российских организаций, работающих в соответствии с требованиями ГОСТ.
Однако на практике могут возникнуть ситуации, когда необходимо:
Использовать альтернативные криптопровайдеры (ruToken, JaCarta, Aktiv и др.);
Поддержать специфические форматы подписи, требуемые заказчиком;
Интегрироваться с корпоративными системами электронного документооборота;
Работать с международными стандартами подписи (PAdES, XAdES и т.д.).
Для таких случаев веб-версия Pilot-ICE Enterprise предоставляет возможность создания собственных плагинов-криптопровайдеров через открытый SDK.
Для демонстрации основных концепций разработки мы реализуем простой плагин для подписания электронной цифровой подписью на базе хеш-функции FNV-1a.
Важно: FNV-1a не является криптостойким алгоритмом и используется здесь исключительно в образовательных целях. Для production-среды необходимо использовать сертифицированные криптографические алгоритмы.

Архитектура плагина
Плагин представляет собой TypeScript-модуль, который подключается к веб-версии Pilot-ICE Enterprise через механизм Module Federation.
Основной класс CryptoProviderFnv1Extension реализует интерфейс ICryptoProvider и предоставляет методы для:
Получения списка сертификатов (
getCertificates)Создания электронной подписи документа (
sign)Проверки подписи (
verify,verifyImportedSignature)Определения поддерживаемых алгоритмов (
canProcessAlgorithms)
Основные компоненты:
CryptoProviderFnv1Extension — главный класс, реализующий интерфейс
ICryptoProviderFileSignatureUpdater — утилита для обновления подписей в объектах документов
Вспомогательные функции — работа с кодировками и преобразованиями данных
Структура проекта
Перед началом разработки рассмотрим организацию файлов в проекте:
cryptoprovider.fnv1.sample/ │ ├── src/ │ ├── app/ │ │ ├── cryptoProviderFnv1Extension.ts # Главный класс плагина │ │ ├── file-signature.updater.ts # Логика обновления подписей │ │ ├── signature-date-object.interface.ts # Интерфейс данных подписи │ │ └── utils.ts # Вспомогательные функции │ │ │ ├── assets/ │ │ └── extensions.config.json # Манифест плагина │ │ │ └── index.ts # Точка входа (пустой файл) │ ├── dist/ # Директория сборки (генерируется) │ ├── main.js # Скомпилированный модуль │ └── extensions.config.json # Скопированный манифест │ ├── package.json # Конфигурация npm и зависимости ├── tsconfig.json # Конфигурация TypeScript ├── webpack.config.js # Конфигурация Webpack (dev) └── webpack.prod.config.js # Конфигурация Webpack (production)
Назначение ключевых файлов:
Исходный код (src/):
cryptoProviderFnv1Extension.ts— основная логика криптопровайдера, реализация всех методов интерфейсаICryptoProviderfile-signature.updater.ts— класс для работы с Pilot ICE SDK API, модификация объектов документовsignature-date-object.interface.ts— TypeScript-интерфейс для типизации данных подписиutils.ts— функции для преобразования между ArrayBuffer, строками и Base64index.ts— формальная точка входа для Webpack (может быть пустым)
Конфигурация (assets/):
extensions.config.json— манифест, описывающий метаданные плагина и экспортируемые модули
Поток данных в приложении (Data Flow Diagram):
┌──────────────────────┐ │ веб-версия │ │ Pilot-ICE Enterprise│ └──────────┬───────────┘ │ ▼ ┌──────────────────────────────┐ │ CryptoProviderFnv1 │ │ Extension │ └─────┬────────────────┬───────┘ │ │ ▼ ▼ ┌─────────────┐ ┌──────────────┐ │ sign() │ │ verify() │ └──────┬──────┘ └──────┬───────┘ │ │ ▼ ▼ ┌──────────────┐ ┌──────────────────┐ │ FNV1a Hash │ │ Декодирование │ │ Calculation │ │ подписи │ └──────┬───────┘ └──────┬───────────┘ │ │ ▼ ▼ ┌──────────────┐ ┌──────────────────┐ │ Base64 │ │ Сравнение │ │ Encoding │ │ хешей │ └──────┬───────┘ └──────┬───────────┘ │ │ ▼ ▼ ┌──────────────────┐ ┌─────────────────┐ │ FileSignature │ │ Результат │ │ Updater │ │ проверки │ └──────┬───────────┘ └─────────────────┘ │ ▼ ┌──────────────────┐ │ IModifier API │ └──────┬───────────┘ │ ▼ ┌──────────────────────┐ │ Сохранение в │ │ веб-версии Pilot-ICE │ └──────────────────────┘
Эта структура обеспечивает четкое разделение ответственности: логика криптографии отделена от работы с API системы, а утилиты вынесены в отдельный модуль.
Шаг 1: Инициализация проекта
Создание package.json
Начнем с настройки зависимостей проекта:
Создайте новый angular-проект командой. Чтобы создать angular-расширение, выполните следующие действия:
ng new cryptoprovider.fnv1.sample
Выполните команды в окружении проекта:
npm init npm install @pilotdev/pilot-web-sdk npm install @angular-architects/module-federation --save-dev
{ "name": "cryptoprovider.fnv1.sample", "version": "1.0.0", "main": "index.js", "scripts": { "build": "webpack", "build-prod": "webpack --config webpack.prod.config.js", "start": "webpack serve" }, "dependencies": { "@pilotdev/pilot-web-sdk": "^25.13.0", "rxjs": "7.8.1" }, "devDependencies": { "@types/jsrsasign": "10.5.15", "copy-webpack-plugin": "13.0.0", "ts-loader": "9.5.2", "typescript": "5.8.2", "webpack": "5.98.0", "webpack-cli": "6.0.1" } }
Ключевые зависимости:
@pilotdev/pilot-web-sdk— SDK для разработки плагиновrxjs— библиотека для работы с асинхронными операциями через реактивное программирование. Используется для обработки потоков данных (Observable), что позволяет элегантно работать с асинхронными операциями подписания и проверки документов. Все методы SDK возвращают Observable, что обеспечивает единообразный подход к обработке асинхронностиwebpack— для сборки модуля
Настройка TypeScript
Создайте tsconfig.json с базовыми настройками:
Добавьте в корневую папку файл tsconfig.json командой
npx tsc --init
{ "compilerOptions": { "target": "es2016", "module": "commonjs", "esModuleInterop": true, "forceConsistentCasingInFileNames": true, "strict": true, "skipLibCheck": true } }
Шаг 2: Конфигурация Webpack
Webpack используется для создания модуля, который можно загрузить в веб-версию Pilot-ICE Enterprise. Ключевой момент — использование Module Federation Plugin.
webpack.config.js
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin'); const CopyPlugin = require("copy-webpack-plugin"); module.exports = [{ mode: "development", entry: {main: './src/index.ts'}, module: { rules: [{ test: /\.tsx?$/, use: 'ts-loader', exclude: /node_modules/ }] }, resolve: { extensions: [".tsx", ".ts", ".jsx", ".js", ".json"], }, output: { publicPath: 'auto', uniqueName: 'cryptoprovider_fnv1', scriptType: 'text/javascript', filename: '[name].js', clean: true }, optimization: { runtimeChunk: false }, plugins: [ new ModuleFederationPlugin({ name: 'cryptoprovider_fnv1', library: { type: 'var', name: '[name]' }, filename: '[name].js', exposes: [{ ICryptoProvider: './src/app/cryptoProviderFnv1Extension.ts' }], shared: { '@pilotdev/pilot-web-sdk': { singleton: true, } } }), new CopyPlugin({ patterns: [ { from: "./src/assets/extensions.config.json", to: "extensions.config.json" } ], }), ], devServer: { port: 4300, allowedHosts: 'auto', headers: { 'Access-Control-Allow-Origin': '*', "Access-Control-Allow-Credentials": "true", "Access-Control-Allow-Methods": "GET,HEAD,OPTIONS,POST,PUT", "Access-Control-Allow-Headers": "Origin, X-Requested-With, Content-Type, Accept, Authorization, x-time-zone-offset" }, }, }]
Важные моменты конфигурации:
ModuleFederationPlugin — позволяет экспортировать модуль для динамической загрузки:
name— уникальное имя модуляexposes— какие модули экспортируются (наш криптопровайдер)shared— общие зависимости с хост-приложением (SDK должен быть singleton)
CopyPlugin — копирует конфигурационный файл
extensions.config.jsonв output-директориюdevServer — настройки для разработки с CORS-заголовками:
port: 4300— порт, на котором будет запущен dev-сервер для локальной разработкиallowedHosts: 'auto'— автоматически разрешает подключения с любых хостовheaders— набор HTTP-заголовков для работы Cross-Origin Resource Sharing (CORS):Access-Control-Allow-Origin: '*'— разрешает запросы с любых доменов (для production следует указать конкретный домен)Access-Control-Allow-Credentials: "true"— позволяет передавать cookies и авторизационные данные в кросс-доменных запросахAccess-Control-Allow-Methods— разрешенные HTTP-методы для кросс-доменных запросовAccess-Control-Allow-Headers— список заголовков, которые клиент может отправить в запросе
Эти настройки необходимы, так как плагин будет загружаться с отдельного dev-сервера (localhost:4300) в веб-версию Pilot-ICE Enterprise, работающую на другом порту или домене. Без правильных CORS-заголовков браузер заблокирует загрузку модуля по соображениям безопасности.
Шаг 3: Манифест плагина
Создайте src/assets/extensions.config.json:
{ "$schema": "node_modules/@pilotdev/pilot-web-sdk/extensions.config.schema.json", "manifestVersion": 1, "author": "ASCON JSC", "license": "MIT", "title": "cryptoprovider.fnv1", "description": "Educational plugin demonstrating the creation of a custom cryptographic provider using FNV-1a hashing algorithm for document signing and verification.", "version": "1.0.0", "extension": { "name": "cryptoprovider_fnv1", "entry": "cryptoprovider_fnv1.js", "modules": [{ "ngModuleName": "CryptoProviderFnv1Extension", "exposedInterface": "ICryptoProvider" }] } }
Этот файл описывает метаданные плагина и указывает веб-версии Pilot-ICE Enterprise, какой интерфейс реализует модуль.
Шаг 4: Главный класс и вспомогательные файлы
Для полной функциональности плагина необходимо создать вспомогательные модули:
src/app/utils.ts— функции для преобразования между ArrayBuffer, строками и Base64src/app/signature-date-object.interface.ts— TypeScript-интерфейс для типизации данных подписиsrc/app/file-signature.updater.ts— класс для работы с веб-версией Pilot-ICE Enterprise SDK API и модификации объектов документовsrc/app/cryptoProviderFnv1Extension.ts— главный класс плагина с реализацией всех методов интерфейсаICryptoProvider
Полную реализацию этих файлов можно посмотреть в репозитории на GitHub: https://github.com/AlexLazareva/cryptoprovider-fnv1-sample
Шаг 5: Точка входа
Создайте пустой src/index.ts — он нужен как entry point для webpack, но весь функционал находится в cryptoProviderFnv1Extension.ts, который экспортируется через Module Federation.
Шаг 6: Сборка и запуск
Режим разработки
npm install npm start
Webpack запустит dev-сервер на порту 4300 с hot-reload.
Production-сборка
npm run build-prod
Файлы будут собраны в директорию dist/:
main.js— скомпилированный модульextensions.config.json— манифест плагина
Как это работает
Подписание документа:
Пользователь выбирает документ и инициирует подписание
Веб-версия Pilot-ICE Enterprise вызывает
getCertificates()для получения списка сертификатовПосле выбора сертификата вызывается
sign()Метод вычисляет FNV-1a хеш файла
Создается JSON с метаданными (хеш, дата, субъект, издатель)
JSON кодируется в base64
Через
FileSignatureUpdaterсоздается файл подписи и привязывается к документу
Проверка подписи:
Веб-версия Pilot-ICE Enterprise вызывает
verify()илиverifyImportedSignature()Декодируется файл подписи
Вычисляется хеш текущего файла
Сравнивается с хешем из подписи
Возвращается результат с статусом и метаданными
Заключение
Мы создали полноценный плагин криптопровайдера для веб-версии Pilot-ICE Enterprise:
✅ Настроили webpack с Module Federation для динамической загрузки
✅ Реализовали все методы интерфейса ICryptoProvider
✅ Использовали SDK API для модификации объектов документов
✅ Создали систему проверки подписей с кастомными состояниями
Этот пример демонстрирует архитектуру плагинов веб-версии Pilot-ICE Enterprise и может служить основой для разработки плагинов для подписания электронной подписью с реальными криптографическими библиотеками (CryptoPro CSP, КриптоПро ЭЦП Browser Plugin, ruToken и др.).
Полный код проекта доступен в репозитории на GitHub: https://github.com/AlexLazareva/cryptoprovider-fnv1-sample
Дальнейшее развитие
Для production-использования необходимо:
Интегрировать сертифицированную криптографическую библиотеку
Добавить обработку ошибок и валидацию сертификатов
Реализовать проверку цепочки доверия
Добавить поддержку различных форматов подписи (CAdES-BES, CAdES-T и т.д.)
Настроить CI/CD для автоматической сборки и тестирования
Ссылки:
SDK Documentation — Центр загрузок АСКОН, "SDK, комплект разработчика"
RxJS Documentation — руководство по операторам и API Reference
