Подпись по формату PKCS#7

1. eliseev112 22.03.21 19:56 Сейчас в теме
Коллеги, нужна помощь в подписи по формату PKCS#7 для подписания вложений для обмена со СМЭВ

Необходимо реализовать обмен с ВС "Информация о данных в хранилище СМЭВ"

Требования СМЭВ:
Формирование подписи по алгоритму ГОСТ 34.10-2012, 256 бит (ГОСТ Р 34.10-2012)
Расчёт хеш-суммы, 256 бит (ГОСТ Р 34.11-2012)

Вычисляю ХЕШ
ДД = Новый ДвоичныеДанные(Путь);
Base64 = Base64Строка(ДД);
CADESCOM_HASH_ALGORITHM_CP_GOST_3411_2012_256 = 101;

HashedData = Новый COMОбъект("CAdESCOM.HashedData");
HashedData.Algorithm = CADESCOM_HASH_ALGORITHM_CP_GOST_3411_2012_256;

HashedData.Hash(base64);

Хэш_HEXстрока = HashedData.Value;
Хэш_ДвочиныеДанные = ПолучитьДвоичныеДанныеИзHexСтроки(Хэш_HEXстрока);
СтрокаBase64 = Base64Строка(Хэш_ДвочиныеДанные);
Показать

Далее полученный ХЕШ пробую подписать через "МенеджерКриптографии"
//Дальше получаем значение подписи хэша:
мМенеджерКриптографии = Новый МенеджерКриптографии("Crypto-Pro GOST R 34.10-2012 Cryptographic Service Provider", "", 80);
//мМенеджерКриптографии.ВключениеСертификатовВПодпись = РежимВключенияСертификатовКриптографии.ВключатьСертификатСубъекта;
мМенеджерКриптографии.ПарольДоступаКЗакрытомуКлючу = "12345678";
//Подпись_ДвочиныеДанные = мМенеджерКриптографии.Подписать(Хэш_ДвочиныеДанные, "C:\nn\signature.sign",СертификатКриптографии);
Подпись_ДвочиныеДанные = мМенеджерКриптографии.Подписать(Хэш_ДвочиныеДанные, СертификатКриптографии);
//Кодируем подпись в Base64:
ПодписьBase64 = Base64Строка(Подпись_ДвочиныеДанные


Формирую пакет, но в итоге получаю сообщение от СМЭВ
"Проверка подписи на вложении 9debc120-8b12-11eb-976a-2cf05d7da61a: Дайджест не прошел проверку!"

Уже что только не перепробовал. От ТП СМЭВ - помощи нет никакой, только переписка постоянная.

Может есть кто счастливчик кто уже ранее делал интеграцию со СМЭВ. Поделитесь алгоритмом, или хотябы направление куда смотреть?
Сил уже нет никаких
misssixty; +1 Ответить
По теме из базы знаний
Вознаграждение за ответ
Показать полностью
Ответы
Подписаться на ответы Инфостарт бот Сортировка: Древо развёрнутое
Свернуть все
2. Leon75 23.03.21 00:13 Сейчас в теме
Вроде бы алгоритмы старые, не Стрибог. Сам алгоритм подписи на элиптических кривых. Теоретически можно и математикой сделать. Я сам этот гост раньше не делал, но думаю менеджер криптографии 1с не подходит.
https://m.habr.com/ru/post/191240/
3. shurikvz 23.03.21 18:49 Сейчас в теме
А точно надо подписывать хэш от данных, а не сами данные?
6. eliseev112 26.03.21 07:30 Сейчас в теме
(3) Изначально я подписывал сам файл данных, но мне ответили что подписывать необходимо вычесленных ХЕШ.
7. uno-c 267 26.03.21 10:18 Сейчас в теме
(6) Тоже бывает. В Тинькофбанке помню - сначала вычислял хеш, потом вычислял ХешХеша, и уже SingHash(ХешХеша) - только тогда прошло успешно. Обратите внимание - в коде два раза идет oHashedData.Hash. И кстати они же два примера - как захешировать строку и как захешировать двоичные данные:
UTF8Encoding = Новый COMОбъект("System.Text.UTF8Encoding");
//байты = UTF8Encoding.GetBytes_4(Строка); //массив чисел-байтов, каждый байт - буква переменной Строка
oHashedData.Hash(UTF8Encoding.GetBytes_4(Строка));
ХэшХексСтрока = oHashedData.Value;

ХэшДвоичный =  Получить_ДвоичныеДанныеИзHexСтроки(ХэшХексСтрока); //этот хеш будем отправлять в POST запрос
ХэшДвоичный_64 = Base64Строка(ХэшДвоичный);
oHashedData.DataEncoding = CADESCOM_BASE64_TO_BINARY;
oHashedData.Hash(ХэшДвоичный_64); //хешируем хеш чтобы подписать методом SignHash
sRawSignature = oRawSignature.SignHash(oHashedData, oCertificate);

ПодписьДвоичная =  Получить_ДвоичныеДанныеИзHexСтроки(sRawSignature);

СтруктураОтвета = Новый Структура;
СтруктураОтвета.Вставить("DigestValue", Base64Строка(ХэшДвоичный));
СтруктураОтвета.Вставить("SignatureValue", Base64Строка(ПодписьДвоичная));
СтруктураОтвета.Вставить("X509SerialNumber", oCertificate.SerialNumber);
Показать
8. uno-c 267 26.03.21 10:25 Сейчас в теме
(7) Обратите внимание, что SignHash() отличается от МенеджерКриптографии.Подписать (в Кадескоме вроде тоже был Sign() у какого-то класса). Отличие в том, что SignHash никаких преобразований с подуписуемым хешем не делает - сразу вычисляет подпись для oHashedData без вычисления дайджеста (т.е. не вычисляется ХешХеша). Метод Подписать - сначала вычисляет дайджест и уже по нему вычисляет подпись. Т.е. если Методу Подписать скормить изначально полученный хеш - то МенеджерКриптографии из этого хеша вычислит еще один хеш, и для ХешХеша будет вычислять подпись.
"Вычислить подпись" и "подписать" - я имею в виду разные понятия. Подписать - это две операции 1) вычислить хеш; 2) вычислить подпись для полученного хеша.
9. Sonic_seb 2 25.03.22 16:10 Сейчас в теме
(7)
Строка

