Ошибка вызова метода sign компоненты xmldsig

Подпись xml файла из 1C - Добрый день! В 1С организована работа с порталом СЭП(Системы электронных паспортов) для оформления ЭПТС(Электронных паспортов технических средств) через криптошлюз. Обмен осуществляется xml файлами...

Offline

Dartwed1989

 


#1
Оставлено
:

15 октября 2019 г. 14:15:58(UTC)

Dartwed1989

Статус: Участник

Группы: Участники

Зарегистрирован: 17.05.2019(UTC)
Сообщений: 13
Российская Федерация
Откуда: Санкт-Петербург

Поблагодарили: 1 раз в 1 постах

Добрый день!

В 1С организована работа с порталом СЭП(Системы электронных паспортов) для оформления ЭПТС(Электронных паспортов технических средств) через криптошлюз.
Обмен осуществляется xml файлами с подписью XMLDSig.
Есть 3 сертификата (для трех разных пользователей). Для двух пользователей все работает без нареканий. Для одного пользователя выводится данная ошибка:

«Ошибка по VIN: XW7BF3HK00S151416 Ошибка при обработке данных: {ОбщийМодуль.ЭлектроннаяПодписьСлужебный.Модуль(1556)}: Ошибка вызова метода Sign компоненты XMLDSig. Ошибка криптографии : Системная криптографическая ошибка — 0x8010006B ( =1).»

Сертификат пользователя проходит все проверки, он действителен, не заблокирован и пользователь с помощью данного сертификата заходит на портал СЭП и работает в системе Диадок.

Версия КриптоПро — 4.0.9.
Сравнение настроек с другими пользователями расхождений не выявило.
Сертификат загружен в контейнер сертификатов. В чем конкретно ошибка понять уже не удается. Можете что-либо подсказать?


Вверх


Offline

Dartwed1989

 


#2
Оставлено
:

15 октября 2019 г. 16:13:11(UTC)

Dartwed1989

Статус: Участник

Группы: Участники

Зарегистрирован: 17.05.2019(UTC)
Сообщений: 13
Российская Федерация
Откуда: Санкт-Петербург

Поблагодарили: 1 раз в 1 постах

Проблему решить удалось.

Необходимо было в КриптоПро поставить галочку — «Запомнить пароль».


Вверх

Пользователи, просматривающие эту тему

Guest

Быстрый переход
 

Вы не можете создавать новые темы в этом форуме.

Вы не можете отвечать в этом форуме.

Вы не можете удалять Ваши сообщения в этом форуме.

Вы не можете редактировать Ваши сообщения в этом форуме.

Вы не можете создавать опросы в этом форуме.

Вы не можете голосовать в этом форуме.

