Как исправить ошибки "Подпись не соответствует формату CMS" и "Проверка подписи не пройдена" при работе с API Честного ЗНАКа?

Программист 1С v7.7 Торговля и дистрибуция
← На главную

При интеграции учетных систем (в частности, на платформах 1С 7.7 и 8.х) с API государственной информационной системы маркировки товаров (ГИС МТ "Честный ЗНАК") разработчики часто сталкиваются с проблемами на этапе криптографии. Наиболее распространенные ошибки, которые возвращает сервер: "Подпись не соответствует формату CMS" и "Ошибка: Проверка подписи не пройдена". В этой статье мы подробно разберем причины возникновения этих ошибок, различия в форматах подписей (MIIW... против MIAG...) и рассмотрим надежные методы их устранения, включая использование библиотек CAdESCOM и утилиты csptest.

Понимаем суть проблемы: форматы MIIW и MIAG

В процессе обсуждения мы заметили, что разные инструменты генерируют Base64-строки подписи, начинающиеся с разных символов:

Важно понимать: для сервера "Честного ЗНАКа" оба формата являются валидными, если сама структура CMS (PKCS#7) корректна. Ошибка "Подпись не соответствует формату CMS" чаще всего возникает не из-за заголовка, а из-за неправильной структуры данных или проблем с кодировкой. Если вам необходимо дополнительно проверить статус марок, можно использовать специальный запрос информации кодов Честного знака, который поможет убедиться в корректности данных перед подписанием.

Решение 1. Правильная настройка CAdESCOM для открепленной подписи

Для большинства методов API Честного ЗНАКа (например, ввод в оборот или выгрузка файлов XML для ЭДО с поддержкой маркировки) требуется открепленная подпись (Detached Signature). Это означает, что сама подпись передается отдельно от подписываемых данных (обычно в заголовке или отдельном поле JSON).

Если вы используете COM-объект CAdESCOM.CadesSignedData, необходимо убедиться, что вы вызываете метод подписания с правильными параметрами. Рассмотрим пример функции на языке JScript (который удобно встраивать в 1С через MSScriptControl), позволяющей формировать корректную открепленную подпись.


function SignFile(FileName, Cert, OutFileName) {
    // Чтение исходного файла в бинарном режиме
    InStream = new ActiveXObject("ADODB.Stream");
    InStream.Type = 1; // binary data
    InStream.Mode = 3; // read/write
    InStream.Open();
    InStream.LoadFromFile(FileName);
    InData = InStream.Read(-1);

    // Инициализация подписанта
    Signer = new ActiveXObject("CAdESCOM.CpSigner");
    Signer.Certificate = Cert;
    Signer.Options = 2; // CAPICOM_CERTIFICATE_INCLUDE_END_ENTITY_ONLY

    // Создание объекта подписи
    SignedData = new ActiveXObject("CAdESCOM.CadesSignedData");
    SignedData.Content = InData;

    // Важный момент: Параметры метода SignCades
    // 1 (bDetached) = true (открепленная подпись)
    // 0 (EncodingType) = CADESCOM_BASE64_TO_BINARY (вывод в Base64)
    // В примере ниже используется SignCades(Signer, 1, 1, 0)
    // Второй параметр: CadesType (1 = CADES_BES)
    // Третий параметр: bDetached (1 = True, открепленная)
    // Четвертый параметр: EncodingType (0 = Base64)
    OutSignedData = SignedData.SignCades(Signer, 1, 1, 0);

    // Сохранение подписи в файл
    OutStream = new ActiveXObject("ADODB.Stream");
    OutStream.Type = 2; // text data
    OutStream.CharSet = "utf-8";
    OutStream.Mode = 3; // read/write
    OutStream.Open();
    OutStream.WriteText(OutSignedData);
    OutStream.SaveToFile(OutFileName, 2);
    OutStream.Close();

    return(0);
}

Обратите внимание на использование объекта ADODB.Stream. Для чтения исходного файла мы используем Type = 1 (бинарный), чтобы избежать искажения байтов при чтении. Это критически важно, так как подпись рассчитывается от бинарного представления данных.

Решение 2. Проблема кодировок и BOM (Ошибка "Проверка подписи не пройдена")

Даже если формат CMS верен, сервер может вернуть ошибку "Проверка подписи не пройдена" (при этом HTTP статус может быть 200). Это означает, что криптографический хэш данных, который вы подписали, не совпадает с хэшем данных, которые получил сервер.

Основная причина: различие в кодировках. 1С хранит строки в формате UCS-2 (UTF-16LE). API Честного ЗНАКа ожидает UTF-8. Если вы сохраняете JSON из 1С в файл как текст, система может добавить в начало файла метку порядка байт (BOM — Byte Order Mark). Для UTF-8 это три байта: EF BB BF.

Если вы подпишете файл с BOM, а на сервер отправите тело запроса без BOM (или наоборот), хэши не совпадут. Поэтому, прежде чем подписывать данные, мы должны убедиться, что работаем с "чистым" бинарным массивом данных в UTF-8 без лишних заголовков.

Решение 3. Использование утилиты csptest.exe (Самый надежный способ)

Если манипуляции с COM-объектами и потоками ADODB вызывают сложности или нестабильный результат (особенно в старых версиях платформы, таких как 1С 7.7, где реализуется разрешительный режим на кассах), мы рекомендуем использовать штатную утилиту csptest.exe, входящую в состав дистрибутива КриптоПро CSP.

Этот метод работает надежнее, так как утилита работает с файлами на низком уровне, исключая проблемы конвертации строк внутри 1С. Также это отличное решение, если вы хотите самостоятельно управлять процессом подписания.

Алгоритм действий:

  1. Сформируйте тело запроса (JSON) в 1С.
  2. Сохраните его в файл, убедившись, что кодировка UTF-8 (без BOM). Если используете 1С 7.7, можно использовать ADODB.Stream для конвертации строки в UTF-8.
  3. Запустите csptest.exe через ЗапуститьПриложение или КомандаСистемы.
  4. Прочитайте полученный файл с подписью.

Пример командной строки для формирования открепленной подписи в формате Base64:


"C:\Program Files\Crypto Pro\CSP\csptest.exe" -sfsign -sign -detached -base64 -in "путь_к_файлу_данных.json" -out "путь_к_файлу_подписи.txt" -my "ИмяСертификатаИлиОтпечаток"

Разберем ключи команды:

Использование этого метода решило проблему автора темы, когда стандартные компоненты выдавали ошибку проверки.

Дополнительные инструменты: Очистка подписи и Base64

При получении подписи (через CAdESCOM или файл) она может содержать переносы строк (\r\n), которые иногда мешают корректной передаче в JSON. Рекомендуется удалять их перед отправкой.

Функция очистки подписи от переносов строк и заголовков:


Функция УбратьПереносыСтрок(ПутьКСертификату) Экспорт
    ТекстСертификата = "";
    // Чтение файла
    АдоДБСтрим = СоздатьОбъект("ADODB.Stream");
    АдоДБСтрим.Mode = 3;
    АдоДБСтрим.Type = 2; // текст
    АдоДБСтрим.charset = "utf-8";
    АдоДБСтрим.Open();
    АдоДБСтрим.LoadFromFile(ПутьКСертификату);
    ТекстСертификата = АдоДБСтрим.ReadText(-1);
    АдоДБСтрим.Close();

    ВозврPublic_cert = "";
    Для СчСтрок = 1 По СтрКоличествоСтрок(ТекстСертификата) Цикл
        ТекСтрока = СтрПолучитьСтроку(ТекстСертификата, СчСтрок);
        // Пропускаем заголовки BEGIN/END, если они есть
        Если Лев(ТекСтрока, 4) = "----" Тогда
            Продолжить;
        КонецЕсли;
        // Удаляем переносы
        ТекСтрока = СтрЗаменить(ТекСтрока, РазделительСтрок, "");
        ВозврPublic_cert = ВозврPublic_cert + ТекСтрока;
    КонецЦикла;

    Возврат ВозврPublic_cert;
КонецФункции

Также, если вам необходимо кодировать данные в Base64 штатными средствами Windows (без внешних компонент), можно использовать объект CDO.Message. Это особенно актуально для старых платформ.


Функция КодироватьBase64(Стр, Кодировка = "base64") Экспорт
    ЦДО = СоздатьОбъект("CDO.Message");
    БодиПарт = ЦДО.BodyPart;
    Поля = БодиПарт.Fields;
    Поле = Поля.Item("urn:schemas:mailheader:content-type");
    Поле.Value = "text/plain; charset=""windows-1251""";
    Поля.Update();
    
    // Запись исходного текста
    Стрим = БодиПарт.GetDecodedContentStream();
    Стрим.charset = "windows-1251";
    Стрим.WriteText(Стр);
    Стрим.Flush();
    
    // Получение закодированного текста
    БодиПарт.ContentTransferEncoding = Кодировка;
    Стрим = БодиПарт.GetEncodedContentStream();
    
    // CDO может добавлять переносы строк, их лучше удалить
    Результат = Стрим.ReadText();
    // Здесь рекомендуется добавить очистку от символов перевода строки, если API этого требует
    Возврат Результат; 
КонецФункции

Выводы

Для успешной работы с API Честного ЗНАКа обратите внимание на следующие пункты:

  1. Всегда используйте открепленную подпись (Detached).
  2. Следите за тем, чтобы подписываемые данные были идентичны отправляемым (байт-в-байт). Остерегайтесь автоматического добавления BOM при сохранении UTF-8 файлов.
  3. Если встроенные методы CAdESCOM работают некорректно или вызывают трудности с типами данных в 1С, используйте вызов внешней утилиты csptest.exe с параметрами -detached -base64. Это наиболее стабильное решение.
  4. Удаляйте переносы строк из итоговой строки Base64 перед вставкой в JSON-запрос.
  5. Для быстрой проверки статуса марок прямо на складе или в магазине можно использовать мобильное приложение для Android устройств, которое работает в разрешительном режиме.
← На главную