Доброго времени суток!
При
sRawSignature = oRawSignature.SignHash(oHashedData, oCertificate);

Произошла исключительная ситуация (CAdESCOM.RawSignature.1): Указан неправильный алгоритм.

Полностью ваш код....Что может быть не подскажете?
10. Sonic_seb 2 25.03.22 16:22 Сейчас в теме
(7)
	UTF8Encoding = Новый COMОбъект("System.Text.UTF8Encoding"); 
		oRawSignature = Новый COMОбъект("CAdESCOM.RawSignature");   
		oHashedData = Новый COMОбъект("CAdESCOM.HashedData");
		
		CADESCOM_BASE64_TO_BINARY = 1; //"0x01";
		CADESCOM_HASH_ALGORITHM_CP_GOST_3411_2012_256	= 101;
		

		//байты = UTF8Encoding.GetBytes_4(Строка); //массив чисел-байтов, каждый байт - буква переменной Строка
		oHashedData.Hash(UTF8Encoding.GetBytes_4(СтрокаДляПодписи));
		ХэшХексСтрока = oHashedData.Value;
		
		ХэшДвоичный =  ПолучитьДвоичныеДанныеИзХеша(ХэшХексСтрока); //этот хеш будем отправлять в POST запрос
		ХэшДвоичный_64 = Base64Строка(ХэшДвоичный);
		oHashedData.DataEncoding = CADESCOM_BASE64_TO_BINARY;
		oHashedData.Algorithm	= CADESCOM_HASH_ALGORITHM_CP_GOST_3411_2012_256;
		oHashedData.Hash(ХэшДвоичный_64); //хешируем хеш чтобы подписать методом SignHash
		sRawSignature = oRawSignature.SignHash(oHashedData, oCertificate);
		
		ПодписьДвоичная =  ПолучитьДвоичныеДанныеИзХеша(sRawSignature);
		
		СтруктураПодписи = Новый Структура;
		СтруктураПодписи.Вставить("DigestValue", Base64Строка(ХэшДвоичный));
		СтруктураПодписи.Вставить("SignatureValue", Base64Строка(ПодписьДвоичная));  
		СтруктураПодписи.Вставить("Отказ", Ложь);
Показать


Валится - передана не корректная подпись.

а если
CADESCOM_HASH_ALGORITHM_CP_GOST_3411_2012_256	= 100 

тогда Произошла исключительная ситуация (CAdESCOM.RawSignature.1): Указан неправильный алгоритм.
12. uno-c 267 25.03.22 19:18 Сейчас в теме
(10) А, неправильный алгоритм - это сотый? Ну он устарел и запрещен вроде уже.
CADESCOM_HASH_ALGORITHM_CP_GOST_3411 Алгоритм ГОСТ Р 34.11-94.
=100

