📦 Современная обёртка над CryptoPro CAdES plugin
Позволяет удобно работать с сертификатами, получать информацию, подписывать данные (attached/detached) и добавлять метку времени — всё на async/await.
npm install @mmskazak/crypto-pro-plus
⚠️ Требуется установленный CryptoPro CSP и плагинcadesplugin.
- 🔍 Проверка версии плагина
- 📋 Получение списка сертификатов
- 🧾 Получение информации по сертификату
- ✍️ Detached/Attached подпись (CAdES-BES)
- ⏱ Подпись с меткой времени (CAdES-T)
- 👥 Множественные подписи (коллективные, последовательные)
- 🛡️ Контрподписи (заверение подписей)
- ✅ Проверка валидности цифровых подписей (включая множественные подписи)
- 🔐 Создание хешей (SHA-1/256/384/512, ГОСТ Р 34.11-94/2012-256/2012-512)
- ✍️ Подпись хешей любых алгоритмов (detached с поддержкой меток времени)
- 🔄 Конвертация Unicode-строк в корректный base64
Библиотека полностью организована по модульному принципу. Каждый модуль отвечает за свою область функциональности:
crypto-pro-plus/
├── src/
│ ├── common.js # Общие функции (pluginVersion, openCertificateStore)
│ ├── certificates.js # Работа с сертификатами
│ ├── signing.js # Подпись данных (attached/detached)
│ ├── multiple-signing.js # Множественные подписи (коллективные, последовательные)
│ ├── hashing.js # Создание хешей
│ ├── hash-signing.js # Подпись хешей
│ ├── verification.js # Проверка подписей
│ ├── countersigning.js # Контрподписи
│ └── utils.js # Утилиты (toBase64Unicode)
├── cadesplugin-wrapper.js
└── cadesplugin_api.js
// Работа с сертификатами
import { getCertificates, getCertificateInfo } from '@mmskazak/crypto-pro-plus/certificates';
// Подпись данных
import { signBase64Detached } from '@mmskazak/crypto-pro-plus/signing';
// Создание хешей
import { createSHA256Hash, createGost2012_256Hash } from '@mmskazak/crypto-pro-plus/hashing';
// Подпись хешей
import { signSHA256HashDetached } from '@mmskazak/crypto-pro-plus/hash-signing';
// Проверка подписей
import { verifyDetachedSignature } from '@mmskazak/crypto-pro-plus/verification';
// Множественные подписи
import { coSignBase64, createCollectiveSignature, createSequentialSignature } from '@mmskazak/crypto-pro-plus/multiple-signing';
// Контрподписи
import { counterSign } from '@mmskazak/crypto-pro-plus/countersigning';
// Утилиты
import { toBase64Unicode } from '@mmskazak/crypto-pro-plus/utils';
// Общие функции
import { pluginVersion } from '@mmskazak/crypto-pro-plus/common';import { pluginVersion } from '@mmskazak/crypto-pro-plus/common';
import { countCertificates } from '@mmskazak/crypto-pro-plus/certificates';
try {
const version = await pluginVersion();
console.log("Версия плагина:", version);
const count = await countCertificates();
console.log("Сертификатов найдено:", count);
} catch (error) {
// Плагин не установлен, хранилище недоступно и т.п.
console.error("Плагин КриптоПро недоступен:", error.message);
}import { getCertificates } from '@mmskazak/crypto-pro-plus/certificates';
const certs = await getCertificates();
certs.forEach(cert => {
console.log(cert.subjectName, cert.thumbprint, cert.validToDate);
});import { getCertificateByThumbprint, getCertificateInfo } from '@mmskazak/crypto-pro-plus/certificates';
const cert = await getCertificateByThumbprint("DA9142...");
const info = await getCertificateInfo(cert);
console.log(info);import { signBase64Detached } from '@mmskazak/crypto-pro-plus/signing';
import { toBase64Unicode } from '@mmskazak/crypto-pro-plus/utils';
const data = toBase64Unicode("Данные для подписи");
const signature = await signBase64Detached(data, "DA9142...");
console.log(signature);import { signBase64DetachedWithTimestamp } from '@mmskazak/crypto-pro-plus/signing';
const signature = await signBase64DetachedWithTimestamp(data, "DA9142...", "http://testca.cryptopro.ru/tsp/");
console.log(signature);import { signBase64Attached } from '@mmskazak/crypto-pro-plus/signing';
const signature = await signBase64Attached(data, "DA9142...");
console.log(signature);import { signBase64AttachedWithTimestamp } from '@mmskazak/crypto-pro-plus/signing';
const signature = await signBase64AttachedWithTimestamp(data, "DA9142...", "http://testca.cryptopro.ru/tsp/");
console.log(signature);import { signWithCertificateSelection } from '@mmskazak/crypto-pro-plus/signing';
import { toBase64Unicode } from '@mmskazak/crypto-pro-plus/utils';
const data = toBase64Unicode("Важный документ");
// Пользователю покажется диалог выбора сертификата
const result = await signWithCertificateSelection(
data,
true, // isDetached
"http://testca.cryptopro.ru/tsp/" // опционально TSP
);
console.log('Подпись:', result.signature);
console.log('Сертификат:', result.certificateInfo.subjectName);
console.log('Предупреждения:', result.certificateInfo.warnings);import { signWithValidation, validateCertificateForSigning } from '@mmskazak/crypto-pro-plus/signing';
// Сначала проверим сертификат
const validation = await validateCertificateForSigning("DA9142...");
if (!validation.isValid) {
console.error('Сертификат недействителен:', validation.reason);
return;
}
// Подписываем с проверкой
try {
const result = await signWithValidation(data, "DA9142...", true);
console.log('Подпись создана:', result.signature);
} catch (error) {
console.error('Ошибка подписания:', error.message);
}import {
createGost2012_256Hash,
createSHA256Hash,
createSHA512Hash,
createHash
} from '@mmskazak/crypto-pro-plus/hashing';
import { toBase64Unicode } from '@mmskazak/crypto-pro-plus/utils';
const data = toBase64Unicode("Данные для хеширования");
// ГОСТ Р 34.11-2012 256 бит
const gost256Hash = await createGost2012_256Hash(data);
// SHA-256
const sha256Hash = await createSHA256Hash(data);
// SHA-512
const sha512Hash = await createSHA512Hash(data);
// Универсальная функция с любым алгоритмом
import { cadesplugin } from '@mmskazak/crypto-pro-plus/cadesplugin-wrapper';
const customHash = await createHash(data, cadesplugin.CADESCOM_HASH_ALGORITHM_SHA_384);import {
signGost2012_256HashDetached,
signSHA256HashDetached,
signHashDetached
} from '@mmskazak/crypto-pro-plus/hash-signing';
// Подпись ГОСТ-хеша
const gost256Signature = await signGost2012_256HashDetached(gost256Hash, "DA9142...");
// Подпись SHA-256 хеша
const sha256Signature = await signSHA256HashDetached(sha256Hash, "DA9142...");
// Универсальная функция
import { cadesplugin } from '@mmskazak/crypto-pro-plus/cadesplugin-wrapper';
const customSignature = await signHashDetached(
customHash,
"DA9142...",
cadesplugin.CADESCOM_HASH_ALGORITHM_SHA_384
);import {
signGost2012_256HashDetachedWithTimestamp,
signSHA256HashDetachedWithTimestamp,
signHashDetachedWithTimestamp
} from '@mmskazak/crypto-pro-plus/hash-signing';
// ГОСТ с меткой времени
const gost256Signature = await signGost2012_256HashDetachedWithTimestamp(
gost256Hash,
"DA9142...",
"http://testca.cryptopro.ru/tsp/"
);
// SHA-256 с меткой времени
const sha256Signature = await signSHA256HashDetachedWithTimestamp(
sha256Hash,
"DA9142...",
"http://testca.cryptopro.ru/tsp/"
);
// Универсальная функция с меткой времени
import { cadesplugin } from '@mmskazak/crypto-pro-plus/cadesplugin-wrapper';
const customSignature = await signHashDetachedWithTimestamp(
customHash,
"DA9142...",
cadesplugin.CADESCOM_HASH_ALGORITHM_SHA_384,
"http://testca.cryptopro.ru/tsp/"
);import {
verifyDetachedSignature,
verifyAttachedSignature,
verifyTimestampedSignature,
getSignersInfo
} from '@mmskazak/crypto-pro-plus/verification';
// Проверка detached подписи
const isValidDetached = await verifyDetachedSignature(originalData, signature);
console.log('Detached подпись валидна:', isValidDetached);
// Проверка attached подписи
const attachedResult = await verifyAttachedSignature(signedMessage);
console.log('Attached подпись валидна:', attachedResult.isValid);
if (attachedResult.isValid) {
console.log('Извлеченные данные:', attachedResult.content);
}
// Проверка подписи с меткой времени
const timestampResult = await verifyTimestampedSignature(originalData, signature, true);
console.log('Подпись с меткой времени валидна:', timestampResult.isValid);
if (timestampResult.timestampInfo) {
console.log('Время подписи:', timestampResult.timestampInfo.signingTime);
}
// Получение информации о всех подписчиках (может быть несколько!)
const signersInfo = await getSignersInfo(signature, true, originalData);
console.log(`Найдено подписчиков: ${signersInfo.length}`);
signersInfo.forEach((signer, index) => {
console.log(`\n--- Подписчик ${index + 1} ---`);
console.log('Имя:', signer.subjectName);
console.log('Издатель сертификата:', signer.issuerName);
console.log('Отпечаток сертификата:', signer.thumbprint);
console.log('Время подписи:', signer.signingTime);
console.log('Сертификат действителен с:', signer.validFromDate);
console.log('Сертификат действителен до:', signer.validToDate);
});import {
coSignBase64,
coSignBase64WithTimestamp,
createMultipleSignature,
createSequentialSignature
} from '@mmskazak/crypto-pro-plus/multiple-signing';
// Добавление соподписи к существующей подписи
const coSignedData = await coSignBase64(
originalData,
existingSignature,
secondSignerThumbprint,
true // isDetached
);
// Создание коллективной подписи сразу несколькими подписчиками
const thumbprints = ["cert1_thumbprint", "cert2_thumbprint", "cert3_thumbprint"];
// Коллективная подпись (все подписывают исходные данные одновременно)
const collectiveSignature = await createCollectiveSignature(
originalData,
thumbprints,
true, // isDetached
"http://testca.cryptopro.ru/tsp/" // опционально TSP для всех
);
// Последовательная подпись (цепочка: каждый подписывает результат предыдущего)
const signers = [
{ thumbprint: "manager_cert", tspUrl: "http://testca.cryptopro.ru/tsp/" },
{ thumbprint: "director_cert", tspUrl: "http://testca.cryptopro.ru/tsp/" },
{ thumbprint: "accountant_cert" } // без TSP
];
const sequentialResult = await createSequentialSignature(originalData, signers, true);
console.log('Финальная подпись:', sequentialResult.signature);
console.log('История подписания:', sequentialResult.history);import {
counterSign,
counterSignBySigner,
counterSignAll,
getCounterSignersInfo
} from '@mmskazak/crypto-pro-plus/countersigning';
// Создание контрподписи (заверение первого подписчика)
const counterSignedData = await counterSign(
existingSignature,
notaryThumbprint,
"http://testca.cryptopro.ru/tsp/" // опционально
);
// Контрподпись конкретного подписчика (по индексу)
const counterSignedSpecific = await counterSignBySigner(
existingSignature,
notaryThumbprint,
2, // индекс подписчика
"http://testca.cryptopro.ru/tsp/"
);
// Контрподпись всех подписчиков
const counterSignedAll = await counterSignAll(
existingSignature,
notaryThumbprint,
"http://testca.cryptopro.ru/tsp/"
);
// Получение информации о контрподписях
const counterSignersInfo = await getCounterSignersInfo(signature, originalData, true);
counterSignersInfo.forEach(info => {
console.log(`Подписчик: ${info.signerName}`);
info.counterSignatures.forEach(cs => {
console.log(` Заверил: ${cs.counterSignerName} в ${cs.counterSigningTime}`);
});
});| Алгоритм | Константа | Функции |
|---|---|---|
| SHA-1 | CADESCOM_HASH_ALGORITHM_SHA1 |
createSHA1Hash, signSHA1HashDetached* |
| SHA-256 | CADESCOM_HASH_ALGORITHM_SHA_256 |
createSHA256Hash, signSHA256HashDetached* |
| SHA-384 | CADESCOM_HASH_ALGORITHM_SHA_384 |
createSHA384Hash, signSHA384HashDetached* |
| SHA-512 | CADESCOM_HASH_ALGORITHM_SHA_512 |
createSHA512Hash, signSHA512HashDetached* |
| ГОСТ Р 34.11-94 | CADESCOM_HASH_ALGORITHM_CP_GOST_3411 |
createGost94Hash, signGost94HashDetached* |
| ГОСТ Р 34.11-2012 256 | CADESCOM_HASH_ALGORITHM_CP_GOST_3411_2012_256 |
createGost2012_256Hash, signGost2012_256HashDetached* |
| ГОСТ Р 34.11-2012 512 | CADESCOM_HASH_ALGORITHM_CP_GOST_3411_2012_512 |
createGost2012_512Hash, signGost2012_512HashDetached* |
Примечание:
*означает, что доступны варианты как без метки времени, так и с меткой времени (WithTimestamp)
По умолчанию библиотека пишет диагностические сообщения через console (debug/info/warn/error). Это поведение можно подменить своим логгером или отключить полностью:
import { setLogger } from '@mmskazak/crypto-pro-plus/logger';
// Свой логгер (например, обёртка над winston/pino) — можно переопределить только часть методов
setLogger({
error: (...args) => myLogger.error(...args),
});
// Полностью отключить вывод
setLogger(null);
// Вернуть логирование в console
setLogger();Обработка ошибок: функции получения данных (
pluginVersion,countCertificates,getCertificatesи т.п.) и функции подписания выбрасывают исключение, если что-то пошло не так (плагин недоступен, сертификат не найден и т.д.) — оборачивайте вызовы вtry/catch. Функции проверки/валидации (verify*,validateCertificateForSigning,getSignersInfo,getCounterSignersInfo) исключений не бросают — они возвращают структурированный результат (false/{ isValid: false }/[]), потому что невалидная подпись или сертификат — ожидаемый результат проверки, а не ошибка.
| Метод | Описание |
|---|---|
| pluginVersion() | Возвращает версию установленного плагина |
| countCertificates() | Считает количество сертификатов в личном хранилище |
| getCertificates() | Получает список сертификатов с SubjectName, Thumbprint, сроками и объектом |
| getCertificateByThumbprint(thumbprint) | Возвращает объект сертификата по отпечатку |
| getCertificateInfo(cert) | Возвращает подробную информацию о сертификате |
| selectCertificateFromDialog(title) | Показывает диалог выбора сертификата пользователю |
| getValidCertificates() | Получает список всех действующих сертификатов |
| validateCertificateForSigning(thumbprint) | Проверяет может ли сертификат использоваться для подписания |
| signBase64Detached(dataBase64, thumbprint) | Detached-подпись без метки времени (CAdES-BES) |
| signBase64DetachedWithTimestamp(dataBase64, thumbprint, tspUrl) | Detached-подпись с меткой времени (CAdES-T) |
| signBase64Attached(dataBase64, thumbprint) | Attached-подпись без метки времени (CAdES-BES) |
| signBase64AttachedWithTimestamp(dataBase64, thumbprint, tspUrl) | Attached-подпись с меткой времени (CAdES-T) |
| signWithCertificateSelection(dataBase64, isDetached, tspUrl) | Подпись с выбором сертификата через диалог |
| signWithValidation(dataBase64, thumbprint, isDetached, tspUrl) | Безопасное подписание с проверкой сертификата |
| Функции хеширования | |
| createHash(dataBase64, algorithm) | Универсальная функция создания хеша с любым алгоритмом |
| createGost2012_256Hash(dataBase64) | Создает ГОСТ-хеш данных (ГОСТ Р 34.11-2012 256 бит) |
| createSHA1Hash(dataBase64) | Создает SHA-1 хеш данных |
| createSHA256Hash(dataBase64) | Создает SHA-256 хеш данных |
| createSHA384Hash(dataBase64) | Создает SHA-384 хеш данных |
| createSHA512Hash(dataBase64) | Создает SHA-512 хеш данных |
| createGost2012_512Hash(dataBase64) | Создает ГОСТ Р 34.11-2012 512 бит хеш данных |
| createGost94Hash(dataBase64) | Создает ГОСТ Р 34.11-94 хеш данных |
| Функции подписи хешей | |
| signHashDetached(hashBase64, thumbprint, algorithm) | Универсальная detached-подпись хеша без метки времени |
| signHashDetachedWithTimestamp(hashBase64, thumbprint, algorithm, tspUrl) | Универсальная detached-подпись хеша с меткой времени |
| signGost2012_256HashDetached(hashBase64, thumbprint) | Detached-подпись ГОСТ 2012-256 хеша без метки времени (CAdES-BES) |
| signGost2012_256HashDetachedWithTimestamp(hashBase64, thumbprint, tspUrl) | Detached-подпись ГОСТ 2012-256 хеша с меткой времени (CAdES-T) |
| signSHA1HashDetached(hashBase64, thumbprint) | Detached-подпись SHA-1 хеша без метки времени |
| signSHA1HashDetachedWithTimestamp(hashBase64, thumbprint, tspUrl) | Detached-подпись SHA-1 хеша с меткой времени |
| signSHA256HashDetached(hashBase64, thumbprint) | Detached-подпись SHA-256 хеша без метки времени |
| signSHA256HashDetachedWithTimestamp(hashBase64, thumbprint, tspUrl) | Detached-подпись SHA-256 хеша с меткой времени |
| signSHA384HashDetached(hashBase64, thumbprint) | Detached-подпись SHA-384 хеша без метки времени |
| signSHA384HashDetachedWithTimestamp(hashBase64, thumbprint, tspUrl) | Detached-подпись SHA-384 хеша с меткой времени |
| signSHA512HashDetached(hashBase64, thumbprint) | Detached-подпись SHA-512 хеша без метки времени |
| signSHA512HashDetachedWithTimestamp(hashBase64, thumbprint, tspUrl) | Detached-подпись SHA-512 хеша с меткой времени |
| signGost2012_512HashDetached(hashBase64, thumbprint) | Detached-подпись ГОСТ 2012-512 хеша без метки времени |
| signGost2012_512HashDetachedWithTimestamp(hashBase64, thumbprint, tspUrl) | Detached-подпись ГОСТ 2012-512 хеша с меткой времени |
| signGost94HashDetached(hashBase64, thumbprint) | Detached-подпись ГОСТ-94 хеша без метки времени |
| signGost94HashDetachedWithTimestamp(hashBase64, thumbprint, tspUrl) | Detached-подпись ГОСТ-94 хеша с меткой времени |
| Функции множественных подписей | |
| coSignBase64(dataBase64, existingSignature, thumbprint, isDetached) | Добавляет соподпись к существующей подписи |
| coSignBase64WithTimestamp(dataBase64, existingSignature, thumbprint, tspUrl, isDetached) | Добавляет соподпись с меткой времени |
| createCollectiveSignature(dataBase64, thumbprints, isDetached, tspUrl) | Создает коллективную подпись (все подписывают исходные данные) |
| createSequentialSignature(dataBase64, signers, isDetached) | Создает последовательную подпись (цепочка подписей) |
| createMultipleSignature(dataBase64, thumbprints, isDetached, tspUrl) | Алиас для createCollectiveSignature (обратная совместимость) |
| Функции проверки подписей | |
| verifyDetachedSignature(dataBase64, signatureBase64) | Проверяет detached подпись CAdES |
| verifyAttachedSignature(signatureBase64) | Проверяет attached подпись CAdES |
| verifyTimestampedSignature(dataBase64, signatureBase64, isDetached) | Проверяет подпись с меткой времени CAdES-T |
| getSignersInfo(signatureBase64, isDetached, dataBase64) | Получает информацию о всех подписчиках (поддержка множественных подписей) |
| verifySignature(signatureBase64, options) | Универсальная функция проверки подписи |
| Функции контрподписей | |
| counterSign(existingSignature, thumbprint, tspUrl) | Создает контрподпись (заверение подписи) |
| counterSignBySigner(existingSignature, thumbprint, signerIndex, tspUrl) | Контрподпись конкретного подписчика |
| counterSignAll(existingSignature, thumbprint, tspUrl) | Контрподпись всех подписчиков |
| getCounterSignersInfo(signatureBase64, dataBase64, isDetached) | Получает информацию о контрподписях |
| Утилиты | |
| toBase64Unicode(str) | Кодирует строку в корректный base64 с поддержкой Unicode |
npm install # установка зависимостей
npm test # запуск тестов (Vitest, мок cadesplugin — реальный плагин не нужен)
npm run lint # проверка стиля кода
npm run format # автоформатирование (Prettier)CI (GitHub Actions) прогоняет lint и test на Node 20.x/22.x при каждом push в master и на каждый pull request.
Михаил Мельников — github.com/mmskazak
MIT