#Область ПрограммныйИнтерфейс // Начинает подписание XML сообщения. // // Параметры: // ОповещенияПриЗавершении — ОписаниеОповещения — процедура, которая будет вызвана после подписания сообщения // КонвертSOAP — Строка — Шаблон подписываемого сообщения // СертификатКриптографии — СертификатКриптографии // МенеджерКриптографии — МенеджерКриптографии // Процедура НачатьПодписание(ОповещенияПриЗавершении, КонвертSOAP, ДанныеАлгоритмаПодписания, СертификатКриптографии, МенеджерКриптографии) Экспорт Контекст = Новый Структура; Контекст.Вставить(«Режим», «РежимПодписание»); Контекст.Вставить(«ОповещенияПриЗавершении», ОповещенияПриЗавершении); Контекст.Вставить(«УстанавливатьКомпоненту», Истина); Контекст.Вставить(«КонвертSOAP», КонвертSOAP); Контекст.Вставить(«ДанныеАлгоритмаПодписания», ДанныеАлгоритмаПодписания); Контекст.Вставить(«СертификатКриптографии», СертификатКриптографии); Контекст.Вставить(«СертификатКриптографииBase64», Неопределено); Контекст.Вставить(«МенеджерКриптографии», МенеджерКриптографии); Контекст.Вставить(«ТипКриптопровайдера», Неопределено); Контекст.Вставить(«ИмяКриптопровайдера», Неопределено); СертификатКриптографии.НачатьВыгрузку( Новый ОписаниеОповещения(«НачатьПодписание_ПослеВыгрузкиСертификатаКриптографии», ЭтотОбъект, Контекст)); КонецПроцедуры // Начинает подписание XML сообщения. // // Параметры: // ОповещенияПриЗавершении — ОписаниеОповещения — процедура, которая будет вызвана после подписания сообщения // КонвертSOAP — Строка — Шаблон подписываемого сообщения // ДанныеАлгоритмаПодписания — Структура со свойствами: // МенеджерКриптографии — МенеджерКриптографии // Процедура НачатьПроверкуПодписи(ОповещенияПриЗавершении, КонвертSOAP, ДанныеАлгоритмаПодписания, МенеджерКриптографии) Экспорт СертификатКриптографииBase64 = ИнтеграцияГИСМКлиентСервер.НайтиВXML(КонвертSOAP, «wsse:BinarySecurityToken»); ДвоичныеДанные = Base64Значение(СертификатКриптографииBase64); Контекст = Новый Структура; Контекст.Вставить(«Режим», «РежимПроверка»); Контекст.Вставить(«ОповещенияПриЗавершении», ОповещенияПриЗавершении); Контекст.Вставить(«УстанавливатьКомпоненту», Истина); Контекст.Вставить(«КонвертSOAP», КонвертSOAP); Контекст.Вставить(«ДанныеАлгоритмаПодписания», ДанныеАлгоритмаПодписания); Контекст.Вставить(«СертификатКриптографии», Новый СертификатКриптографии(ДвоичныеДанные)); Контекст.Вставить(«СертификатКриптографииBase64», СертификатКриптографииBase64); Контекст.Вставить(«МенеджерКриптографии», МенеджерКриптографии); Контекст.Вставить(«ТипКриптопровайдера», Неопределено); Контекст.Вставить(«ИмяКриптопровайдера», Неопределено); НачатьПодключениеВнешнейКомпоненты( Новый ОписаниеОповещения(«ПослеПодключенияКомпоненты», ЭтотОбъект, Контекст), «ОбщийМакет.КомпонентаXMLDSIG», «ОбъектXMLDSIG», ТипВнешнейКомпоненты.Native); КонецПроцедуры #КонецОбласти #Область СлужебныеПроцедурыИФункции #Область УстановкаИПодключение // Начинает установку компоненты XMLDSIG. // // Параметры: // ОповещениеПриЗавершении — ОписаниеОповещения — процедура, которая будет вызвана после установки компоненты. // Процедура НачатьУстановкуКомпоненты(ОповещениеПриЗавершении = Неопределено) Экспорт ДополнительныеПараметры = Новый Структура(«ОповещениеПриЗавершении», ОповещениеПриЗавершении); НачатьУстановкуВнешнейКомпоненты( Новый ОписаниеОповещения(«УстановкаКомпоненты_Завершение», ЭтотОбъект, ДополнительныеПараметры), «ОбщийМакет.КомпонентаXMLDSIG»); КонецПроцедуры Процедура УстановкаКомпоненты_Завершение(ДополнительныеПараметры) Экспорт ВыполнитьОбработкуОповещения(ДополнительныеПараметры.ОповещениеПриЗавершении, Неопределено); КонецПроцедуры Процедура ПослеУстановкиКомпоненты(Результат, Контекст) Экспорт Контекст.Вставить(«УстанавливатьКомпоненту», Ложь); НачатьПодключениеВнешнейКомпоненты( Новый ОписаниеОповещения(«ПослеПодключенияКомпоненты», ЭтотОбъект, Контекст), «ОбщийМакет.КомпонентаXMLDSIG», «ОбъектXMLDSIG», ТипВнешнейКомпоненты.Native); КонецПроцедуры Процедура ПослеПодключенияКомпоненты(Подключено, Контекст) Экспорт Если Подключено Тогда Выполнено = Ложь; Попытка ОбъектКомпоненты = Новый(«Addin.ОбъектXMLDSIG.XMLDSignAddIn»); Выполнено = Истина; Исключение ЗавершитьОперациюСОшибкой( Контекст, НСтр(«ru = ‘Ошибка инициализации компоненты XMLDSIG.'») + Символы.ПС + ПодробноеПредставлениеОшибки(ИнформацияОбОшибке())); Выполнено = Ложь; КонецПопытки; Если Выполнено Тогда Контекст.Вставить(«ОбъектКомпоненты», ОбъектКомпоненты); Если Контекст.Режим = «РежимПроверка» Тогда ОповещениеПриЗавершении = Новый ОписаниеОповещения(«НачатьПроверкуПодписиСообщенияSOAP», ЭтотОбъект, Контекст); Контекст.МенеджерКриптографии.НачатьПолучениеИнформацииМодуляКриптографии(ОповещениеПриЗавершении); ИначеЕсли Контекст.Режим = «РежимПодписание» Тогда ОповещениеПриЗавершении = Новый ОписаниеОповещения(«НачатьПодписаниеСообщенияSOAP», ЭтотОбъект, Контекст); Контекст.МенеджерКриптографии.НачатьПолучениеИнформацииМодуляКриптографии(ОповещениеПриЗавершении); Иначе ЗавершитьОперациюСОшибкой( Контекст, НСтр(«ru = ‘Не установлен режим работы компоненты XMLDSIG.'»)); КонецЕсли; КонецЕсли; ИначеЕсли Контекст.УстанавливатьКомпоненту Тогда ТекстВопроса = НСтр(«ru = ‘Для подписания XML необходима установка компоненты XMLDSIG. |Перейти к установке данной компоненты?'»); ПоказатьВопрос( Новый ОписаниеОповещения(«ПередУстановкойКомпоненты», ЭтотОбъект, Контекст), ТекстВопроса, РежимДиалогаВопрос.ДаНет); Иначе ЗавершитьОперациюСОшибкой( Контекст, НСтр(«ru = ‘Операция невозможна. Требуется установка компоненты XMLDSIG.'»)); КонецЕсли; КонецПроцедуры Процедура ПередУстановкойКомпоненты(РезультатВопроса, Контекст) Экспорт Если НЕ РезультатВопроса = КодВозвратаДиалога.Да Тогда ЗавершитьОперациюСОшибкой( Контекст, НСтр(«ru = ‘Операция невозможна. Требуется установка компоненты для HTTP-запросов.'»)); Иначе НачатьУстановкуКомпоненты(Новый ОписаниеОповещения(«ПослеУстановкиКомпоненты», ЭтотОбъект, Контекст)); КонецЕсли; КонецПроцедуры #КонецОбласти #Область Подписание Процедура НачатьПодписание_ПослеВыгрузкиСертификатаКриптографии(ДвоичныеДанныеСертификата, Контекст) Экспорт СертификатКриптографииBase64 = ОтформатироватьСтрокуBase64(Base64Строка(ДвоичныеДанныеСертификата)); Контекст.КонвертSOAP = СтрЗаменить( Контекст.КонвертSOAP, «%BinarySecurityToken%», СертификатКриптографииBase64); Контекст.СертификатКриптографииBase64 = СертификатКриптографииBase64; НачатьПодключениеВнешнейКомпоненты( Новый ОписаниеОповещения(«ПослеПодключенияКомпоненты», ЭтотОбъект, Контекст), «ОбщийМакет.КомпонентаXMLDSIG», «ОбъектXMLDSIG», ТипВнешнейКомпоненты.Native); КонецПроцедуры Процедура НачатьПодписаниеСообщенияSOAP(ИнформацияМодуляКриптографии, Контекст) Экспорт ИмяКриптопровайдера = ИнформацияМодуляКриптографии.Имя; ТипКриптопровайдера = ЭлектроннаяПодписьXMLDSIGВызовСервераПовтИсп.ТипКриптопровайдераПоИмени(ИмяКриптопровайдера); Если ТипКриптопровайдера = Неопределено Тогда ЗавершитьОперациюСОшибкой( Контекст, СтрШаблон( НСтр(«ru = ‘Не удалось определить тип криптопровайдера %1′»), ИнформацияМодуляКриптографии.Имя) + Символы.ПС + ПодробноеПредставлениеОшибки(ИнформацияОбОшибке())); Возврат; КонецЕсли; Контекст.ТипКриптопровайдера = ТипКриптопровайдера; Контекст.ИмяКриптопровайдера = ИмяКриптопровайдера; Попытка ОписаниеОповещения = Новый ОписаниеОповещения( «Подписание_ПослеВыполненияGetSignOIDFromCert», ЭтотОбъект, Контекст, «Подписание_ПослеВыполненияGetSignOIDFromCert_Ошибка», ЭтотОбъект); Контекст.ОбъектКомпоненты.НачатьВызовGetSignOIDFromCert( ОписаниеОповещения, Контекст.СертификатКриптографииBase64); Исключение ЗавершитьОперациюСОшибкой( Контекст, НСтр(«ru = ‘Ошибка вызова метода GetSignOIDFromCert компоненты XMLDSIG.'») + Символы.ПС + ПодробноеПредставлениеОшибки(ИнформацияОбОшибке())); КонецПопытки; КонецПроцедуры Процедура Подписание_ПослеВыполненияGetSignOIDFromCert_Ошибка(ИнформацияОбОшибке, СтандартнаяОбработка, Контекст) Экспорт СтандартнаяОбработка = Ложь; ЗавершитьОперациюСОшибкой( Контекст, НСтр(«ru = ‘Ошибка вызова метода GetSignOIDFromCert компоненты XMLDSIG.'») + Символы.ПС + ПодробноеПредставлениеОшибки(ИнформацияОбОшибке)); КонецПроцедуры Процедура Подписание_ПослеВыполненияGetSignOIDFromCert(OIDАлгоритмаПодписи, Параметры, Контекст) Экспорт Если OIDАлгоритмаПодписи = Неопределено Тогда НачатьПолучениеТекстаОшибки(НСтр(«ru = ‘При выполнении метода GetSignOIDFromCert произошла ошибка:'»), Контекст); Возврат; КонецЕсли; Если OIDАлгоритмаПодписи <> Контекст.ДанныеАлгоритмаПодписания.OIDАлгоритмаПодписи Тогда ЗавершитьОперациюСОшибкой( Контекст, СтрШаблон( НСтр(«ru = ‘Алгоритм подписи сертификата криптографии отличается от алгоритма %1.'»), Контекст.ДанныеАлгоритмаПодписания.ИмяАлгоритмаПодписи)); Иначе Попытка ОписаниеОповещения = Новый ОписаниеОповещения( «Подписание_ПослеВыполненияC14N_ПодписываемыйТег», ЭтотОбъект, Контекст, «Подписание_ПослеВыполненияC14N_ПодписываемыйТег_Ошибка», ЭтотОбъект); Контекст.ОбъектКомпоненты.НачатьВызовC14N( ОписаниеОповещения, Контекст.КонвертSOAP, Контекст.ДанныеАлгоритмаПодписания.XPathПодписываемыйТег); Исключение ЗавершитьОперациюСОшибкой( Контекст, НСтр(«ru = ‘Ошибка вызова метода C14N компоненты XMLDSIG.'») + Символы.ПС + ПодробноеПредставлениеОшибки(ИнформацияОбОшибке())); КонецПопытки; КонецЕсли; КонецПроцедуры Процедура Подписание_ПослеВыполненияC14N_ПодписываемыйТег_Ошибка(ИнформацияОбОшибке, СтандартнаяОбработка, Контекст) Экспорт СтандартнаяОбработка = Ложь; ЗавершитьОперациюСОшибкой( Контекст, НСтр(«ru = ‘Ошибка вызова метода C14N компоненты XMLDSIG.'») + Символы.ПС + ПодробноеПредставлениеОшибки(ИнформацияОбОшибке)); КонецПроцедуры Процедура Подписание_ПослеВыполненияC14N_ПодписываемыйТег(КаноникализированныйТекстXML, Параметры, Контекст) Экспорт Если КаноникализированныйТекстXML = Неопределено Тогда НачатьПолучениеТекстаОшибки(НСтр(«ru = ‘При выполнении метода C14N произошла ошибка:'»), Контекст); Возврат; КонецЕсли; Попытка ОписаниеОповещения = Новый ОписаниеОповещения( «Подписание_ПослеВыполненияHash_ПодписываемыйТег», ЭтотОбъект, Контекст, «Подписание_ПослеВыполненияHash_ПодписываемыйТег_Ошибка», ЭтотОбъект); Контекст.ОбъектКомпоненты.НачатьВызовHash( ОписаниеОповещения, КаноникализированныйТекстXML, Контекст.ДанныеАлгоритмаПодписания.OIDАлгоритмаХеширования, Контекст.ТипКриптопровайдера); Исключение ЗавершитьОперациюСОшибкой( Контекст, НСтр(«ru = ‘Ошибка вызова метода Hash компоненты XMLDSIG.'») + Символы.ПС + ПодробноеПредставлениеОшибки(ИнформацияОбОшибке())); КонецПопытки; КонецПроцедуры Процедура Подписание_ПослеВыполненияHash_ПодписываемыйТег_Ошибка(ИнформацияОбОшибке, СтандартнаяОбработка, Контекст) Экспорт СтандартнаяОбработка = Ложь; ЗавершитьОперациюСОшибкой( Контекст, НСтр(«ru = ‘Ошибка вызова метода Hash компоненты XMLDSIG.'») + Символы.ПС + ПодробноеПредставлениеОшибки(ИнформацияОбОшибке)); КонецПроцедуры Процедура Подписание_ПослеВыполненияHash_ПодписываемыйТег(DigestValue, Параметры, Контекст) Экспорт Если DigestValue = Неопределено Тогда НачатьПолучениеТекстаОшибки(НСтр(«ru = ‘При выполнении метода Hash произошла ошибка:'»), Контекст); Возврат; КонецЕсли; Контекст.КонвертSOAP = СтрЗаменить(Контекст.КонвертSOAP, «%DigestValue%», DigestValue); Попытка ОписаниеОповещения = Новый ОписаниеОповещения( «Подписание_ПослеВыполненияC14N_SignedInfo», ЭтотОбъект, Контекст, «Подписание_ПослеВыполненияC14N_SignedInfo_Ошибка», ЭтотОбъект); Контекст.ОбъектКомпоненты.НачатьВызовC14N( ОписаниеОповещения, Контекст.КонвертSOAP, Контекст.ДанныеАлгоритмаПодписания.XPathSignedInfo); Исключение ЗавершитьОперациюСОшибкой( Контекст, НСтр(«ru = ‘Ошибка вызова метода C14N компоненты XMLDSIG.'») + Символы.ПС + ПодробноеПредставлениеОшибки(ИнформацияОбОшибке())); КонецПопытки; КонецПроцедуры Процедура Подписание_ПослеВыполненияC14N_SignedInfo_Ошибка(ИнформацияОбОшибке, СтандартнаяОбработка, Контекст) Экспорт СтандартнаяОбработка = Ложь; ЗавершитьОперациюСОшибкой( Контекст, НСтр(«ru = ‘Ошибка вызова метода C14N компоненты XMLDSIG.'») + Символы.ПС + ПодробноеПредставлениеОшибки(ИнформацияОбОшибке)); КонецПроцедуры Процедура Подписание_ПослеВыполненияC14N_SignedInfo(КаноникализированныйТекстXMLSignedInfo, Параметры, Контекст) Экспорт Если КаноникализированныйТекстXMLSignedInfo = Неопределено Тогда НачатьПолучениеТекстаОшибки(НСтр(«ru = ‘При выполнении метода C14N произошла ошибка:'»), Контекст); Возврат; КонецЕсли; Попытка Контекст.ОбъектКомпоненты.НачатьВызовSign( Новый ОписаниеОповещения(«Подписание_ПослеВыполненияSign», ЭтотОбъект, Контекст), КаноникализированныйТекстXMLSignedInfo, Контекст.СертификатКриптографииBase64, Контекст.МенеджерКриптографии.ПарольДоступаКЗакрытомуКлючу); Исключение ЗавершитьОперациюСОшибкой( Контекст, НСтр(«ru = ‘Ошибка вызова метода Sign компоненты XMLDSIG.'») + Символы.ПС + ПодробноеПредставлениеОшибки(ИнформацияОбОшибке())); КонецПопытки; КонецПроцедуры Процедура Подписание_ПослеВыполненияSign_Ошибка(ИнформацияОбОшибке, СтандартнаяОбработка, Контекст) Экспорт СтандартнаяОбработка = Ложь; ЗавершитьОперациюСОшибкой( Контекст, НСтр(«ru = ‘Ошибка вызова метода Sign компоненты XMLDSIG.'») + Символы.ПС + ПодробноеПредставлениеОшибки(ИнформацияОбОшибке)); КонецПроцедуры Процедура Подписание_ПослеВыполненияSign(SignatureValue, Параметры, Контекст) Экспорт Если SignatureValue = Неопределено Тогда НачатьПолучениеТекстаОшибки(НСтр(«ru = ‘При выполнении метода Sign произошла ошибка:'»), Контекст); Возврат; КонецЕсли; КонвертSOAP = СтрЗаменить(Контекст.КонвертSOAP, «%SignatureValue%», SignatureValue); ВыполнитьОбработкуОповещения(Контекст.ОповещенияПриЗавершении.Успех, КонвертSOAP); КонецПроцедуры Процедура НачатьПроверкуПодписиСообщенияSOAP(ИнформацияМодуляКриптографии, Контекст) Экспорт ИмяКриптопровайдера = ИнформацияМодуляКриптографии.Имя; ТипКриптопровайдера = ЭлектроннаяПодписьXMLDSIGВызовСервераПовтИсп.ТипКриптопровайдераПоИмени(ИмяКриптопровайдера); Если ТипКриптопровайдера = Неопределено Тогда ЗавершитьОперациюСОшибкой( Контекст, СтрШаблон( НСтр(«ru = ‘Не удалось определить тип криптопровайдена %1′»), ИнформацияМодуляКриптографии.Имя) + Символы.ПС + ПодробноеПредставлениеОшибки(ИнформацияОбОшибке())); Возврат; КонецЕсли; Контекст.ТипКриптопровайдера = ТипКриптопровайдера; Контекст.ИмяКриптопровайдера = ИмяКриптопровайдера; Попытка ОписаниеОповещения = Новый ОписаниеОповещения( «Проверка_ПослеВыполненияGetSignOIDFromCert», ЭтотОбъект, Контекст, «Проверка_ПослеВыполненияGetSignOIDFromCert_Ошибка», ЭтотОбъект); Контекст.ОбъектКомпоненты.НачатьВызовGetSignOIDFromCert( ОписаниеОповещения, Контекст.СертификатКриптографииBase64); Исключение ЗавершитьОперациюСОшибкой( Контекст, НСтр(«ru = ‘Ошибка вызова метода GetSignOIDFromCert компоненты XMLDSIG.'») + Символы.ПС + ПодробноеПредставлениеОшибки(ИнформацияОбОшибке())); КонецПопытки; КонецПроцедуры Процедура Проверка_ПослеВыполненияGetSignOIDFromCert_Ошибка(ИнформацияОбОшибке, СтандартнаяОбработка, Контекст) Экспорт СтандартнаяОбработка = Ложь; ЗавершитьОперациюСОшибкой( Контекст, НСтр(«ru = ‘Ошибка вызова метода GetSignOIDFromCert компоненты XMLDSIG.'») + Символы.ПС + ПодробноеПредставлениеОшибки(ИнформацияОбОшибке)); КонецПроцедуры Процедура Проверка_ПослеВыполненияGetSignOIDFromCert(OIDАлгоритмаПодписи, Параметры, Контекст) Экспорт Если OIDАлгоритмаПодписи = Неопределено Тогда НачатьПолучениеТекстаОшибки(НСтр(«ru = ‘При выполнении метода GetSignOIDFromCert произошла ошибка:'»), Контекст); Возврат; КонецЕсли; Если OIDАлгоритмаПодписи <> Контекст.ДанныеАлгоритмаПодписания.OIDАлгоритмаПодписи Тогда ЗавершитьОперациюСОшибкой( Контекст, НСтр(«ru = ‘Ошибка вызова метода Hash компоненты XMLDSIG.'») + Символы.ПС + ПодробноеПредставлениеОшибки(ИнформацияОбОшибке())); Иначе Попытка ОписаниеОповещения = Новый ОписаниеОповещения( «Проверка_ПослеВыполненияC14N_SignedInfo», ЭтотОбъект, Контекст, «Проверка_ПослеВыполненияC14N_SignedInfo_Ошибка», ЭтотОбъект); Контекст.ОбъектКомпоненты.НачатьВызовC14N( ОписаниеОповещения, Контекст.КонвертSOAP, Контекст.ДанныеАлгоритмаПодписания.XPathSignedInfo); Исключение ЗавершитьОперациюСОшибкой( Контекст, НСтр(«ru = ‘Ошибка вызова метода C14N компоненты XMLDSIG.'») + Символы.ПС + ПодробноеПредставлениеОшибки(ИнформацияОбОшибке())); КонецПопытки; КонецЕсли; КонецПроцедуры Процедура Проверка_ПослеВыполненияC14N_SignedInfo_Ошибка(ИнформацияОбОшибке, СтандартнаяОбработка, Контекст) Экспорт СтандартнаяОбработка = Ложь; ЗавершитьОперациюСОшибкой( Контекст, НСтр(«ru = ‘Ошибка вызова метода C14N компоненты XMLDSIG.'») + Символы.ПС + ПодробноеПредставлениеОшибки(ИнформацияОбОшибке)); КонецПроцедуры Процедура Проверка_ПослеВыполненияC14N_SignedInfo(КаноникализированныйТекстXMLSignedInfo, Параметры, Контекст) Экспорт Если КаноникализированныйТекстXMLSignedInfo = Неопределено Тогда НачатьПолучениеТекстаОшибки(НСтр(«ru = ‘При выполнении метода C14N произошла ошибка:'»), Контекст); Возврат; КонецЕсли; SignatureValue = ИнтеграцияГИСМКлиентСервер.НайтиВXML(Контекст.КонвертSOAP, «SignatureValue»); СертификатКриптографииBase64 = ИнтеграцияГИСМКлиентСервер.НайтиВXML(Контекст.КонвертSOAP, «wsse:BinarySecurityToken»); Попытка ОписаниеОповещения = Новый ОписаниеОповещения( «Проверка_ПослеВыполненияVerifySign», ЭтотОбъект, Контекст, «Проверка_ПослеВыполненияVerifySign_Ошибка», ЭтотОбъект); Контекст.ОбъектКомпоненты.НачатьВызовVerifySign( ОписаниеОповещения, КаноникализированныйТекстXMLSignedInfo, SignatureValue, СертификатКриптографииBase64, Контекст.ТипКриптопровайдера); Исключение ЗавершитьОперациюСОшибкой( Контекст, НСтр(«ru = ‘Ошибка вызова метода VerifySign компоненты XMLDSIG.'») + Символы.ПС + ПодробноеПредставлениеОшибки(ИнформацияОбОшибке())); КонецПопытки; КонецПроцедуры Процедура Проверка_ПослеВыполненияVerifySign_Ошибка(ИнформацияОбОшибке, СтандартнаяОбработка, Контекст) Экспорт СтандартнаяОбработка = Ложь; ЗавершитьОперациюСОшибкой( Контекст, НСтр(«ru = ‘Ошибка вызова метода VerifySign компоненты XMLDSIG.'») + Символы.ПС + ПодробноеПредставлениеОшибки(ИнформацияОбОшибке)); КонецПроцедуры Процедура Проверка_ПослеВыполненияVerifySign(ПодписьВерна, Параметры, Контекст) Экспорт Если ПодписьВерна = Неопределено Тогда НачатьПолучениеТекстаОшибки(НСтр(«ru = ‘При выполнении метода VerifySign произошла ошибка:'»), Контекст); Возврат; КонецЕсли; Если Не ПодписьВерна Тогда ЗавершитьОперациюСОшибкой( Контекст, НСтр(«ru = ‘Подпись не верна (SignatureValue не корректно.'»)); Иначе Попытка ОписаниеОповещения = Новый ОписаниеОповещения( «Проверка_ПослеВыполненияC14N_ПодписываемыйТег», ЭтотОбъект, Контекст, «Проверка_ПослеВыполненияC14N_ПодписываемыйТег_Ошибка», ЭтотОбъект); Контекст.ОбъектКомпоненты.НачатьВызовC14N( ОписаниеОповещения, Контекст.КонвертSOAP, Контекст.ДанныеАлгоритмаПодписания.XPathПодписываемыйТег); Исключение ЗавершитьОперациюСОшибкой( Контекст, НСтр(«ru = ‘Ошибка вызова метода C14N компоненты XMLDSIG.'») + Символы.ПС + ПодробноеПредставлениеОшибки(ИнформацияОбОшибке())); КонецПопытки; КонецЕсли; КонецПроцедуры Процедура Проверка_ПослеВыполненияC14N_ПодписываемыйТег_Ошибка(ИнформацияОбОшибке, СтандартнаяОбработка, Контекст) Экспорт СтандартнаяОбработка = Ложь; ЗавершитьОперациюСОшибкой( Контекст, НСтр(«ru = ‘Ошибка вызова метода C14N компоненты XMLDSIG.'») + Символы.ПС + ПодробноеПредставлениеОшибки(ИнформацияОбОшибке)); КонецПроцедуры Процедура Проверка_ПослеВыполненияC14N_ПодписываемыйТег(КаноникализированныйТекстXMLПодписываемыйТег, Параметры, Контекст) Экспорт Если КаноникализированныйТекстXMLПодписываемыйТег = Неопределено Тогда НачатьПолучениеТекстаОшибки(НСтр(«ru = ‘При выполнении метода C14N произошла ошибка:'»), Контекст); Возврат; КонецЕсли; Попытка ОписаниеОповещения = Новый ОписаниеОповещения( «Проверка_ПослеВыполненияHash_ПодписываемыйТег», ЭтотОбъект, Контекст, «Проверка_ПослеВыполненияHash_ПодписываемыйТег_Ошибка», ЭтотОбъект); Контекст.ОбъектКомпоненты.НачатьВызовHash( ОписаниеОповещения, КаноникализированныйТекстXMLПодписываемыйТег, Контекст.ДанныеАлгоритмаПодписания.OIDАлгоритмаХеширования, Контекст.ТипКриптопровайдера); Исключение ЗавершитьОперациюСОшибкой( Контекст, НСтр(«ru = ‘Ошибка вызова метода Hash компоненты XMLDSIG.'») + Символы.ПС + ПодробноеПредставлениеОшибки(ИнформацияОбОшибке())); КонецПопытки; КонецПроцедуры Процедура Проверка_ПослеВыполненияHash_ПодписываемыйТег_Ошибка(ИнформацияОбОшибке, СтандартнаяОбработка, Контекст) Экспорт СтандартнаяОбработка = Ложь; ЗавершитьОперациюСОшибкой( Контекст, НСтр(«ru = ‘Ошибка вызова метода Hash компоненты XMLDSIG.'») + Символы.ПС + ПодробноеПредставлениеОшибки(ИнформацияОбОшибке)); КонецПроцедуры Процедура Проверка_ПослеВыполненияHash_ПодписываемыйТег(HashValue, Параметры, Контекст) Экспорт Если HashValue = Неопределено Тогда НачатьПолучениеТекстаОшибки(НСтр(«ru = ‘При выполнении метода Hash произошла ошибка:'»), Контекст); Возврат; КонецЕсли; DigestValue = ИнтеграцияГИСМКлиентСервер.НайтиВXML(Контекст.КонвертSOAP, «DigestValue»); ПодписьВерна = (DigestValue = HashValue); Если Не ПодписьВерна Тогда ЗавершитьОперациюСОшибкой( Контекст, НСтр(«ru = ‘Подпись не верна (SignatureValue корректно, отличается DigestValue).'»)); Иначе ДвоичныеДанные = Base64Значение(Контекст.СертификатКриптографииBase64); ДатаПодписания = ЭлектроннаяПодписьКлиент.ДатаПодписания(ДвоичныеДанные); Если Не ЗначениеЗаполнено(ДатаПодписания) Тогда ДатаПодписания = Неопределено; КонецЕсли; ВозвращаемоеЗначение = Новый Структура; ВозвращаемоеЗначение.Вставить(«Сертификат», Контекст.СертификатКриптографии); ВозвращаемоеЗначение.Вставить(«ДатаПодписания», ДатаПодписания); ВыполнитьОбработкуОповещения(Контекст.ОповещенияПриЗавершении.Успех, ВозвращаемоеЗначение); КонецЕсли; КонецПроцедуры Процедура НачатьПолучениеТекстаОшибки(НачалоОписанияТекстаОшибки, Контекст) Попытка Контекст.Вставить(«НачалоОписанияТекстаОшибки», НачалоОписанияТекстаОшибки); ОписаниеОповещения = Новый ОписаниеОповещения( «ПослеВыполненияGetLastError», ЭтотОбъект, Контекст, «ПослеВыполненияGetLastError_Ошибка», ЭтотОбъект); Контекст.ОбъектКомпоненты.НачатьВызовGetLastError(ОписаниеОповещения); Исключение ЗавершитьОперациюСОшибкой( Контекст, НСтр(«ru = ‘Ошибка вызова метода GetLastError компоненты XMLDSIG.'») + Символы.ПС + ПодробноеПредставлениеОшибки(ИнформацияОбОшибке())); КонецПопытки; КонецПроцедуры Процедура ПослеВыполненияGetLastError_Ошибка(ИнформацияОбОшибке, СтандартнаяОбработка, Контекст) Экспорт СтандартнаяОбработка = Ложь; ЗавершитьОперациюСОшибкой( Контекст, НСтр(«ru = ‘Ошибка вызова метода GetLastError компоненты XMLDSIG.'») + Символы.ПС + ПодробноеПредставлениеОшибки(ИнформацияОбОшибке)); КонецПроцедуры Процедура ПослеВыполненияGetLastError(ТекстОшибки, Параметры, Контекст) Экспорт ЗавершитьОперациюСОшибкой( Контекст, Контекст.НачалоОписанияТекстаОшибки + Символы.ПС + ТекстОшибки); КонецПроцедуры #КонецОбласти #Область Прочее Процедура ЗавершитьОперациюСОшибкой(Контекст, ТекстОшибки) Экспорт ВыполнитьОбработкуОповещения(Контекст.ОповещенияПриЗавершении.Ошибка, ТекстОшибки); КонецПроцедуры // Форматирует строку в формате base64, удаляя символы перевода строки и возврата каретки. // // Параметры: // СтрокаBase64 — Строка — строка, которая подлежит преобразования. // // Возвращаемое значение: // Строка — строка, из которой удалены символы перевода строки и возврата каретки. // Функция ОтформатироватьСтрокуBase64(СтрокаBase64) Значение = СтрЗаменить(СтрокаBase64, Символы.ВК, «»); Значение = СтрЗаменить(Значение, Символы.ПС, «»); Возврат Значение; КонецФункции #КонецОбласти #КонецОбласти
   cmex