Вы уверены, что Вам хеш хеша надо подписывать? Просто первый хеш подписать не пробовали, без повторного хеширования? И это непонятно ХэшДвоичный = ПолучитьДвоичныеДанныеИзХеша(ХэшХексСтрока), там может какие-то нюансы с little endian.
14. Sonic_seb 2 25.03.22 19:48 Сейчас в теме
(12)вот и я пытаюсь понять.... В банке мне ничего подсказать не могут...
11. uno-c 267 25.03.22 19:09 Сейчас в теме
(9) Может у Вас сертификат/ключ другой длины?
CADESCOM_HASH_ALGORITHM_CP_GOST_3411_2012_512 Алгоритм ГОСТ Р 34.11-2012.
=102
4. uno-c 267 25.03.21 19:34 Сейчас в теме
МенеджерКриптографии не умеет переданный ему хеш подписывать по типу CADESCOM-ового SignHash(). Если делать как у Вас - то Менеджер сначала посчитает ХешХеша, а потом его и подпишет. Подписывайте исходные данные.
В процессе МенеджерКриптографии.Подписать происходит сначала вычисление хеша, а потом пополученному хешу вычисляется подись - она и возвращается как результат.
5. uno-c 267 25.03.21 20:28 Сейчас в теме
Хотя у Вас по ошибке даже дайджест не проходит (он же хэш). Т.е. уже хеш у Вас видимо неправильный. Смотрю свой код трехлетней давности, судя по нему по умолчанию переданную на хеширование строку Кадеском воспринимает просто как строку(CADESCOM_STRING_TO_UCS2LE), а не как бейс64. Чтобы он воспринял ее как бейс-64 нужно команду дать
HashedData.DataEncoding = 1;
Скопирую сюда что нашел, первый вариант с прямой передачей исходной строки у меня тогда не работал. Только бейс64 или в старой платформе - двоичные данные скармливал Кадескому - тогда результат норм. получался.

//Способ кодирования данных для подписи для пслеующего свойства ContentEncoding 
CADESCOM_BASE64_TO_BINARY = 1; // Кодировка BASE64.
CADESCOM_STRING_TO_UCS2LE = 0; // Кодировка UTF-8 или UNICODE. 
//oHashedData.DataEncoding = CADESCOM_STRING_TO_UCS2LE; //этот и так по умолчпнию
//прямая передача строки в ком (без флага раскодировки) дает другой хеш
//oHashedData.Hash(Строка); //врет (без oHashedData.DataEncoding = CADESCOM_BASE64_TO_BINARY)

//Строка64 = Base64Строка(ПолучитьДвоичныеДанныеИзСтроки(Строка)); //ПолучитьДвоичныеДанныеИзСтроки нет в 1с 8.9
//тмп = oHashedData.Value; //сбросим аккумуляцию хеша на всякий слуай
//oHashedData.Hash(Строка64);

UTF8Encoding = Новый COMОбъект("System.Text.UTF8Encoding");
//байты = UTF8Encoding.GetBytes_4(Строка); //массив чисел-байтов, каждый байт - буква переменной Строка
oHashedData.Hash(UTF8Encoding.GetBytes_4(Строка));
ХэшХексСтрока = oHashedData.Value;
Показать
13. Sonic_seb 2 25.03.22 19:37 Сейчас в теме
(11)в сертификате написано 256
15. Sonic_seb 2 30.03.22 15:13 Сейчас в теме
Может кто подскажет какое значение должно быть у oHashedData.Algorithm если для подписи используется Алгоритм ГОСТ Р 34.11-2012. 256
18. Sonic_seb 2 30.03.22 15:35 Сейчас в теме
ТинькофФ прислал. и вот SignatureValue не правильно вычисляется
19. vadymdymdym 01.04.22 13:37 Сейчас в теме
Я на СМЭВе съел собаку, поэтому, если тема еще актуальна, могу предложить альтернативное решение. Начнем с того, что во-первых, вам нужно подписывать НЕ ХЕШ, А САМ ФАЙЛ. Во-вторых, подпись является НЕОБЯЗАТЕЛЬНОЙ. А хеш - обязательным. Теперь по вашей теме: рекомендую использовать внешнюю компоненту XMLDSign, либо ее современный аналог ExtraCryptoAPI. В типовых конфигурациях наподобие Розницы ее можно найти (могу скинуть в личку). Лично я использую первую, хотя вторая более универсальная (работает практически в любом браузере на веб-клиенте, на винде, на линухе и на маке. Первая же работает в веб-клиенте только на винде, и НЕ на веб-клиенте - на винде и на линухе. Обе работают как под 32, так и под 64-бит). У компоненты есть метод Hash. Идентификатор ("AddIn. ...") первой компоненты - XMLDSign, второй - ExtraCryptoAPI. Она возвращает в Base-64 формате готовый (тот, который нужен вам) хеш. Функция:

Hash(ДанныеДляХеширования, OIDАлгоритмаХеширования, ТипКриптопровайдера)

Параметры:

