Возникла проблема при подписании файлов при работе с физическими токенами.
У нас есть достаточно большое количество PDF и XML-файлов. Для каждого из этих файлов мы создаём по две откреплённые ЭЦП - условно, подпись конкретного пользователя и подпись организации.
Само подписание реализуем через
КриптоПро CSP и
КриптоПро ЭЦП Browser plug-in.
Проблема заключается в следующем: если при подписании использовать сертификат, установленный в локальное хранилище на компьютере - все подписи создаются быстро, никаких проблем нет. Выходит примерно 10-40 мс на создание каждой откреплённой подписи.
Но если использовать физические токены (например, сертификат на каком-нибудь Рутокен S) - каждая подпись начинает создаваться в разы дольше (в районе 700-1000мс).
Вот пример кода, который сейчас используется. Он основан на примерах подписания из документации. Специально упростил его до двух последовательных вызовов
SignCades друг за другом, чтобы исключить влияние размеров файлов, влияние функций создания атрибутов и т.п.
Код:this.sign = async (certificate, pdfFileContent) => {
let Signature1 = null, Signature2 = null;
let errormes = "";
let oSigner;
try {
oSigner = await this.api.CreateObjectAsync("CAdESCOM.CPSigner");
} catch (err) {
errormes = "Failed to create CAdESCOM.CPSigner: " + err.number;
throw errormes;
}
let oSigningTimeAttr = await this.api.CreateObjectAsync("CADESCOM.CPAttribute");
let CAPICOM_AUTHENTICATED_ATTRIBUTE_SIGNING_TIME = 0;
await oSigningTimeAttr.propset_Name(CAPICOM_AUTHENTICATED_ATTRIBUTE_SIGNING_TIME);
let oTimeNow = new Date();
await oSigningTimeAttr.propset_Value(oTimeNow);
let attr = await oSigner.AuthenticatedAttributes2;
await attr.Add(oSigningTimeAttr);
let oDocumentNameAttr = await this.api.CreateObjectAsync("CADESCOM.CPAttribute");
let CADESCOM_AUTHENTICATED_ATTRIBUTE_DOCUMENT_NAME = 1;
await oDocumentNameAttr.propset_Name(CADESCOM_AUTHENTICATED_ATTRIBUTE_DOCUMENT_NAME);
await oDocumentNameAttr.propset_Value("Document Name");
await attr.Add(oDocumentNameAttr);
if (oSigner) {
await oSigner.propset_Certificate(certificate);
} else {
errormes = "Failed to create CAdESCOM.CPSigner";
throw errormes;
}
let oSignedData = await this.api.CreateObjectAsync("CAdESCOM.CadesSignedData");
if (pdfFileContent) {
await oSignedData.propset_ContentEncoding(this.api.CADESCOM_BASE64_TO_BINARY);
await oSignedData.propset_Content(pdfFileContent);
await oSigner.propset_Options(1); //CAPICOM_CERTIFICATE_INCLUDE_WHOLE_CHAIN
try {
Signature1 = await oSignedData.SignCades(oSigner, this.api.CADESCOM_CADES_BES, true); //Первая подпись - вызов занимает ~700мс
Signature2 = await oSignedData.SignCades(oSigner, this.api.CADESCOM_CADES_BES, true); //Вторая подпись - вызов занимает ещё ~700мс
} catch (err) {
errormes = "Не удалось создать подпись из-за ошибки: " + this.api.getLastError(err);
throw errormes;
}
}
return [Signature1, Signature2];
}
Я понимаю, что чтение физического токена - дело не быстрое, в том числе из-за разницы в скоростях интерфейсов, поэтому сначала мы просто смирились с этим ограничением.
Но потом наши партнёры дали поработать со своим проектом, написанным на Python и использующим вызовы C-функций. Первое подписание в нём также выполняется в районе 700-1000мс, но все последующие - около 100мс. Подписи оттуда выходят валидными, проверки проходят.
К сожалению, я не могу описать, каким образом работает этот проект, т.к. разбирался в нём пока только поверхностно.
Судя по всему, после первого подписания сохраняется некий "контекст сертификата", который затем переиспользуется, и к физическому токену идёт обращение только для получения private key.
При подписании в том проекте используется некая функция
CryptSignMessage, которая под Linux вызывается из библиотеки libcapi20.so. Сигнатура функции совпадает с описанной
здесь в MSDN.
Подскажите, пожалуйста, можно ли при работе через плагин КриптоПро реализовать что-то подобное в плане временного хранения "контекста сертификата", и тем самым ускорить процесс подписания через плагин? Или подобное достигается только "переездом" с плагина на вызовы нативных функций?
Или может быть я упустил что-то, и в указанном выше коде есть какие-то проблемы, из-за чего с физического токена постоянно читаются какие-то лишние данные?
Доп.информация:ОС: Linux Mint 20.2 Uma (оболочка Cinnamon, базовый дистрибутив Ubuntu 20.04 focal)
Версия плагина: 2.0.14530
Версия криптопровайдера: 5.0.12417
Криптопровайдер: Crypto-Pro GOST R 34.10-2012 KC1 CSP
Тип создаваемых подписей: CAdES-BES