01.06.20 — 11:16

Добрый день!

Для использования всех методов необходимо иметь УКЭП. Кто нибудь из 1С уже работал по API? Как получить тестовую УКЭП, как использовать уже имеющуюся УКЭП директора организации на Сервере, чтобы автоматом можно было подписывать? Кто сталкивался? Просветите, как процесс устроен? Слышал, про тестовый УЦ, где выдают УКЭП, но не понял как с этим работать. В общем, буду признателен за любую информацию

   cmex

1 — 01.06.20 — 14:21

up

   ChMikle

2 — 01.06.20 — 14:25

   cmex

3 — 01.06.20 — 17:08

(2) Спасибо

   timurhv

4 — 01.06.20 — 17:13

Тестовая УКЭП должна быть на имя директора (если стоит KIS, то его лучше закрыть, т.к. из-за сертификата тестового сервера не пускает дальше регистрировать):

https://ismp.wiki/index.php?title=Софт

Если выходит ошибка регистрации на тестовой площадке — писать в тех.поддержку ЧЗ.

   lodger

5 — 01.06.20 — 18:46

(0)

1) есть боевой и есть тестовый контур API

2) ключ можно использовать один и тот же.

3) нет нужды использовать ключ на сервере. достаточно сотворить токен авторизации на клиенте и отдать его на сервер.

4) у токена есть время жизни, 10 часов.

   cmex

6 — 02.06.20 — 13:29

(5) удалось получить сертификат в Тестовом подчиненном УЦ КриптоПро. Установил сертификат под своей учеткой в ОС

Далее пишу код

ИнтеграционныйСтенд = «int01.gismt.crpt.tech/api/v3/true-api»;

Соединение = Новый HTTPСоединение(ИнтеграционныйСтенд,,,,,, Новый ЗащищенноеСоединениеOpenSSL);

HTTPЗапрос = Новый HTTPЗапрос(«/auth/key»);

HTTPОтвет = Соединение.ВызватьHTTPМетод(«GET», HTTPЗапрос);    

СтрокаJSON = HTTPОтвет.ПолучитьТелоКакСтроку();

ЧтениеJSON = Новый ЧтениеJSON;

ЧтениеJSON.УстановитьСтроку(СтрокаJSON);

Структура = ПрочитатьJSON(ЧтениеJSON);

ЧтениеJSON.Закрыть();

МенеджерКриптографии = Новый МенеджерКриптографии(«Crypto-Pro GOST R 34.10-2001 Cryptographic Service Provider», «», 75);

Хранилище = МенеджерКриптографии.ПолучитьХранилищеСертификатов();

СертификатыХранилища = Хранилище.ПолучитьВсе();

Сертификат = СертификатыХранилища[0];    

ДД = ПолучитьДвоичныеДанныеИзСтроки(Структура.data);

Результат = МенеджерКриптографии.Подписать(ДД, Сертификат);

ЗаписьJSON = Новый ЗаписьJSON;

ЗаписьJSON.УстановитьСтроку();

ЗаписатьJSON(ЗаписьJSON, Структура);

СтрокаJSON = ЗаписьJSON.Закрыть();

Заголовки = Новый Соответствие;    

Заголовки.Вставить(«Content-Type», «application/json; charset=utf-8»);

Заголовки.Вставить(«cache-control», «no-cache»);

HTTPЗапрос = Новый HTTPЗапрос(«/auth/simpleSignIn», Заголовки);    

HTTPЗапрос.УстановитьТелоИзСтроки(СтрокаJSON);

Попытка

    HTTPОтвет = Соединение.ВызватьHTTPМетод(«POST», HTTPЗапрос);    

Исключение

    Сообщить(ОписаниеОшибки());

КонецПопытки;

Вываливается ошибка «Ошибка при получении свойства сертификата (0x00000000)»

Это я не так пользуюсь менеджером или этот ключ не может в принципе подписать? Читал про какой то закрытый ключ, но его в сертификате не увидел

Вот некая инфа по сертификату

Версия    «V3»    Строка

ДатаНачала    01.06.2020 8:26:46    Дата

ДатаОкончания    01.09.2020 8:36:46    Дата

CN    «Тестовый подчиненный УЦ ООО «КРИПТО-ПРО» ГОСТ 2012 (УЦ 2.0)»    Строка

Крипто ПРО csp 4.0

   cmex

7 — 02.06.20 — 13:32

(5) у меня регламент будет работать с API, то есть сервер, поэтому на клиент доступа нет. может есть какой вариант другой? я слабо представляю как устроена вся схема работы эцп

   Garykom

8 — 02.06.20 — 13:37

(7) У вас два варианта или самим разбираться или кого то нанимать кто уже разобрался

   cmex

9 — 02.06.20 — 13:49

(8) почему 2? я бы помог, если бы знал, форму для этого и нужен, имхо

   cmex

10 — 02.06.20 — 13:58

(9) *форум

   Garykom

11 — 02.06.20 — 14:23

(9) Потому что там нет ничего сложного, обычная работа, которую бесплатно хрен кто будет делать.

Мануалы есть и они достаточно вменяемы, есть форум ЧЗ если что и прочие форумы.

В т.ч. как запросы по http делать или с криптопро/сертификатами работать

   Garykom

12 — 02.06.20 — 14:24

Ты конкретные вопросы задавай что не получается а не «помогите мне кто нибудь — сделайте за меня или дайте разжеванное/готовое»

   cmex

13 — 02.06.20 — 14:37

(12) ок, по мануалу разобрался. подписать вроде удалось, но в ответ на post запрос прилетает {«error_message»:»Ошибка при проверке подписи»}

подписываю, кодирую и отправляю так

ДД = ПолучитьДвоичныеДанныеИзСтроки(data, КодировкаТекста.UTF8);

РезультатДД = МенеджерКриптографии.Подписать(ДД, Сертификат);

РезультатСтрока = Base64Строка(РезультатДД);

может я в двоичные данные строку исходную не так перевожу? как правильно подписать?

   Garykom

14 — 02.06.20 — 14:43

Вариант синтаксиса: Данные подписи сохраняются в файл

Синтаксис:

Подписать(<ИсходныеДанные>, <ВыходныеДанные>, <Сертификат>)

Параметры:

<ИсходныеДанные> (обязательный)

Тип: Строка, ДвоичныеДанные, Поток, ПотокВПамяти, ФайловыйПоток.

Исходные данные для подписывания.

Данные могут размещаться в файле (в этом случае указывается имя файла) или представлены как ДвоичныеДанные.

<ВыходныеДанные> (обязательный)

Тип: Строка: Поток, ПотокВПамяти, ФайловыйПоток.

Имя файла или поток, в который будут выгружены подписанные данные.

<Сертификат> (необязательный)

Тип: СертификатКриптографии.

Используемый сертификат криптографии.

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

Описание варианта метода:

Подписанные данные записываются в файл. Возвращает Неопределено.

   cmex

15 — 02.06.20 — 14:46

(14) намекаете, что надо пописать строку, предварительно не переводя в ДД?

   cmex

16 — 02.06.20 — 14:46

(15) *подДписать)

   Garykom

17 — 02.06.20 — 14:47

(14)+ Только уверен что апи требует прикрепленную подпись?

Если нужна открепленная то стандартный менеджер не пойдет и надо нечто вроде http://catalog.mista.ru/public/1058940/

   cmex

18 — 02.06.20 — 14:47

(17) из Мануала —  data String + Подписанные УКЭП зарегистрированного УОТ случайные данные в base64 (ЭП

присоединенная)

   Garykom

19 — 02.06.20 — 14:47

(16) А фуй знает что надо, надо разбираться

Лично я не имею представления по какому мануалу и что твоя делать

   cmex

20 — 02.06.20 — 14:48

(19) v5 — Документация для партнёров по True API

   Garykom

21 — 02.06.20 — 14:49

(18) Тогда попробуй имя файла входного и выходного указывать в Подписать

А затем сравнить с тем что выходит другими способами подписи того же файла.

Есть у криптопро cryptcp и csptest

   Garykom

22 — 02.06.20 — 14:51

   Garykom

23 — 02.06.20 — 14:55

   cmex

24 — 02.06.20 — 15:30

все попробовал — не взлетает пока. чую, что близко, но пока никак

   timurhv

25 — 02.06.20 — 16:27