ДанныеДляХеширования: Тип - Строка или ДвоичныеДанные. То, что собираемся хешировать. Как видите, можно хешировать не только двоичные данные, но и строку, что очень пригождается при вычислении хеша от части xml, когда требуется получить подпись в формате xmldsign (опять же, пригодится и для подписи запросов в СМЭВ. Лично я использую. Единственная проблема - алгоритм трансформации СМЭВ в этой компоненте не заложен. Лично я писал его ручками прямо в 1С и он работает. Однако, в последней компоненте ExtraCryptoAPI уже есть готовый метод TransformSMEV, который также можно использовать. Когда я его писал - я про вторую компоненту еще не знал, поэтому написал руками)

OIDАлгоритмаХеширования: Тип - Строка. Идентификатор алгоритма хеширования, который будет использован для вычисления хеша. В твоем случае ГОСТ Р 34.11-2012 256 бит, его OID = "1.2.643.7.1.1.2.2". Если требуется 512 бит - то OID = "1.2.643.7.1.1.2.3"

ТипКриптопровайдера: Тип - Число. Его ты возмьешь уже из свойств твоих данных. Где у тебя там хранятся сертификаты или программы электронной подписи и шифрования. Например, если это КриптоПро (сертификат 256 бит), то его тип = 80, если же КриптоПро Стронг (сертификат 512 бит), то его тип = 81

Возвращаемое значение: Тип - Строка. Нужный хеш в формате Base-64. Если ничего не вернулось - значит произошла ошибка. Последнюю ошибку можно узнать через функцию компоненты GetLastError(). Однако, на последних версиях платформы (не помню с какой) на винде она возрвращает ошибку не в той кодировке. Поэтому, если видите крякозябры - перекодируйте ошибку из UTF-8 в ANSI

P.S. Где-то на каких-то форумах я читал, что ваш вариант хеширования тоже подойдет, только его нужно, кажется, повернуть в обратном направлении (то ли каждый байт развернуть побитово, то ли полубайты поменять местами, то ли целиком развернуть побайтово. Не помню. Кажется, полубайт. Т.е. например очередной байт в хеше 8A. После преобразования будет A8. Разумеется, я сейчас говорю про шестнадцатиричное представление хеша, а не про Base-64. Как перевести из одного в другое, думаю, разберетесь). Попробуйте различные варианты переворота хеша, если не хотите заморачиваться с внешней компонентой. Возможно, получится

P.P.S. На официальном сайте VipNET есть консольная утилита, которая возвращает нужный вам хеш от файла. Скачайте. Она бесплатная. Ее тоже можно использовать. Если не в коде 1С, то хотя бы для проверки. Она возвращает хеш, правда в шестнадцатиричном виде, но вы, опять же, без труда переведете в Base64. Утилита есть под винду и под линух

Удачи))
22. Enotov13 11.10.24 10:57 Сейчас в теме
(19)
Поэтому, если видите крякозябры - перекодируйте ошибку из UTF-8 в ANSI

Добрый день! Подскажите, есть ли пример использования TransformSMEV с последующем чтении XML-строки после трансформации?
У нас как раз таки СМЭВ, использовали компоненту из БГУ Xades с методом ПодписатьXMLDSignСМЭВ3 для подписи, но с переходом серверов на Линукс начали сталкиваться с ошибкой, что кириллица возвращается как кракозябры... Попробовали на днях метод XMLDSig, подписывает нормально, потом трансформируем через TransformSMEV, и дальше надо модернизировать код для прочтения этой строки, так как она отличается от строки, которая у нас была при использовании ПодписатьXMLDSignСМЭВ3 ...
20. gml 06.05.22 14:59 Сейчас в теме
Для проверки правильности формирования дайджеста (хэша) XMLDSig найдите любое достоверное (прошедшее проверку подписи) XML-сообщение и отправьте его на вход своего алгоритма.
Рассчитанное значение сравните со значением элемента Signature/SignedInfo/DigestValue. Если дело только в изменении порядка байтов - Вы легко это исправите. Хуже, если Ваш алгоритм трансформации не соответствует актуальной версии (а версию алгоритма в СМЭВ в прошлом году поменяли).
21. gml 07.05.22 17:35 Сейчас в теме
Попробуйте при вычислении "сырой" подписи не хэшировать ранее рассчитанный хэш, а просто подставить его значение:

...
oHashedData.DataEncoding = CADESCOM_BASE64_TO_BINARY;
oHashedData.Algorithm = CADESCOM_HASH_ALGORITHM_CP_GOST_3411_2012_256;
oHashedData.SetHashValue(ХэшДвоичный_64); //подставляем рассчитанный хеш чтобы подписать методом SignHash
sRawSignature = oRawSignature.SignHash(oHashedData, oCertificate);

ПодписьДвоичная = ПолучитьДвоичныеДанныеИзХеша(sRawSignature);
...
Оставьте свое сообщение

Для получения уведомлений об ответах подключите телеграм бот:
Инфостарт бот