Процедура ОбновитьТокен(ВидAPI, СледующееОповещение)

    АдресМетода = «/api/v3/auth/cert/key»;

    …

    HTTPСервисЗапрос = Новый HTTPСоединение(СтруктураПодключения[АдресAPI],,,,,60, Новый ЗащищенноеСоединениеopenSSL());

    Запрос = Новый HTTPЗапрос(АдресМетода);

    Ответ = HTTPСервисЗапрос.Получить(Запрос);

    Если Ответ.КодСостояния = 200 Тогда

        …

        МассивСертификатов = Новый Массив();

        Если ЗначениеЗаполнено(СертификатыКлючейЭлектроннойПодписиИШифрования) Тогда

            МассивСертификатов.Добавить(СертификатыКлючейЭлектроннойПодписиИШифрования);

        КонецЕсли;

        СтруктураОсновныхДанных = Новый Структура();

        СтруктураОсновныхДанных.Вставить(«СтрокаJSON», ОтветСтруктура.data);

        СтруктураОсновныхДанных.Вставить(«МассивСертификатов», МассивСертификатов);

        СтруктураОсновныхДанных.Вставить(«Открепленная», Ложь);

        СтруктураОсновныхДанных.Вставить(«СсылкаДокумента», «»);

        
        СтруктураДополнительныхДанных = Новый Структура();

        СтруктураДополнительныхДанных.Вставить(«СертификатПользователя», СертификатыКлючейЭлектроннойПодписиИШифрования);

        СтруктураДополнительныхДанных.Вставить(«ОтветСтруктура», ОтветСтруктура);

        СтруктураДополнительныхДанных.Вставить(«СледующееОповещение», СледующееОповещение);

        СтруктураДополнительныхДанных.Вставить(«HTTPСервисЗапрос», HTTPСервисЗапрос);

        ИнтеграцияИСМПСлужебныйКлиент.Подписать(СтруктураОсновныхДанных, Новый ОписаниеОповещения(«ОбработатьСообщения_ПриЗавершенииОперацииПодписи», ЭтотОбъект, СтруктураДополнительныхДанных));

    КонецЕсли;

КонецПроцедуры

Процедура ОбработатьСообщения_ПриЗавершенииОперацииПодписи(ПодписанныеСообщения, Контекст) Экспорт

    Если ПодписанныеСообщения = Неопределено Тогда

        Возврат;

    КонецЕсли;

    
    Для Каждого ЭлементСообщения Из ПодписанныеСообщения Цикл

        ПодписанныйФайл = ИнтеграцияИСКлиентСервер.ДвоичныеДанныеBase64(ЭлементСообщения.СвойстваПодписи.Подпись);

        HTTPСервисЗапрос = Контекст.HTTPСервисЗапрос;

        Тело = «{

        |»»uuid»»: «»» + Контекст.ОтветСтруктура.uuid + «»»,

        |»»data»»: «»» + ПодписанныйФайл + «»»

        |}»;

        АдресМетода = «api/v3/auth/cert/»;

        HTTPЗапрос = Новый HTTPЗапрос(АдресМетода);

        HTTPЗапрос.Заголовки.Вставить(«Content-Type»,»application/json;charset=UTF-8″);

        HTTPЗапрос.УстановитьТелоИзСтроки(Тело);

        Ответ = HTTPСервисЗапрос.ОтправитьДляОбработки(HTTPЗапрос);

    КонецЦикла;

КонецПроцедуры

   timurhv

26 — 02.06.20 — 16:41

(13) Base64 = Base64Строка(ДвоичныеДанные);

Base64 = СтрЗаменить(Base64, Символы.ПС, «»);

Base64 = СтрЗаменить(Base64, Символы.ВК, «»);

Символы переноса скорее забыли убрать

   cmex

27 — 03.06.20 — 11:16

(26) это из какой конфы? ИнтеграцияИСКлиентСервер.ДвоичныеДанныеBase64(ЭлементСообщения.СвойстваПодписи.Подпись);

   cmex

28 — 03.06.20 — 11:33

кажись разобрался, вернусь с результатом

   cmex

29 — 03.06.20 — 17:18

причина была в том, что 1совский метод Подписать НЕ включает данные в подпись, как того требует ЧЗ. Для этого необходимо использовать внешнюю компоненту.

вот код рабочий. саму компоненту дернул из 11.4

МенеджерКриптографии = Новый МенеджерКриптографии(«Crypto-Pro GOST R 34.10-2012 Cryptographic Service Provider», «», 80);

    Хранилище = МенеджерКриптографии.ПолучитьХранилищеСертификатов();

    СертификатыХранилища = Хранилище.ПолучитьВсе();

    Сертификат = СертификатыХранилища[0];

    
    Макет = ПолучитьМакетНаСервере();

    Адрес = ПоместитьВоВременноеХранилище(Макет);     

    Если ПодключитьВнешнююКомпоненту(Адрес, «XMLDSignAddInSymbolicName», ТипВнешнейКомпоненты.Native) Тогда

        Попытка

            ОбъектКомпоненты = Новый(«AddIn.XMLDSignAddInSymbolicName.XMLDSignAddIn»);

            Если ОбъектКомпоненты = Неопределено Тогда

                ВызватьИсключение НСтр(«ru = ‘Оператор Новый вернул Неопределено'»);

            КонецЕсли;

        Исключение

            ВызватьИсключение НСтр(«ru = ‘Не удалось чуток'»);            

        КонецПопытки;        

    КонецЕсли;    

    ОбъектКомпоненты.ПутьККриптопровайдеру = «»;

    СертификатКриптографииBase64 = СертификатКриптографииBase64(Сертификат.Выгрузить());    

    Попытка

        SignatureValue = ОбъектКомпоненты.CMSSign(

            Структура.data, //данные тип строка

            СертификатКриптографииBase64, //выгруженный в ДД сертификат в base64

            «», //пароль

            0, //тип подписи «CAdES-BES»

            Ложь, //открепленная

            17, //РежимВключенияСертификатовКриптографии.ВключатьПолнуюЦепочку

            0) //тип входных данных Строка;

    Исключение

        ВызватьИсключение НСтр(«ru = ‘Ошибка вызова метода CMSSign компоненты XMLDSig.'») + Символы.ПС + ПодробноеПредставлениеОшибки(ИнформацияОбОшибке());

    КонецПопытки;    

    Подпись = Base64Значение(SignatureValue);

    
    Структура.data = ДвоичныеДанныеBase64(Подпись);

    
    ЗаписьJSON = Новый ЗаписьJSON;

    ЗаписьJSON.УстановитьСтроку();

    ЗаписатьJSON(ЗаписьJSON, Структура);

    СтрокаJSON = ЗаписьJSON.Закрыть();

    
    Заголовки = Новый Соответствие;    

    Заголовки.Вставить(«Content-Type», «application/json; charset=utf-8»);

    Заголовки.Вставить(«cache-control», «no-cache»);

    HTTPЗапрос = Новый HTTPЗапрос(«/auth/simpleSignIn», Заголовки);    

    HTTPЗапрос.УстановитьТелоИзСтроки(СтрокаJSON);

    Попытка

        HTTPОтвет = Соединение.ВызватьHTTPМетод(«POST», HTTPЗапрос);    

    Исключение

        Сообщить(ОписаниеОшибки());

    КонецПопытки;

    
    Если Не HTTPОтвет.КодСостояния = 200 Тогда

        Сообщить(«Код состояния не равен 200»);

        Возврат;

    КонецЕсли;

    
    СтрокаJSON = HTTPОтвет.ПолучитьТелоКакСтроку();

    ЧтениеJSON = Новый ЧтениеJSON;

    ЧтениеJSON.УстановитьСтроку(СтрокаJSON);

    Структура = ПрочитатьJSON(ЧтениеJSON);

    ЧтениеJSON.Закрыть();

    
    Токен = Структура.token;

   timurhv

30 — 04.06.20 — 10:10

(29) БСП нет разве в конфе?

   cmex

31 — 04.06.20 — 13:14

(30) нет, 11.1

  

VitShvets

32 — 19.06.20 — 16:51

(29) Спасибо тебе, добрый человек.

Оглавление

  1. Цель статьи
  2. Криптография вокруг нас
  3. Немного истории вопроса
    1. Способы шифрования без устройств
  4. Современные способы шифрования
    1. Симметричное и асимметричное шифрование
    2. Несколько известных алгоритмов шифрования
  5. Криптоанализ
  6. Криптографические примитивы
    1. Хеширование
    2. Шифрование
    3. Электронная подпись
  7. Виды электронной подписи в России
  8. Криптопровайдеры
  9. Инициализация криптографии в 1С
  10. Установка, хранение и связь ключей, сертификаты, контейнеры
    1. Сертификаты
    2. Закрытый ключ
    3. Связь ключей и криптопровайдера
  11. Сертификаты в коде 1С
  12. Стандарты, форматы
    1. Формат CMS/CAdES
    2. CMS в коде 1С
    3. Подпись PDF
    4. Формат XMLDSig/XAdES
    5. Что такое Каноникализация XML?
    6. Дополнительные трансформации XML
    7. Создание подписи XMLDsig
  13. Продолжение следует

Цель статьи

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

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

Криптография вокруг нас

Области применения криптографии разнообразны.
Это явное применение ее «по назначению»:

  • хранения шифрованной конфиденциальной информации;
  • электронная подпись, подтверждение подлинности;
  • шпионские послания.

Применение, как один из вспомогательных элементов и составляющей более комплексных систем, протоколов, процессов:

  • механизмы авторизации в различных интернет-сервисах;
  • протоколы передачи данных (HTTPS, TLS, SSL);
  • шифрование голосового трафика в сотовых сетях.

Масса вещей вокруг нас тоже содержит криптографию:

  • данные в нашем телефоне или компьютере часто зашифрованы;
  • сообщения в некоторых мессенджерах;
  • информация на банковской карте;
  • брелок сигнализации автомобиля.

Всего и не перечислить.

Множество механизмов и технологий используют криптографию неявно, глубоко в библиотеках, и мы не задумываемся, что это так, даже используя эти технологии.

Например, соединение по протоколу HTTPS и соответствующий объект 1С HTTPСоединение. Этот объект использует шифрование, но это происходит само по себе, без нашего участия. И не требует вмешательства и даже понимания, как это работает.

Иногда же возникают задачи, требующие более глубокого погружения в тему.

Сама криптография (как, в общем-то, следует из названия) — это наука о шифровании. Попросту говоря, шифрование занимается тем, что превращает понятные данные в непонятные, иногда с возможностью обратного действия, а иногда — с отсутствием такой возможности.

Немного истории вопроса

Первые методы шифрования были основаны на чисто физических объектах.

Скитала (сцитала)

Скитала (сцитала)

Представляет собой стержень, на который наматывается длинный, узкий (в ширину буквы) пергамент. Вдоль оси стержня записывается текст (в несколько рядов). После разматывания сообщение превращается в набор букв. Для расшифровки требуется стержень такого же диаметра.

Линейка Энея

Линейка Энея

Линейка Энея

На линейку возле засечек нанесены буквы и имеется «нулевое» отверстие. Буква кодируется расстоянием между «нулевым» отверстием и соответствующей прорезью. То есть, нить протягивают между этими отверстиями и завязывают узелок, затем продевают обратно в «нулевое» отверстие и в прорезь следующей буквы. Последовательно на нити завязываются узелки для всего текста. Расстояния между узелками — коды букв. Обратная расшифровка требует такой же линейки, с такими же буквами.

Диск Энея

Диск Энея

Диск имеет отверстия, каждое закодировано буквой. Нить просто последовательно продевается через буквы-отверстия, соответствующие тексту послания. Вытягивая нить обратно, восстанавливаем текст. Секретность состоит в том, что диск можно сломать или быстро вытащить нить и все — сообщение прочитано быть не может.

Решётка Кардано

Решётка Кардано

Послание прячется внутри внешне безобидного текста, для расшифровки требуется наложить на текст шаблон (решетку) с отверстиями и прочитать полученное.

В таких способах шифрования подразумевается, что и у отправителя, и у получателя сообщений есть одинакового вида приборы, по сути это древний пример симметричного шифрования. Ну, а ключом симметричного шифра в данном случае является, по сути, набор физических характеристик предмета, то есть геометрические размеры, расположение дырочек, засечек и т д. И для соблюдения секретности нужно держать в тайне сам предмет — инструмент шифрования.

Всем известная Энигма

Всем известная Энигма

Основа этой машины — шифры замены, когда алфавиту исходного текста сопоставляются другой набор символов и производится замена (как, к примеру, Шифр Цезаря).

Для каждой буквы сообщения Энигма выполняла последовательно несколько таких замен, при этом на каждой букве менялся сам шифр замены, то есть соответствие символов.

Способы шифрования без устройств

Другие способы шифрования заключались уже в некотором алгоритме по перестановке или замене букв, и здесь не было устройства, но необходимо было знать ключ. Например, Шифр Цезаря — для него требуется знать, на сколько символов мы сдвинули алфавит вправо или влево.

Шифр Цезаря

Символы алфавита с номером N заменяются на символы с номером N + k.

Шифр Цезаря

Атбаш

Буквы заменяются на симметричные им буквы с конца алфавита.

Атбаш

«Пляшущие человечки»

Нельзя не вспомнить шифр из произведения Конан-Дойля «Пляшущие человечки». Это простейший шифр замены (букв на пиктограммы)

Пляшущие человечки

Современные способы шифрования

Современная криптография не полагается на какие-то механические и физические устройства, а применяет исключительно математический аппарат для преобразования данных.

Физические устройства используются только в качестве вычислительных машин или защищённого хранилища для секретных ключей.

При этом, сами методы шифрования не держатся в секрете, как, например, физические механизмы, использовавшиеся когда-то. Алгоритмы свободно распространяются и вполне могут быть воспроизведены.

Но алгоритмы предполагают входные параметры, называемые ключами, и вся секретность как раз содержится в ключах, так как от использования правильного ключа зависит выходной результат того или иного вычисления.

Симметричное и асимметричное шифрование

Алгоритмы шифрования разделяют на два класса: с закрытым ключом (симметричное шифрование) и с открытым ключом (асимметричное).

При симметричном шифровании ключ один, он используется и для операции шифрования, и для расшифровки.

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

Несколько известных алгоритмов шифрования

Симметричные шифры

  • XOR, шифр Вернама. Это, наверное, самый простой шифр. К каждому байту текста применяется операция XOR с соответствующим байтом ключа. Ключ имеет ту же длину, что и текст сообщения.
  • RC4. В данном шифре используется XOR не с заранее заготовленным ключом, а с последовательностью псевдослучайных чисел. Сама последовательность генерируется на основе короткого закрытого ключа. Алгоритм генерации таков, что обе стороны получают одинаковый набор псевдослучайных чисел. Это позволяет на одной стороне зашифровать этими числами, а на другой такими же числами расшифровать. Передавать сами числа, то есть ключ, не требуется.
  • DES — это устаревший стандарт шифрования США (Data Encryption Standard). С ростом производительности компьютеров, стала все более вероятна возможность вскрытия шифра обычным перебором или более сложной атакой, и он был заменен на AES.
  • AES (Rijndael) — текущий стандарт шифрования в США. Интересно, что операции, требуемые для шифрования этим алгоритмом, встроены непосредственно в систему команд процессоров Intel x86 (расширение AES).

Асимметричные шифры

  • На основе факторизации целых чисел:
    • RSA.
  • На основе дискретного логарифмирования:
    • DSA,
    • ГОСТ Р 34.10-94.
  • На эллиптических кривых:
    • ECDSA,
    • ГОСТ Р 34.10-2012.

В основе надежности асимметричных шифров лежит сложность некоторых математических задач.

В алгоритме RSA, и подобных ему, это задачи факторизации, то есть разложения на простые множители больших чисел.

Перемножить два числа достаточно легко, при этом обратная задача является вычислительно сложной. С увеличением самих чисел становится практически невозможно выполнить ее за разумное время.

Одно из самых известных применений RSA — в криптографических протоколах, используемых в интернете — SSL и TLS.

SSL (англ. Secure Sockets Layer — уровень защищённых сокетов) — криптографический протокол, который подразумевает более безопасную связь. Он использует асимметричную криптографию для аутентификации ключей обмена, симметричное шифрование для сохранения конфиденциальности, коды аутентификации сообщений для целостности сообщений.

Протокол широко использовался для обмена мгновенными сообщениями и передачи голоса через IP (англ. Voice over IP — VoIP) в таких приложениях, как электронная почта, интернет-факс и др. В 2014 году правительство США сообщило об уязвимости в текущей версии протокола. SSL должен быть исключён из работы в пользу TLS (см. CVE-2014-3566).

SSL изначально разработан компанией Netscape Communications для добавления протокола HTTPS в свой веб-браузер Netscape Navigator. Впоследствии на основании протокола SSL 3.0 был разработан и принят стандарт RFC, получивший имя TLS.

Wikipedia®

В схеме Эль-Гамаля сложность состоит в получении значения дискретного логарифма в конечном поле. По этой схеме построены алгоритмы DSA, шифрование по стандарту ГОСТ Р 34.10-94.

Еще более стойкими к взлому считаются алгоритмы, основанные на сложности вычисления дискретного логарифма в группе точек эллиптической кривой (математические операции совершаются не над целыми числами, а над точками кривой). Такая схема используется в алгоритме ECDSA (по сути, это модификация DSA под эллиптические кривые). Кроме того, алгоритмы из последних стандартов ГОСТ построены на этой схеме (ГОСТ 2001, 2012, 2018).

Пример алгоритма работы шифра RSA

Сначала выполняется вычисление (создание) ключей:

  • Выбирается два произвольных простых числа: p и q
  • Вычисляется их произведение n = p × q
  • Вычисляется функция Эйлера для числа n: φ = φ (n) = (p — 1) × (q — 1)
    • Функция Эйлера дает количество чисел, взаимно простых с n и меньших n.
  • Выбирается число e. Оно должно быть взаимно простым с φ. Это число называется «публичная экспонента».
  • Вычисляется число d — обратное к e по модулю φ. Это число называется «приватная экспонента».

Теперь пара чисел [n, d] — это закрытый ключ, [n, e] — открытый.
То есть, при помощи пары чисел [n,e] можно зашифровать число, и только при помощи чисел [n, d] можно легко получить его обратно.

Предположим, нам надо зашифровать число M (M должно быть меньше n).

Для этого вычисляется значение C = Me mod n. Значение с — это и есть шифротекст, зашифрованное сообщение.

Для расшифровки нужно вычислить значение M’ = Cd mod n . Полученное значение M’ будет равно исходному числу M. Почему так происходит — вопрос сложный и требует более глубокого погружения в математическую теорию, что не входит в задачи данной статьи.

Пример:

Выбираем p = 11, q = 3, получаем n = 33, φ = 20
Выбираем e = 7
Вычисляем d = 3 (так как 7 × 3 (mod 20) = 1)
Открытый ключ — [33, 7], закрытый ключ — [33, 3]

Возьмем для шифрования число M = 20, используем открытый ключ [33, 7]
Тогда C = 207 mod 33 = 26

C = 26 — это зашифрованное значение. Из него получатель шифровки при помощи своего закрытого ключа [33, 3] получит исходное M:

M = 26³ mod 33 = 20

В реальности, конечно, работа идет с большими числами, размерностью 1024 бит и выше. И вычисления сложнее, и подобрать ключ практически невозможно.

Надо отметить, что асимметричные алгоритмы работают медленнее, чем алгоритмы симметричного шифрования, так как требуют гораздо больше (и более сложных) вычислений.

Криптоанализ

Одновременно с криптографией появился и криптоанализ — наука о способах дешифровки зашифрованной информации, и вообще, о поиске уязвимостей в криптографической защите.

Например, самые простые атаки и способы взлома шифров замены заключаются в статистическом анализе шифротекста и сравнении результатов с закономерностями в языке, на котором написан исходный текст. Популярно об этом можно прочитать в уже упоминавшихся «Пляшущих человечках» (Артур Конан Дойль).

Используемые сейчас сложные шифры так легко не взламываются, криптографический инструментарий стал гораздо менее уязвим к атакам различного рода. Тем не менее, существуют различные методы криптоанализа, и иногда в тех или иных шифрах находят уязвимости.

Также, с развитием вычислительной техники и появлением распределенных вычислений, периодически становится актуальна защищенность методов шифрования от атак типа Brute Force, простым перебором значений. Именно поэтому важную роль играет длина ключа шифрования — чем длиннее ключ, тем больше вариантов надо перебрать.

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

Таким образом, каждый алгоритм имеет свой уровень стойкости к взлому, надёжности, безопасности.

Криптографические примитивы

Всё, о чем мы говорили выше, касается, в основном, шифрования.

В современной криптографии шифрование — это не только самоцель всего процесса, но и инструмент, который используется и для выполнения других операций (примитивов).

Каждый из этих примитивов также занимается превращением понятных данные в непонятные, но с разными целями и разными результатами. Обычно выделяется три таких операции-примитива.

Хеширование

Хеш-функция — это некая функция, которая позволяет из данных произвольного размера получить псевдо-уникальное значение определённой фиксированной длины. Это, можно сказать, тоже своего рода процедура шифрования (из понятных данные мы получаем непонятные), но она необратима, то есть из хеш-суммы обратно данные получить нельзя.

Хеширование

Бывают алгоритмы хэширования, так сказать, повседневные, знакомые всем:

  • MD5,
  • SHA256.

Также применяются более экзотические алгоритмы хэширования, например, в российских стандартах:

  • ГОСТ Р 34.11-94,
  • ГОСТ Р 34.11-2012 (этот алгоритм имеет собственное имя Стрибог).

Шифрование

Вторая криптографическая функция — это собственно шифрование. Обратная к ней — это расшифровка данных.

В этой операции уже задействовано такое понятие, как ключ — открытый и закрытый. Открытый ключ — это значение, которое свободно распространяется и открыто для всех.

Закрытый ключ — это секретное значение которое владелец держит в тайне.

Шифрование происходит с использованием открытого ключа, На выходе мы получаем шифротекст.

Шифрование

Расшифровка выполняется при помощи закрытого ключа.

Есть распространенное заблуждение, что все как раз наоборот, и для шифрования используется закрытый секретный ключ. Это так, для симметричных шифров. В них открытый и закрытый ключ совпадают, используется всего один ключ, который известен и получателю и отправителю и они, конечно, держат его в секрете.

При асимметричном шифровании схема работает приблизительно так:

  • Если мы хотим получать от наших корреспондентов зашифрованную информацию, мы генерируем ключевую пару и распространяем открытый ключ.
  • Этим открытым ключом для нас будут шифровать информацию. При этом расшифровать открытым ключом нельзя — это односторонняя операция.
  • Но расшифровать можем мы, при помощи своего закрытого ключа. В этом и есть секретность — закрытый ключ известен только владельцу.

Мы уже говорили ранее, что асимметричное шифрование — это медленная операция. Поэтому на практике часто шифрование, в особенности больших или потоковых данных происходит так:

  • Генерируется ключ для симметричного шифра — «сеансовый ключ».
  • Ключ зашифровывается открытым ключом асимметричного шифра.
  • Зашифрованный ключ передается второму участнику взаимодействия.
  • Второй участник расшифровывает симметричный ключ.
  • Теперь у каждого участника обмена есть ключ для симметричного шифра, переданный безопасным способом.
  • Дальнейший обмен происходит при помощи симметричного шифрования.

Как вариант, при передаче пакета данных в него сразу помещается:

  • Ключ симметричного шифра (сеансовый), зашифрованный асимметричным.
  • Данные, зашифрованные симметричным шифром, сеансовым ключом.

Шифрование

Результат шифрования, полученный при помощи платформы 1С, устроен как раз таким образом.

Эта схема позволяет выполнять «дорогостоящее» асимметричное шифрование на небольшом значении сеансового ключа. А большой объём передаваемых данных — зашифровывать быстрым симметричным шифром.

Электронная подпись

Электронная подпись создается с использованием закрытого ключа. Закрытый ключ известен только владельцу подписи, соответственно, только он может создать эту подпись. Для этого исходные данные и закрытый ключ передаются в функцию вычисления подписи, на выходе получаем некое новое значение, которое называется «подпись» или «значение подписи».

Это небольшое значение, например 512 бит. Иногда его называют «сырое» значение электронной подписи — то есть, не завернутое ни в какие обертки или форматы, просто результат вычисления математической криптографической функции.

Электронная подпись — это тоже своего рода шифрование. Из понятных входных данных мы получаем непонятное значение подписи.

Электронная подпись

Обратная задача состоит в том, чтобы убедиться, что исходные данные соответствуют значению подписи. Два этих значения передаются в криптографическую функцию, и при помощи открытого ключа проверяется, соответствуют ли эти данные — этой подписи.

Открытый ключ надо где-то заранее получить, часто он вкладывается в тот же пакет, что и остальные значения.

Отметим такой момент, что электронная подпись никоим образом не шифрует информацию. И не предназначена для скрытия или шифрования информации. Значение подписи вообще не содержит исходных данных — это некое дополнительное значение.

Чтобы передать получателю подписанные данные, мы помещаем в один пакет сами исходные данные и значение подписи. Исходные данные при этом не шифруются..

Если же требуется засекретить информацию, надо дополнительно использовать шифрование, и это будет уже более сложная операция.

Часто при установке электронной подписи к данным в один пакет добавляют также дополнительные сведения для большей защищенности. Например дату подписания, имя субъекта, который подписал и прочую информацию.

Все это объединяется в одну общую структуру информации, а затем уже устанавливается подпись на полный объем данных. Таким образом, например, если кто-то подложит исходные истинные данные, но подменит дату подписи, это можно будет установить.

Как и при шифровании, в случае большого объема данных установка подписи является достаточно затратной вычислительной операцией. Поэтому часто также применяется методика для уменьшения этих вычислительных затрат.

Подпись устанавливается не на исходные данные или [исходные данные + дополнительные сведения], а на хеш этого значения.

Хеш считается (относительно) быстро, а посчитать подпись от нескольких байт значения хеша гораздо менее затратно, чем от большого блока исходных данных, теоретически ничем не ограниченных.

Электронная подпись

Электронная подпись даёт нам несколько полезных возможностей:

  • Мы всегда можем проверить соответствие исходных данных значению подписи. И таким образом убедиться, что исходные данные верны и целостны.
  • Мы всегда можем проверить, что именно этот владелец подписи подписал исходные данные. То есть убедиться в том, что данные заверены именно нужным субъектом.
  • Также, так как только владелец подписи может её создать, наличие подписи удостоверяет, что именно он создал или заверил исходные данные. То есть владелец подписи не может отказаться от авторства.
  • Используя более сложные форматы подписи, мы можем также удостоверять различные свойства исходных данных, такие, как, например, дата подписания документа.

Виды электронной подписи в России

Существует, в соответствии с российским законодательством, три вида электронной подписи:

  • простая,
  • усиленная неквалифицированная,
  • усиленная квалифицированная.

Простая подпись — это, например, SMS-код. Криптография не используется.

Усиленная неквалифицированная (УНЭП) — создается при помощи криптографических средств. Такая подпись генерируется и действует внутри какой-то системы. Например, это может быть внутренний документооборот компании. Также, этот вид подписи выпускается для желающих физлиц в личном кабинете налогоплательщика. Теоретически, может использоваться любое программное обеспечение.

Усиленная квалифицированная (УКЭП) — тоже создается при помощи криптографических средств, но используемые средства и алгоритмы должны быть сертифицированы ФСБ.

Таким образом, в теории, в некоторых случаях можно использовать УНЭП. На практике, почти все системы взаимодействия и интеграции, в особенности государственные, требуют использования УКЭП. По сути, это сужает выбор из всего разнообразия алгоритмов до алгоритмов, определенных стандартами ГОСТ (сейчас — ГОСТ 2012), а программного обеспечения — до имеющих соответствующую регистрацию и сертификацию.

Криптопровайдеры

Как мы выяснили, для работы с криптографическими инструментами нужно иметь математический аппарат (алгоритмы), набор криптографических функций для преобразования данных. Некоторые из них мы уже перечислили выше. Алгоритмов существует множество, они различаются назначением, сложностью, стойкостью к различным атакам.

Криптография не стоит на месте. Какие-то алгоритмы считаются устаревшими или небезопасными и уже не используются.

Иногда появляются новые модификации алгоритмов. Например, алгоритмы, закрепленные ГОСТ, менялись несколько раз: ГОСТ Р34.10-94 сменился на версию ГОСТ 2001, а несколько лет назад происходил массовый переход систем на ГОСТ 2012. При этом менялись не просто названия, а существенные характеристики алгоритмов.

Для того, чтобы практически выполнять какое-то шифрование не требуется реализовывать заново алгоритм на каком-либо языке программирования. Для этого предназначены готовые программные продукты — СКЗИ (средства криптографической защиты информации), или криптопровайдеры.

Программ-криптопровайдеров существует немало, вот несколько из них:

  • Microsoft Base Cryptographic Provider,
  • Крипто Про,
  • Vipnet,
  • Сигнал-Ком,
  • Код безопасности.

Разные криптопровайдеры умеет исполнять разные наборы алгоритмов криптографии. Некоторые простые алгоритмы встроены в операционную систему, например, в системе Windows есть Microsoft Base Cryptographic Provider. Он используется для различных внутренних криптографических потребностей системы, таких как: шифрование и хеширование паролей, поддержка протоколов SSL и TLS.

Но те алгоритмы, которые нам обычно интересны, например, все тот же ГОСТ, в операционной системе отсутствует и для них нужно использовать дополнительные программы.

В нашей практике двумя самыми распространёнными криптопровайдерами являются КриптоПро и VipNet.

Криптопровайдеры — капризные программы, с ними иногда бывают трудности. Установленные на одной операционной системе криптопровайдеры часто конфликтуют между собой. Также есть случаи конфликтов с антивирусами. Отмечались случаи, когда возникали проблемы с подключением к сети на компьютере.

Также, в документации 1С (its.1c.ru/db/v83doc#bookmark:dev:TI000000835) прямо написано, что в Linux с 1С работает СКЗИ КриптоПро. По сути, мы ограничены одним программным продуктом. Другие программы не упоминается и, соответственно, не поддерживаются.

Инициализация криптографии в 1С

Внимание 
Прежде, чем приводить различные фрагменты кода, оговоримся.

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

Во-вторых, многие операции хорошо автоматизированы в Библиотеке Стандартных Подсистем, а соответственно и во многих типовых конфигурациях. Более того, в Библиотеке также есть готовые блоки структуры данных и интерфейсов для взаимодействия с пользователем. Все эти элементы можно использовать в своем решении, а функции просто вызвать в своем коде.

Но для того, чтобы понимать, как готовые функциональные блоки, представленные в библиотеке БСП работают, грамотно их встраивать, уметь их отладить и доработать, требуется понимать, какие есть элементарные операции и объекты для взаимодействия с криптографией в платформе 1С. Если требуется разработать что-то свое — тем более нужно иметь представление об этом.

Для использования криптографии в 1С у нас есть не так много инструментов — их всего два. Это менеджер криптографии и внешняя компонента XMLDSig.

Менеджер криптографии — это объект встроенного языка 1С.

Сам он не выполняет криптографических операций, а вызывает функции установленных в системе библиотек криптопровайдеров посредством интерфейса — CryptoAPI.

Внешняя компонента XMLDSig — второй инструмент, это внешняя компонента для платформы 1С, она включается в поставку БСП. В новой версии БСП появилась новая версия этой компоненты с несколько дополненными возможностями, она называется ExtraCryptoAPI.

Инициализация криптографии в 1С

Эта библиотека несколько расширяет возможности менеджера криптографии.

В БСП в модулях подсистемы электронно-цифровой подписи реализованы основные необходимые функции-обертки для работы с этой компонентой.

Для того, чтобы начать работу с объектом МенеджерКриптографии, его надо инициализировать. Здесь приведен пример кода.

Инициализация криптографии в 1С

В этом фрагменте можно заметить некоторые константы, которые здесь обозначены ИмяМодуля, ТипМодуля, АлгоритмПодписи и т. д.

Это достаточно важные значения. ИмяМодуля + ТипМодуля задают нам криптопровайдер, который мы хотим вызвать через API операционной системы Windows.

А вот ПутьМодуля — предназначен для инициализации криптографии в системе Linux, где доступ к криптопровайдеру происходит не через системный вызов специального API, а через загрузку библиотек, пути к которым и нужно прописать в этой переменной.

АлгоритмХеширования и АлгоритмПодписи — задают выбранные алгоритмы для последующего выполнения.

Значения всех этих констант не просто произвольны, а зафиксированы для каждого криптопровайдера. Заметим также, что один и тот же алгоритм может в разных СКЗИ иметь разные наименования.

Возможные значения можно посмотреть:

  • В документации к программному обеспечению (криптопровайдеру).
  • В интернете.
  • В исходных модулях БСП.
  • Некоторую информацию можно найти в реестре Windows.
  • В Linux можно воспользоваться утилитой cpconfig (утилита из состава продукта Крипто Про).

Инициализация криптографии в 1С

Инициализация криптографии в 1С

После инициализации у нас есть Менеджер криптографии — это объект, через который мы будем взаимодействовать с криптопровайдером и выполнять такие функции, как:

  • Вычисление/выполнение криптографических примитивов: шифрование, электронная подпись.
  • Взаимодействие с хранилищем сертификатов, установку сертификатов, получение информации о сертификатах, извлечение их из системы.

Также, для подписи и шифрования/расшифровки нам понадобится использовать ключи — открытый и закрытый.

Установка, хранение и связь ключей, сертификаты, контейнеры

Как уже сказано ранее, открытый ключ распространяется среди тех, с кем мы хотим взаимодействовать. Сам по себе открытый ключ — это большое число, чаще всего представленное в виде HEX-строки. Но распространяются ключи не как строка, а в виде сертификатов.

Сертификаты

Сертификат — это некий файл, который содержит, помимо самого открытого ключа, различные сведения об электронной подписи. Сертификат по сути — некий паспорт электронной подписи. Он, в свою очередь, тоже подписан электронной подписью — выпускающего удостоверяющего центра. Сертификат также подтверждает, что данный открытый ключ принадлежит именно нам, то есть конкретной организации или физическому лицу.

Файлы, содержащие сертификаты, часто имеют расширение CER или PFX. Если в проводнике Windows в такой файл щелкнуть, он откроется и будет выглядеть, как на картинке.

Сертификаты

В этом окне можно просмотреть разные свойства электронной подписи. Здесь же можно заметить кнопку «Установить сертификат». Она позволяет поместить сертификат в специальное хранилище сертификатов и он будет «известен» системе.

Конечно, сертификат может просто располагаться в файле и при различных интерактивных и программных манипуляциях мы можем его выбирать или указывать к нему путь, как к физическому файлу.

Но в операционной системе предусмотрено централизованное хранилище сертификатов. Интерактивно, с помощью консольных утилит или программно, мы можем установить сертификат в хранилище.

Сертификаты

Для Windows при этом мы выбираем куда поместить сертификат: в хранилище компьютера, то есть доступно для многих пользователей, или в хранилище конкретного текущего пользователя.

Сертификаты делятся на категории: Личное, Корневые сертификаты удостоверяющих центров и несколько других категорий.

Система и криптопровайдер строят из этих сертификатов цепочки (когда сертификат пользователя, например, подтверждается сертификатом удостоверяющего центра), проверяет их целостность, сроки действия и т. д.

Закрытый ключ

Закрытый ключ

Закрытые ключи хранятся в «контейнерах». Контейнер может располагаться: в файловой системе (на флешке, на дискете), на смарт-карте, на аппаратном носителе-токене, в реестре Windows и даже в облаке.

Некоторые аппаратные носители не просто хранят в себе закрытые ключи, но являются более сложными устройствами и могут сами выполнять какие-то криптографические операции.

Контейнер закрытого ключа иногда можно перемещать между разными хранилищами. Например, ключ с аппаратного носителя можно скопировать в реестр Windows, чтобы каждый раз не требовалось вставлять носитель в USB-порт. Это, конечно удобно, но есть отдельный вопрос относительно безопасности такого решения.

Варианты контейнеров, их расположение, возможность перемещения, экспорта, импорта закрытых ключей определяется уже конкретным криптопровайдером. Дополнительно, контейнер закрытого ключа обычно защищается паролем.

Связь ключей и криптопровайдера

Если мы хотим воспользоваться электронной подписью и подписывать документы, нам необходимо в системе иметь три элемента: два ключа и криптопровайдер, а также установить между ними связь.

Необходимо, чтобы система знала: данный открытый ключ связан с данным контейнером закрытого ключа и с данным криптопровайдером.

Связь ключей и криптопровайдера

При этом, если для установки сертификата воспользоваться кнопкой «Установить сертификат», то такая связь не устанавливается и электронная подпись работает не будет. Для корректной установки сертификата и установления связи между сертификатом открытого ключа и контейнером закрытого ключа обычно выполняется их одновременная установка в систему, при помощи функции криптопровайдера.

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

Связь ключей и криптопровайдера

Связь ключей и криптопровайдера

Некорректная установка подписи (ключей) в систему — одна из частых ошибок при настройке криптографии, при этом могут возникать самые разнообразные ошибки, вот только некоторые из них:

Связь ключей и криптопровайдера

Сертификаты в коде 1С

Для того, чтобы воспользоваться закрытым ключом и что-то подписать или расшифровать данные непосредственно контейнер закрытого ключа не указывают. Используется сертификат открытого ключа, связанный с контейнером.

Для того, чтобы как-то взаимодействовать с открытым ключом, тоже используется сертификат открытого ключа.

Таким образом, для того чтобы выполнить какую-то криптографическую операцию связанную с ключами, нам необходимо указать сертификат.

Сертификатов в системе может быть несколько, так что, для выбора конкретного сертификата, нужно его как-то идентифицировать. У сертификата есть множество параметров, например: издатель, субъект (то есть, владелец), срок действия. Но все эти параметры не подходят в качестве идентификатора. Обычно для идентификации сертификата используется его поле «Отпечаток». Вообще, отпечаток — это просто хеш-сумма файла сертификата, но оно часто используется как его идентификатор.

В коде 1С чаще всего можно увидеть именно такой вариант.

Сертификаты в коде 1С

Для получения сертификата нужно воспользоваться менеджером криптографии, указав при этом из какого хранилища (общего для компьютера или персонального) мы хотим его получить.

Сертификаты в коде 1С

Так как закрытый ключ защищен паролем, для выполнения криптографических операций его тоже нужно указать.

Внимание В приведенном примере пароль указан в явном виде исключительно в демонстрационных целях.

Также, с помощью менеджера криптографии можно выполнять и другие операции с сертификатами — перебирать, сохранять в файл, удалять из хранилища и т. д.

Сертификаты в коде 1С

Стандарты, форматы

Когда специалист погружается в мир криптографии, он сразу сталкивается с огромным количеством различных аббревиатур и обозначений, помимо названий алгоритмов шифрования:

  • ASN.1,
  • CMS,
  • XMLDSig,
  • CAdES, JAdES, PAdES, XadES,
  • PKCS#7, PKCS#10,
  • CER/DER/BER,
  • X.509.

В первый момент здесь легко потеряться. Попробуем, просто для ясности, разобрать что к чему относится.

PKCS#NN — это стандарты (Public Key Cryptography Standards), разработанные компанией RSA Security. Стандарты описывают самые разнообразные вещи, от алгоритмов шифрования до протоколов защищённого обмена и механизмов хранения и передачи криптографической информации. В частности, PKCS#7 (Cryptographic Message Syntax Standard) — стандарт хранения криптографических данных (сертификатов, подписей, шифрованных данных).

CMS, XMLDsig, CAdES, JAdES, PAdES, XAdES — это различные форматы хранения подписей и другой криптографической информации, например, сертификатов. Они описывают достаточно высокоуровневую структуру как и какие данные должны быть представлены внутри файла или потоковых данных с подписью или другой информацией.

ASN.1 — бинарный, низкоуровневый формат для хранения криптографических данных. Описывает побайтовую структуру файла.

CER/DER/BER — это форматы хранения сертификатов ключей электронной подписи и шифрования. Они приблизительно одинаковы, отличаются незначительными особенностями вроде способа кодирования внутренних значений и ограничениями на содержимое.

X.509 — обычно понимается как стандарт структуры сертификата.

Некоторые форматы подписи можно создавать из встроенного языка 1С.

Формат CMS/CAdES

Одним из самых распространённых форматов является формат CMS. CMS — базовый формат. На его основе составлена целая гроздь форматов CAdES.

  • CadES-BES,
  • CadES-T,
  • CadES-C,
  • CadES-X,
  • CAdES-X Long (CadES-XL),
  • CadES-A,
  • CAdES-A v2,
  • CadES-LT,
  • CAdES-A v3.

CMS — это определенная структура, которая содержит внутри значение электронной подписи и множество дополнительных атрибутов этой электронной подписи. Разные форматы CAdES — это разные варианты CMS, отличающиеся набором этих атрибутов.

Формат CMS/CAdES

Файлы, имеющие логическую структуру CMS физически сохраняются в бинарном формате на базе формата ASN.1. Вот пример такого файла в HEX-редакторе и структура, которую он содержит:

Формат CMS/CAdES

Здесь расположено множество атрибутов подписи, такие как:

  • время подписания;
  • сертификат, которым подписано;
  • хеш данных;
  • алгоритмы, использованные при подписи;
  • и многое другое.

Сама же подпись занимает совсем небольшую часть файла:

Формат CMS/CAdES

CMS в коде 1С

Для создания подписи в программном коде 1С, можно воспользоваться объектом МенеджерКриптографии. Сначала инициализируем криптопровайдер, находим сертификат (фрагменты кода приведены ранее). Затем читаем данные из файла, который хотим подписать и передаем их в метод МенеджерКриптографии Подписать(). Кроме данных для подписи передаем сертификат подписи, которым хотим воспользоваться. Если все параметры корректны, на выходе мы получим подпись и запишем ее в файл.

CMS в коде 1С

CMS в коде 1С

В последних версиях платформы (8.3.21…) появилась возможность указать, какой из форматов CAdES-* требуется получить. То есть можно получить подпись именно того формата, который требуется по условиям задачи. Например, можно создать подпись CAdES-T. Для этого, заметим, требуется указать адрес сервера для получения метки времени, которая включается в состав подписи.

CMS в коде 1С

Проверка подписи выполняется также при помощи менеджера криптографии. В случае неверной подписи генерируется исключение.

CMS в коде 1С

Подпись PDF

Платформа 1С позволяет подписывать файлы PDF электронной подписью.

Это внедренная подпись, она содержится внутри самого подписываемого документа, а не в отдельном файле, как в предыдущем случае.

Для создания PDF в платформе есть объект под названием ЗаписьPDF. В 16-ой версии платформы в нем появилась функция ЗаписатьПодпись(), которая позволяет в файл поместить электронную подпись, параметры которой заготовлены заранее.

Подпись PDF

Приведенный код вызовет криптопровайдер, вычислит подпись документа PDF, используя указанный сертификат, и поместит ее внутрь этого же документа в соответствии с форматом PDF. Дополнительно можно разместить картинку-штамп подписи.

Подпись PDF

Формат XMLDSig/XAdES

XMLDSig и XadES — это тоже целое семейство форматов. Логика здесь приблизительно такая же, как с форматами CMS и CAdES — разные форматы XAdES содержат разное количество атрибутов, а базовым форматом является XMLDSig.

Для разработчика 1С это более сложный формат, чем ранее рассмотренные. Он доставляет гораздо больше неприятностей, потому что в отличие от ранее описанных форматов его нельзя просто так получить в одну-две строчки кода. Получение этой подписи — целый процесс.

Формат XMLDSig/XAdES

Прежде всего, XMLDSig — это стандарт для подписи XML документов, например, сообщений веб-сервисов, передаваемых по протоколу SOAP. Подпись может быть внедренной или отделенной, то есть располагаться внутри, рядом или отдельно от подписываемых данных.

Формат XMLDSig/XAdES

Чаще всего это внедренная подпись, то есть содержится в том же XML документе, что и подписываемые данные.

Сведения об электронной подписи могут располагаться в разных частях XML документа. В случае, если это SOAP сообщение, часто для этого используют блок Header.

Формат XMLDSig/XAdES

Но также они могут располагаться и в блоке Body.

Формат XMLDSig/XAdES

В одном XML документе может быть несколько электронных подписей, соответственно, несколько блоков XMLDsig.

Конкретные уточнения этих требований обычно указываются в документации к конкретной системе обмена информацией.

Стандарт описывает блок хранения электронной подписи и поля-теги, которые в нём должны быть расположены.

Формат XMLDSig/XAdES

Основной, корневой блок должен называться Signature и принадлежать пространству имен «http://www.w3.org/2000/09/xmldsig#».

Вложенные блоки:

  • SignatureValue — содержит значение подписи.
  • KeyInfo — содержит значение сертификата подписи.
  • SignedInfo — сведения о подписанных данных:
    • CanonicalizationMethod — метод каноникализации (о том, что это такое ниже). Обычно содержит атрибут Algorithm с одним и тем же значением, как в примерах.
    • SignatureMethod — указывает на алгоритм подписи (например, ГОСТ 2012).
    • Reference — одна или несколько ссылок на исходные подписываемые данные.

Каждый элемент Reference (часто он один) — это ссылка на подписанный фрагмент XML. Для указания этой ссылки у исходных данных ставится атрибут Id, а в тег Reference указывается атрибут URI. В примере мы хотим подписать часть документа — блок Row. Для этого ему дали атрибут Id = «ID1» и сослались на этот Id в блоке Reference.

Формат XMLDSig/XAdES

Также в блоке Reference присутствует три подраздела:

  1. DigestMethod — указывает алгоритм хеширования.
  2. DigestValue — указывает значение хеша подписанных данных (в примере — блока Row).
  3. Transforms — содержит несколько вложенных элементов Transform, описывающих трансформации (о них тоже ниже).

Формат XMLDSig/XAdES

Таким образом, чтобы подписать XML документ подписью XMLDSig надо дополнить документ сформированным блоком Signature и заполнить в нем несколько тегов-полей.

Формат XMLDSig/XAdES

Обычно эти три отмеченных элемента:

  1. В первое поле помещается дайджест, он же хеш подписанных данных.
  2. Второе поле содержит значение подписи.
  3. Третье поле — сертификат.

При этом поле подписи должно содержать значение «сырой» подписи, то есть свободное от разных форматов и оберток. Только те байты, которые возвращает криптографический алгоритм. Поэтому здесь нельзя использовать, например, подпись в формате CMS, которую мы уже умеем легко получать.

И здесь возникает проблема. Дело в том, что платформа 1С в лице менеджера криптографии не умеет вычислять «сырую» подпись. А также не умеет вычислять нужные хеш-функции.

Действительно, в платформе есть объект ХешированиеДанных, но с его помощью можно рассчитать хеш-суммы только самых распространенных алгоритмов:

Формат XMLDSig/XAdES

А вот специфические, например, необходимый для подписи по ГОСТ хеш ГОСТ Р 34.11-2012 — рассчитать нельзя. С другой стороны, такой хеш может рассчитать криптопровайдер — это одна из его функций.

И здесь нам приходит на помощь внешняя компонента XMLDSig (ExtraCryptoAPI). В ней как раз есть недостающие функции:

  • Вычисление «сырой» подписи.
  • Хеширование при помощи криптопровайдера.
  • И важный элемент — Каноникализация.

Что такое Каноникализация XML?

XML документы имеют следующую особенность: одни и те же, логически одинаковые, данные в XML можно представить разными способами, по-разному написав физическую структуру. И это проявляется следующим образом.

Предположим, отправитель решил составить XML документ, подписать электронной подписью и куда-то отправить. У него получился документ номер 1:

Что такое Каноникализация XML

При передаче получателю этот документ пройдет через какие-то парсеры, библиотеки, возможно помещен внутрь SOAP пакета.

Может так получиться, что у получателя он уже будет выглядеть как фрагмент под номером 2. Возможно, какой-нибудь парсер решит документ немного облагородить.

Что такое Каноникализация XML

Это не ошибка, ведь логические данные одинаковы, содержат одинаковую информацию, просто отформатированы по-разному.

Тем не менее, при использовании криптографии это является проблемой.

Побайтовое представление этих документов различно. А, соответственно, будет разное значение хеша и несоответствие электронной подписи.

Для решения этой проблемы существует механизм «каноникализация» (Canonicalization, канонизация, C14N). Это определённый набор правил, описанный стандартом W3C, который применяется к документу с целью привести его в единый канонический вид.

Что такое Каноникализация XML

Предполагается, что если у нас есть два документа XML, одинаковые логически, но при этом отличающиеся формой представления, каноническая форма у них будет одинакова и совпадать до байта.

Как изменится процесс передачи данных при использовании каноникализации:

Что такое Каноникализация XML

Отправитель, подготовив документ (1), сначала проводит его через процедуру каноникализации, получает каноническую форму (3) и над ней уже выполняет криптографические процедуры, например, получает хеш.

Далее отправляет по каналам связи каноническую или исходную форму документа.

Получатель, в свою очередь, загрузив документ (который, почему-то в процессе передачи преобразовался к форме (2)), также вычислит каноническую форму и опять получит такой же документ (3). Теперь над этим документом можно выполнять вычисления. Результат криптографических процедур, например, расчет хеша, теперь сойдется с результатом у отправителя.

Как раз использованный алгоритм каноникализации мы должны указать в разделе CanonicalizationMethod блока Signature, а также в разделе SignedInfo/Reference/Transforms/Transform.

Обычно и там и там указывается Algorithm=»http://www.w3.org/2001/10/xml-exc-c14n#».

Дополнительные трансформации XML

Кроме того, стандарт XMLDsig позволяет выполнять не только каноникализацию типовым алгоритмом, но и вводить свои собственные так называемые трансформации.

На практике, мы сталкивались с такой особенностью в системе СМЭВ.

СМЭВ — система межведомственного электронного взаимодействия, государственная система для обмена данными между учреждениями.

В этой системе документ дополнительно к каноникализации XML надо преобразовывать специальным алгоритмом «трансформации СМЭВ». Таким образом, исходный документ XML проходит еще одну стадию прежде, чем получит каноническую форму (по версии системы СМЭВ):

Дополнительные трансформации XML

В последнее время взаимодействие с этой системой все более востребовано и в компоненте XMLDSig, а также в БСП появилась процедура для этого преобразования.

Дополнительные трансформации XML

В случае использования дополнительной трансформации мы также должны указать это в блоке SignedInfo/Reference/Transforms/
Transform, еще одной строкой:

Дополнительные трансформации XML

Создание подписи XMLDsig

Рассмотрим пошагово, как создать подпись XMLDSig. Связанный с преобразованиями XML код оставим за кадром.

Предположим, у нас есть исходный XML документ, например SOAP-пакет и мы хотим подписать его часть — блок «Row».

Создание подписи XMLDsig

Начинаем с того, что подготавливаем структуру определённого формата для хранения подписей.

Создание подписи XMLDsig

Здесь есть три фрагмента, которые надо заполнить. Что в них размещается мы уже рассмотрели ранее. Все значения записываются в виде Base64 строки.

Из исходного текста нужно выделить этот блок Row отдельно и получить его каноническую форму. Для этого мы пользуемся методом компоненты XMLDSig, который называется C14N(). Для указания, какой конкретно блок необходимо выделить и преобразовать каноническому виду, используем выражение XPath.

Создание подписи XMLDsig

Создание подписи XMLDsig

Здесь можно обратить внимание на табуляции — все табуляции сохранились. То есть несмотря на то, что мы блок выдернули из контекста XML, его каноническая форма по-прежнему содержит все пробельные символы, отступы между тегами. Теперь уже над этой канонической формой будем выполнять криптографические операции — вычислять хеш.

Вычисляем хэш этой строки. Это тоже делается при помощи компоненты. Каким конкретно алгоритмом мы рассчитываем хеш указывается при помощи так называемого OID — это специальный идентификатор, присвоенный тому или иному алгоритму. Например, здесь это ГОСТ Р 2012.

Создание подписи XMLDsig

Получим вот такое значение:

Создание подписи XMLDsig

Ранее полученный из хранилища сертификат преобразуем в Base64.

Создание подписи XMLDsig

И располагаем эти полученные значения в ранее подготовленной структуре в предназначенные для них места (1, 3).

Создание подписи XMLDsig

В DIgestValue (1) помещаем хеш, в X509Certificate (3) — сертификат.

Мы получили уже частично заполненную конструкцию.

Создание подписи XMLDsig

Следующий шаг, собственно, вычисление подписи. Но подписывать мы будем не блок Row (с которого все начиналось), а блок SignedInfo, в которой мы ранее поместили хеш (так устроен этот формат подписи).

В тексте этот блок выделен цветом. Здесь также надо применить процедуру каноникализации.

Создание подписи XMLDsig

Электронную подпись необходимо получить именно «сырую» — непосредственно то значение, которое дает на выходе криптографическая функция. Для получения подписи используем функцию Sign() компоненты XMLDSig. Так же как мы сделали это с блоком Row, вызываем функцию каноникализации C14N(), а затем вызываем функцию Sign() и получаем «сырое» значение подписи в формате base64.

Создание подписи XMLDsig

Полученное значение размещаем в соответствующее место структуры, помеченное здесь цифрой (2) — «SignatureValue»

Создание подписи XMLDsig

Теперь документ подписан. Его можно отправлять другому участнику взаимодействия или обмена.

Продолжение следует

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

В следующей части мы разберем практические вопросы получения ключей, законодательство и настройку в программах 1С.

Автор статьи

I’m trying to sign a xml document following XMLDSIG specification with an enveloped signature, sha1 digest and rss-sha1 signature, and the server keeps returning a «297 — Rejection: Signature does not match calculated [result]»
(«297 — Rejeicao: Assinatura difere do calculado» in Brazilian Portuguese original)

My client application needs to be Mac OS X native (so Objective-C and Swift). I’m adhering to Apple’s CryptoCompatibility guidelines and using Security.framework’s SecSignTransform and CommonCrypto’s CC_SHA1.

Here is the XML I’m trying to XMLDSIG (did not PrettyPrint and omitted terms to save space):

<NFe xmlns="http://www.portalfiscal.inf.br/nfe"><infNFe Id="NFe351503...1455341071" versao="2.00"><ide><cUF>35</cUF><cNF>45534107</cNF><natOp>VENDA</natOp><indPag>1</indPag><mod>55</mod>
...
</infNFe><Signature xmlns="http://www.w3.org/2000/09/xmldsig#"><SignedInfo><CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"></CanonicalizationMethod><SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"></SignatureMethod><Reference URI="#NFe351503...1455341071"><Transforms><Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"></Transform><Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"></Transform></Transforms><DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></DigestMethod><DigestValue>H4l0eMA6H4ndKzY3ftwlsKpeX58=</DigestValue></Reference></SignedInfo><SignatureValue>QYMVPWvZOeF4XgorObl33Tm9DiZEW4N7zuuAbt9Jjop79V41SNAIO5qIXe06cLiJACghi1X+p3pROE3P/E/lhPhwGmA3G26Jm5hZqsGhURS1osHDNKDWARBpi+musgi5naHm4tKqlKKIKqARljyXyYRRVaoxOSrC3vmxPx2ClwwTrlgnqtDTODQU0yNN4OUXTxWAMYPm8rc2rO6OUohTK+eXE3mN5vgCB6GLMWj0Cp2k6N21264WNv/P+L45kHUllFnV+ByMshXFYzySvthujlq/4ClSG+1xOFYMATn1F6qvklMDXy7bS+Dqcp635ZFxfD97gTDriFUYH0+nEe95zw==</SignatureValue><KeyInfo><X509Data><X509Certificate>...</X509Certificate></X509Data></KeyInfo></Signature></NFe>

Unfortunately, since both .Net and Java offer very high level support for XMLDSIG, detailed information about which parts of the XML to get, what to retain and what to remove, are scant on the internet. Apart from W3.org’s own specs, which is pretty dry, the only in-depth explanation I found was: http://www.di-mgt.com.au/xmldsig2.html

I’m not sure if the mismatch is in the sha1 digest or the rsa-sha1 signature, the return code is unclear. Also, I don’t know if I’m using the wrong input, or if Mac OS X libraries I’m using are not compatible with the server (which is .Net based).

Here is the code for the digest. Please notice it uses a same-document URI reference:

// Imports XML data from XML file
var xmlStr: String? = File.open(documentPath)

// gets Id (formated "NFe" + 44x[0-9]) to create SignedInfo reference URI
let myId = getNFeId(xmlStr!)

// creates a XML Document using String xmlStr and canonicalize "c14n"
var xmlDocument = XMLSupportClass.createXMLDocument(xmlStr!)

let canonicalXmlStr = xmlDocument.canonicalXMLStringPreservingComments(false)
var stringToDigest = ""

// retrieves element referenced by URI (#myId) to create digest
if (xmlDocument.rootElement() != nil) {
    let xmlRoot: NSXMLElement = xmlDocument.rootElement()!
//    let myURI = "#" + myId
//    let nodesToTest: [NSXMLElement] = xmlRoot.elementsForLocalName("NFe", URI: myURI) as [NSXMLElement]
//    let nodesToTest2: [NSXMLElement] = xmlRoot.elementsForName("infNFe") as [NSXMLElement]
    let myXPath: String = "//*[@Id='" + myId + "']"
    let nodesToDigest = xmlRoot.nodesForXPath(myXPath, error: &xpathError) as [NSXMLElement]
    if nodesToDigest.count > 0 {
        stringToDigest = nodesToDigest[0].canonicalXMLStringPreservingComments(false)
    } else { println(xpathError) }
} else {
    println("I'm root-less!!")
}

// creates the digest using CryptoCompatibility
digestData = stringToDigest.sha1()
let digestDataAsString: String =  digestData.base64EncodedStringWithOptions(NSDataBase64EncodingOptions.EncodingEndLineWithLineFeed)

Extra methods used in code:

func getNFeId(xml: String) -> String {
    // mas como extrair o atributo Id do elemento <infNFe>
    var myError: NSError?
    let root = XMLSupportClass.createXMLDocument(xml).rootElement()! as NSXMLElement
    let infNodes = root.elementsForName("infNFe") as [NSXMLElement]
    if infNodes.count > 0 {
        let idNode = infNodes[0].attributeForName("Id")! as NSXMLNode
        let myId = idNode.objectValue as String
        println(myId)
        return myId
    } else {
        println("error extracting NFeId")
        return "error extracting NFeId"
    }    
}

// SHA-1 Digest from CryptoCompatibility returning a Hex String
extension String {
    func sha1() -> String {
        let data = self.dataUsingEncoding(NSUTF8StringEncoding)!
        var digest = [UInt8](count:Int(CC_SHA1_DIGEST_LENGTH), repeatedValue: 0)
        CC_SHA1(data.bytes, CC_LONG(data.length), &digest)
        let output = NSMutableString(capacity: Int(CC_SHA1_DIGEST_LENGTH))
        for byte in digest {
            output.appendFormat("%02x", byte)
        }
        return output
    }
}

After calculating the digest, it is inserted in a pre-formatted Signature XML String, to create a XML Document, and then the SignedInfo node is extracted and used to generate the SignatureValue:

// pre-formatted XML String for Signature node, leaving SignatureValue empty for filling in later
let xmlAssinatura = "<Signature xmlns="http://www.w3.org/2000/09/xmldsig#"><SignedInfo><CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/><SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/><Reference URI="#(myId)"><Transforms><Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/><Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/></Transforms><DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/><DigestValue>(digestDataAsString)</DigestValue></Reference></SignedInfo><SignatureValue></SignatureValue><KeyInfo><X509Data><X509Certificate>(certDataAsString)</X509Certificate></X509Data></KeyInfo></Signature>"

// tranforms xmlAssinatura String in NSXMLDocument
var xmlAssinaturaDocument = XMLSupportClass.createXMLDocument(xmlAssinatura)
let signatureNode = xmlAssinaturaDocument.rootElement()!

// and retrieves SignedInfo node, converts to NSData for signing
let xmlSignedInfoElement = (signatureNode.elementsForName("SignedInfo") as [NSXMLElement])[0]


    **// ====> the line below was the problem!!!**
/*let signedInfoData = XMLSupportClass.createXMLDocument(xmlSignedInfoElement.canonicalXMLStringPreservingComments(false)).XMLData */

    **// ====> and this is the fix:**
let signedInfoData = (XMLSupportClass.createXMLDocument(xmlSignedInfoElement.canonicalXMLStringPreservingComments(false), withTidyXML:true).rootElement()!.XMLString).dataUsingEncoding(NSUTF8StringEncoding)!



// creates SecTransform object
    signer = SecSignTransformCreate(priKey, &error).takeRetainedValue()
    if error != nil { print("signer transform creation error: ") ; println(error) }


// signer to use SHA1 digest method and use signedInfoData as input
SecTransformSetAttribute(signer, kSecDigestTypeAttribute, kSecDigestSHA1, &error)
if error != nil { print("verifier digest attribute setting error: ") ; println(error) }
SecTransformSetAttribute(signer, kSecTransformInputAttributeName, signedInfoData, &error)
if error != nil { print("signer attribute setting error: ") ; println(error) }

// executes the transform
signedData = (SecTransformExecute(signer, &error) as NSData)
let signedDataAsString = signedData.base64EncodedStringWithOptions(NSDataBase64EncodingOptions.EncodingEndLineWithLineFeed)
if error != nil { print("signer execute error: ") ; println(error) }

// inserts generated signedDataAsString in <SignatureValue> node
let signatureValueElements = signatureNode.elementsForName("SignatureValue") as [NSXMLElement]
if signatureValueElements.count > 0 { signatureValueElements[0].setStringValue(signedDataAsString, resolvingEntities: false) } else { println(signatureValueElements) }
signatureNode.detach()
xmlAssinaturaDocument = nil

// then replaces <Signature> placeholder node in xmlDocument
if (xmlDocument.rootElement() != nil) {
    let xmlRoot: NSXMLElement = xmlDocument.rootElement()!
    let signatureNodePlaceholder: NSXMLElement = (xmlRoot.elementsForName("Signature") as [NSXMLElement])[0]
    let signatureNodeIndex = signatureNodePlaceholder.index
    xmlRoot.replaceChildAtIndex(signatureNodeIndex, withNode: signatureNode)

    // and creates xmlDocument canonicalized String
    xmlStr = xmlRoot.canonicalXMLStringPreservingComments(false)
}

As far as I can see, everything is correct and compliant with W3.org’s specifications for XMLDSIG. Still, the server always rejects the generated XML.

I’m at wits end. Any help and wisdom will be highly appreciated!!

I’m trying to sign a xml document following XMLDSIG specification with an enveloped signature, sha1 digest and rss-sha1 signature, and the server keeps returning a «297 — Rejection: Signature does not match calculated [result]»
(«297 — Rejeicao: Assinatura difere do calculado» in Brazilian Portuguese original)

My client application needs to be Mac OS X native (so Objective-C and Swift). I’m adhering to Apple’s CryptoCompatibility guidelines and using Security.framework’s SecSignTransform and CommonCrypto’s CC_SHA1.

Here is the XML I’m trying to XMLDSIG (did not PrettyPrint and omitted terms to save space):

<NFe xmlns="http://www.portalfiscal.inf.br/nfe"><infNFe Id="NFe351503...1455341071" versao="2.00"><ide><cUF>35</cUF><cNF>45534107</cNF><natOp>VENDA</natOp><indPag>1</indPag><mod>55</mod>
...
</infNFe><Signature xmlns="http://www.w3.org/2000/09/xmldsig#"><SignedInfo><CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"></CanonicalizationMethod><SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"></SignatureMethod><Reference URI="#NFe351503...1455341071"><Transforms><Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"></Transform><Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"></Transform></Transforms><DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></DigestMethod><DigestValue>H4l0eMA6H4ndKzY3ftwlsKpeX58=</DigestValue></Reference></SignedInfo><SignatureValue>QYMVPWvZOeF4XgorObl33Tm9DiZEW4N7zuuAbt9Jjop79V41SNAIO5qIXe06cLiJACghi1X+p3pROE3P/E/lhPhwGmA3G26Jm5hZqsGhURS1osHDNKDWARBpi+musgi5naHm4tKqlKKIKqARljyXyYRRVaoxOSrC3vmxPx2ClwwTrlgnqtDTODQU0yNN4OUXTxWAMYPm8rc2rO6OUohTK+eXE3mN5vgCB6GLMWj0Cp2k6N21264WNv/P+L45kHUllFnV+ByMshXFYzySvthujlq/4ClSG+1xOFYMATn1F6qvklMDXy7bS+Dqcp635ZFxfD97gTDriFUYH0+nEe95zw==</SignatureValue><KeyInfo><X509Data><X509Certificate>...</X509Certificate></X509Data></KeyInfo></Signature></NFe>

Unfortunately, since both .Net and Java offer very high level support for XMLDSIG, detailed information about which parts of the XML to get, what to retain and what to remove, are scant on the internet. Apart from W3.org’s own specs, which is pretty dry, the only in-depth explanation I found was: http://www.di-mgt.com.au/xmldsig2.html

I’m not sure if the mismatch is in the sha1 digest or the rsa-sha1 signature, the return code is unclear. Also, I don’t know if I’m using the wrong input, or if Mac OS X libraries I’m using are not compatible with the server (which is .Net based).

Here is the code for the digest. Please notice it uses a same-document URI reference:

// Imports XML data from XML file
var xmlStr: String? = File.open(documentPath)

// gets Id (formated "NFe" + 44x[0-9]) to create SignedInfo reference URI
let myId = getNFeId(xmlStr!)

// creates a XML Document using String xmlStr and canonicalize "c14n"
var xmlDocument = XMLSupportClass.createXMLDocument(xmlStr!)

let canonicalXmlStr = xmlDocument.canonicalXMLStringPreservingComments(false)
var stringToDigest = ""

// retrieves element referenced by URI (#myId) to create digest
if (xmlDocument.rootElement() != nil) {
    let xmlRoot: NSXMLElement = xmlDocument.rootElement()!
//    let myURI = "#" + myId
//    let nodesToTest: [NSXMLElement] = xmlRoot.elementsForLocalName("NFe", URI: myURI) as [NSXMLElement]
//    let nodesToTest2: [NSXMLElement] = xmlRoot.elementsForName("infNFe") as [NSXMLElement]
    let myXPath: String = "//*[@Id='" + myId + "']"
    let nodesToDigest = xmlRoot.nodesForXPath(myXPath, error: &xpathError) as [NSXMLElement]
    if nodesToDigest.count > 0 {
        stringToDigest = nodesToDigest[0].canonicalXMLStringPreservingComments(false)
    } else { println(xpathError) }
} else {
    println("I'm root-less!!")
}

// creates the digest using CryptoCompatibility
digestData = stringToDigest.sha1()
let digestDataAsString: String =  digestData.base64EncodedStringWithOptions(NSDataBase64EncodingOptions.EncodingEndLineWithLineFeed)

Extra methods used in code:

func getNFeId(xml: String) -> String {
    // mas como extrair o atributo Id do elemento <infNFe>
    var myError: NSError?
    let root = XMLSupportClass.createXMLDocument(xml).rootElement()! as NSXMLElement
    let infNodes = root.elementsForName("infNFe") as [NSXMLElement]
    if infNodes.count > 0 {
        let idNode = infNodes[0].attributeForName("Id")! as NSXMLNode
        let myId = idNode.objectValue as String
        println(myId)
        return myId
    } else {
        println("error extracting NFeId")
        return "error extracting NFeId"
    }    
}

// SHA-1 Digest from CryptoCompatibility returning a Hex String
extension String {
    func sha1() -> String {
        let data = self.dataUsingEncoding(NSUTF8StringEncoding)!
        var digest = [UInt8](count:Int(CC_SHA1_DIGEST_LENGTH), repeatedValue: 0)
        CC_SHA1(data.bytes, CC_LONG(data.length), &digest)
        let output = NSMutableString(capacity: Int(CC_SHA1_DIGEST_LENGTH))
        for byte in digest {
            output.appendFormat("%02x", byte)
        }
        return output
    }
}

After calculating the digest, it is inserted in a pre-formatted Signature XML String, to create a XML Document, and then the SignedInfo node is extracted and used to generate the SignatureValue:

// pre-formatted XML String for Signature node, leaving SignatureValue empty for filling in later
let xmlAssinatura = "<Signature xmlns="http://www.w3.org/2000/09/xmldsig#"><SignedInfo><CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/><SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/><Reference URI="#(myId)"><Transforms><Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/><Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/></Transforms><DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/><DigestValue>(digestDataAsString)</DigestValue></Reference></SignedInfo><SignatureValue></SignatureValue><KeyInfo><X509Data><X509Certificate>(certDataAsString)</X509Certificate></X509Data></KeyInfo></Signature>"

// tranforms xmlAssinatura String in NSXMLDocument
var xmlAssinaturaDocument = XMLSupportClass.createXMLDocument(xmlAssinatura)
let signatureNode = xmlAssinaturaDocument.rootElement()!

// and retrieves SignedInfo node, converts to NSData for signing
let xmlSignedInfoElement = (signatureNode.elementsForName("SignedInfo") as [NSXMLElement])[0]


    **// ====> the line below was the problem!!!**
/*let signedInfoData = XMLSupportClass.createXMLDocument(xmlSignedInfoElement.canonicalXMLStringPreservingComments(false)).XMLData */

    **// ====> and this is the fix:**
let signedInfoData = (XMLSupportClass.createXMLDocument(xmlSignedInfoElement.canonicalXMLStringPreservingComments(false), withTidyXML:true).rootElement()!.XMLString).dataUsingEncoding(NSUTF8StringEncoding)!



// creates SecTransform object
    signer = SecSignTransformCreate(priKey, &error).takeRetainedValue()
    if error != nil { print("signer transform creation error: ") ; println(error) }


// signer to use SHA1 digest method and use signedInfoData as input
SecTransformSetAttribute(signer, kSecDigestTypeAttribute, kSecDigestSHA1, &error)
if error != nil { print("verifier digest attribute setting error: ") ; println(error) }
SecTransformSetAttribute(signer, kSecTransformInputAttributeName, signedInfoData, &error)
if error != nil { print("signer attribute setting error: ") ; println(error) }

// executes the transform
signedData = (SecTransformExecute(signer, &error) as NSData)
let signedDataAsString = signedData.base64EncodedStringWithOptions(NSDataBase64EncodingOptions.EncodingEndLineWithLineFeed)
if error != nil { print("signer execute error: ") ; println(error) }

// inserts generated signedDataAsString in <SignatureValue> node
let signatureValueElements = signatureNode.elementsForName("SignatureValue") as [NSXMLElement]
if signatureValueElements.count > 0 { signatureValueElements[0].setStringValue(signedDataAsString, resolvingEntities: false) } else { println(signatureValueElements) }
signatureNode.detach()
xmlAssinaturaDocument = nil

// then replaces <Signature> placeholder node in xmlDocument
if (xmlDocument.rootElement() != nil) {
    let xmlRoot: NSXMLElement = xmlDocument.rootElement()!
    let signatureNodePlaceholder: NSXMLElement = (xmlRoot.elementsForName("Signature") as [NSXMLElement])[0]
    let signatureNodeIndex = signatureNodePlaceholder.index
    xmlRoot.replaceChildAtIndex(signatureNodeIndex, withNode: signatureNode)

    // and creates xmlDocument canonicalized String
    xmlStr = xmlRoot.canonicalXMLStringPreservingComments(false)
}

As far as I can see, everything is correct and compliant with W3.org’s specifications for XMLDSIG. Still, the server always rejects the generated XML.

I’m at wits end. Any help and wisdom will be highly appreciated!!

Понравилась статья? Поделить с друзьями:
  • Ошибка вызова внешней компоненты 1с розница
  • Ошибка вызова веб сервиса wsstube0024 error in securing outbound message
  • Ошибка вызова zlib пожалуйста перезагрузите систему проверьте игровые данные или переустановите игру
  • Ошибка вызова zlib gta 5 как исправить steam
  • Ошибка вызова zlib gta 5 как исправить rage mp