Имя: Пароль:
1C
1С v8
Интеграция с Честным Знаком
,
0 1СникСоСтажем
 
14.05.22
18:23
Всем привет! Делаю интеграцию 1с с честным знаком. Задача получать входящие документы и их подписывать.

1. Ключ сессии (/api/v1/session) получаю вполне корректно, Честный знак принимает подпись
2. Входящие документы получаю корректно, но при попытке подписи документов (api/v1/incoming-documents/xml/upd/title) ошибка "Подпись не прошла проверку в crypto" (хотя при получении ключа сессии все нормально)

Ниже пример использования:

ПодписатьТекст(ЗашифроватьBase64(ФайлXML, "windows-1251"),Отпечаток,Истина);

// sThumbprint - отпечаток сертификата, используемого для подписи; строка,
// представляющая отпечаток в шестнадцатеричном виде
// пример 195934d72dcdf69149901d6632aca4562d8806d8
// ТекстДляПодписи должен быть в Base64
// bDetached - Истина/Ложь - откреплённая(для подписания документов)/прикреплённая(для получения токена авторизации) подпись
Функция ПодписатьТекст(ТекстДляПодписи, sThumbprint, bDetached)
    
    CADESCOM_BASE64_TO_BINARY = 1; // Входные данные пришли в Base64
    CADESCOM_CADES_TYPE = 1; // Тип усовершенствованной подписи
    CAPICOM_AUTHENTICATED_ATTRIBUTE_SIGNING_TIME = 0; // Атрибут штампа времени подписи
    
    oSigner = Новый COMОбъект("CAdESCOM.CPSigner");
    // Объект, задающий параметры создания и содержащий информацию об усовершенствованной подписи.
    oSigner.Certificate = ПолучитьСертификатПоОтпечатку(sThumbprint);
    oSigningTimeAttr = Новый COMОбъект("CAdESCOM.CPAttribute");
    oSigningTimeAttr.Name = CAPICOM_AUTHENTICATED_ATTRIBUTE_SIGNING_TIME;
    oSigningTimeAttr.Value = ТекущаяДата();
    oSigner.AuthenticatedAttributes2.Add(oSigningTimeAttr);
    
    ТекстДляПодписи = СокрЛП(ТекстДляПодписи);
    
    oSignedData = Новый COMОбъект("CAdESCOM.CadesSignedData");
    // Объект CadesSignedData предоставляет свойства и методы для работы с усовершенствованной подписью.
    oSignedData.ContentEncoding = CADESCOM_BASE64_TO_BINARY;
    //oSignedData.propset_ContentEncoding = CADESCOM_BASE64_TO_BINARY;
    oSignedData.Content = СокрЛП(ТекстДляПодписи);
    EncodingType = 0;
    sSignedMessage = oSignedData.SignCades(oSigner, CADESCOM_CADES_TYPE,
    bDetached, EncodingType);
    // Метод добавляет к сообщению усовершенствованную подпись.
    sSignedMessage = СтрЗаменить(sSignedMessage,Символы.ПС,"");
    sSignedMessage = СтрЗаменить(sSignedMessage,Символы.ВК,"");
    
    Возврат sSignedMessage; // Подпись в формате Base64
    
КонецФункции

//Отпечаток - строка HEX
Функция ПолучитьСертификатПоОтпечатку(ОтпечатокСтр)
    
    Рез = Неопределено; // Найденный сертификат (Com-объект)
    CAPICOM_CURRENT_USER_STORE = 2;
    //2 - Искать сертификат в ветке "Личное" хранилища.
    CAPICOM_MY_STORE = "My";
    // Указываем, что ветку "Личное" берем из хранилища текущего пользователя
    CAPICOM_STORE_OPEN_READ_ONLY = 0; // Открыть хранилище только на чтение
    oStore = Новый COMОбъект("CAdESCOM.Store"); // Объект описывает хранилище сертификатов
    
    oStore.Open(CAPICOM_CURRENT_USER_STORE, CAPICOM_MY_STORE,
    CAPICOM_STORE_OPEN_READ_ONLY); // Открыть хранилище сертификатов
    // 1 вариант: поиск сертификата по отпечатку
    //CAPICOM_CERTIFICATE_FIND_SHA1_HASH = 0;
    //Certificates = oStore.Certificates.Find(CAPICOM_CERTIFICATE_FIND_SHA1_HASH, ОтпечатокСтр);
    //Рез = Certificates.Item(1);
    
    //2 вариант: обходом по коллекции и сравнение с отпечатком
    Для Каждого ТекСертификат Из oStore.Certificates Цикл
        ТекОтпечаток = ТекСертификат.Thumbprint; // возвращается отпечаток в шестнадцатеричном виде
        Если ВРЕГ(ТекОтпечаток) = ВРЕГ(ОтпечатокСтр) Тогда Рез = ТекСертификат;
            Прервать;
        КонецЕсли;
    КонецЦикла;
    oStore.Close(); // Закрыть хранилище сертификатов и освободить объект 61
    Возврат Рез;
    
КонецФункции

ТекстДляПодписи - содержание XML файла

У клиента менялся сертификат, может быть в этом причина? Хотя при "ручном" подписании ошибок нет.

Кто сталкивался? Отзовитесь плиз.....
1 NorthWind
 
14.05.22
18:29
А штамп времени там реально нужен?
2 NorthWind
 
14.05.22
18:33
у меня есть образец рабочего кода такой фигни, но там простая подпись CADES_BES, без штампа времени. Здесь используется штамп времени, но я что-то не уверен, что он применяется у Честного знака, во всяком случае, упоминаний об этом я не нашел. Он там применяется или нет?
3 1СникСоСтажем
 
14.05.22
19:04
(2) добрый день! Хороший вопрос! Кол не мой. Предлагаете штамм времени опустить? У Вас что - то аналогичное используется в реализации?
4 NorthWind
 
14.05.22
19:27
(3) а ключ сессии вы этим же методом подписываете?
5 1СникСоСтажем
 
14.05.22
20:00
(4) ну да.bDetached Ложь только.  Там в описании функции указано.
6 timurhv
 
14.05.22
20:10
Куда сейчас документацию API выкладывают? Раньше в телеге выкладывали, сейчас - фиг разберешься.

Исходный xml отправляете ведь с подписанным? Через base64Строка прогнать и отправить?
7 1СникСоСтажем
 
14.05.22
21:27
(6) у нах сайт лёг кстати. Успел вытащить описание до санкций.

Да, файл покупателя уже с подписанным файлом от продавца. Вначале в base64, а потом уже подписываем.

Плюс в тело post запроса подпись самого файла от покупателя.

Попробую без CAPICOM_AUTHENTICATED_ATTRIBUTE_SIGNING_TIME. Отпишусь. Думаю,  что всем полезно знать решение
8 1СникСоСтажем
 
14.05.22
23:02
(2)

Решил отказаться от штамма времени в алгоритме при подписании документов:

Функция ПодписатьТекст(ТекстДляПодписи, sThumbprint, bDetached)
    
    CADESCOM_BASE64_TO_BINARY = 1; // Входные данные пришли в Base64
    CADESCOM_CADES_TYPE       = 1; // Тип усовершенствованной подписи
    //CAPICOM_AUTHENTICATED_ATTRIBUTE_SIGNING_TIME = 0; // Атрибут штампа времени подписи
    
    oSigner = Новый COMОбъект("CAdESCOM.CPSigner");
    // Объект, задающий параметры создания и содержащий информацию об усовершенствованной подписи.
    oSigner.Certificate = ПолучитьСертификатПоОтпечатку(sThumbprint);
    
    // при подписании документов не используем штамп времени
    Если Не bDetached Тогда
        oSigningTimeAttr = Новый COMОбъект("CAdESCOM.CPAttribute");
        oSigningTimeAttr.Name = CAPICOM_AUTHENTICATED_ATTRIBUTE_SIGNING_TIME;
        oSigningTimeAttr.Value = ТекущаяДатаСеанса();    
        oSigner.AuthenticatedAttributes2.Add(oSigningTimeAttr);
    КонецЕсли;
    
    ТекстДляПодписи = СокрЛП(ТекстДляПодписи);
    
    oSignedData = Новый COMОбъект("CAdESCOM.CadesSignedData");
    // Объект CadesSignedData предоставляет свойства и методы для работы с усовершенствованной подписью.
    oSignedData.ContentEncoding = CADESCOM_BASE64_TO_BINARY;
    //oSignedData.propset_ContentEncoding = CADESCOM_BASE64_TO_BINARY;
    oSignedData.Content = СокрЛП(ТекстДляПодписи);
    EncodingType = 0;
    sSignedMessage = oSignedData.SignCades(oSigner, CADESCOM_CADES_TYPE,
    bDetached, EncodingType);
    // Метод добавляет к сообщению усовершенствованную подпись.
    sSignedMessage = СтрЗаменить(sSignedMessage,Символы.ПС,"");
    sSignedMessage = СтрЗаменить(sSignedMessage,Символы.ВК,"");
    
    Возврат sSignedMessage; // Подпись в формате Base64
    
КонецФункции

Но ничего не изменилось. А Вы говорили, что у Вас есть рабочий код для подписи? Можете поделиться? Спасибо
9 H A D G E H O G s
 
14.05.22
23:08
Каждый раз читаю это и каждый раз благодарю судьбу, что мы отказались поддерживать ЧЗ.
10 H A D G E H O G s
 
14.05.22
23:11
Создателям этой жлыги можно пожелать встретить голландскую шхуну..
11 timurhv
 
14.05.22
23:16
(9) В БСП 1 функция вызывается. Но у автора, видимо, свой велосипед
12 H A D G E H O G s
 
14.05.22
23:20
(11) Да понятно, что в БСП одна функция, но что-то иногда идет не так. И это не отменяет ебаклакизм ЧЗ, которые забили на запилить приложуху на стороне клиента или хотя бы dll.
13 1СникСоСтажем
 
14.05.22
23:58
(11)

Не всегда БСПшные функции работают корректно. Велосипед, да. Но нужно в нем разобраться.
14 1СникСоСтажем
 
14.05.22
23:58
(12)

Тут согласен полностью. Но имеем то, что имеем.
15 NorthWind
 
15.05.22
09:22
(8)

    def find_cert(self, hash_str):
        """
        ищет сертификат в хранилище сертификатов по его sha1-отпечатку
        """
        current_user_store = 2
        my_store = "my"
        store_open_read_only = 0
        capicom_certificate_find_sha1_hash = 0
        store = win32com.client.Dispatch("CADESCOM.store")
        store.Open(current_user_store, my_store, store_open_read_only)
        certs = store.Certificates.Find(capicom_certificate_find_sha1_hash, hash_str)
        if certs.Count > 0:
            self.cert = certs.Item(1)

    # ---------------------------------------------------------------------------------------------------------------
    def sign(self, filename: str, prefix: str):
        """
        подписывает файл с переданным именем
        """
        cades_bes = 1
        capicom_encode_base64 = 0
        capicom_certificate_include_end_entity_only = 2
        sign_filename = self.get_bin_filename(filename, prefix)
        signer = win32com.client.Dispatch("CADESCOM.CpSigner")
        signer.Certificate = self.cert
        signer.Options = capicom_certificate_include_end_entity_only
        signed_data = win32com.client.Dispatch("CADESCOM.CadesSignedData")
        f_in = open(filename, 'rb')
        signed_data.Content = f_in.read()
        out_data = signed_data.SignCades(signer, cades_bes, True, capicom_encode_base64)
        f_out = open(sign_filename, 'wt')
        f_out.write(out_data)
        f_in.close()
        f_out.close()
16 1СникСоСтажем
 
15.05.22
10:40
(15)
Доброе утро! Что это за язык программирования? Думал пример на 1с :)

1. Поиск сертификата по sha-1 обязательно искать? У меня просто идёт поиск по коллекции сертификатов.
2.
А self, win32com и prefix что означают?
17 NorthWind
 
15.05.22
11:08
(16) это python.
Поиск сертификата есть и у вас, реализован практически точно так же как и в моем случае.
win32com.client это механизм для работы с COM-объектами на python, то же самое что Новый COMОбъект () в 1С.
prefix это строчка, которая добавляется слева к имени файла подписи, для ваших целей это вряд ли нужно.

Вообще у вас вполне нормальный код, только он сильно замусорен непонятно зачем нужными опциями: сертификат ищется двумя способами, к подписи добавляются какие-то атрибуты и штамп времени... Возможно, все это нужно ЧЗ. Или нет? У меня на этот счет сомнения.
18 1СникСоСтажем
 
15.05.22
11:38
(17)

Смотрю да. Надо короче пробовать. Но мы же текстовое содержимое файлов xml файлов подписываем? Пробовал уйти от времени - результат тот же. Подумаем короче...
19 1СникСоСтажем
 
15.05.22
11:39
(17)
Сам алгоритм то рабочий,  сессию же я получаю без проблем. Проблемы с подписью документов.
20 timurhv
 
15.05.22
13:11
(19) Для токена:
ПараметрыCMS = ЭлектроннаяПодписьКлиент.ПараметрыCMS();
ПараметрыCMS.Открепленная = Ложь;

Для Национальный каталог, True Api, ГИС МТ:
ПараметрыCMS.Открепленная = Истина;
21 1СникСоСтажем
 
15.05.22
13:45
(20)
Добрый день! А  подробнее можно? Куда этот параметр CMS вставить?
22 1СникСоСтажем
 
15.05.22
18:04
Причем сверил сам отпечаток. Полностью совпадает  с тем, как тут получают. Так что проблем серфикатом быть не должно

ДанныеСертификата = Сертификат.ДанныеСертификата.Получить();
ск = Новый СертификатКриптографии(ДанныеСертификата);
Отпечаток = ПолучитьHexСтрокуИзДвоичныхДанных(ск.Отпечаток);

Ниже код, как я заполняю справочников сертификатов (отдельный сделал, БСПшный не стал использовать):

ОбъектСертификат.Наименование        = ВладелецСертификата;
ОбъектСертификат.ВладелецСертификата = ВладелецСертификата;
ОбъектСертификат.ДатаНачала          = ТекСертификат.ValidFromDate;
ОбъектСертификат.ДатаОкончания       = ТекСертификат.ValidToDate;
ОбъектСертификат.Контейнер           = ТекСертификат.PrivateKey.UniqueContainerName;
ОбъектСертификат.Отпечаток           = ТекСертификат.Thumbprint;
        
ОбъектСертификат.Записать();

Отказался от CAPICOM_AUTHENTICATED_ATTRIBUTE_SIGNING_TIME. результат тот же...
23 1СникСоСтажем
 
16.05.22
00:40
Народ, тут до меня дошло, что нужно использовать "Новый МенеджерКриптографии" (мне же открепленная подпись нужна). Тут вопрос по параметрам? Где их взять? У кого - нибудь есть рабочий пример?
24 1СникСоСтажем
 
16.05.22
00:54
Вот код:

Функция ПодписатьТекстОтсоединеннаяПодпись(ТекстДляПодписи,Отпечаток)
    
    ФайлСПодписями = ПолучитьИмяВременногоФайла("txt");
    
    МенеджерКриптографии = Новый МенеджерКриптографии("Microsoft Enhanced Cryptographic Provider v1.0","",1);
    Хранилище  = МенеджерКриптографии.ПолучитьХранилищеСертификатов(ТипХранилищаСертификатовКриптографии.ПерсональныеСертификаты);
    Сертификат = Хранилище.НайтиПоОтпечатку(ПолучитьДвоичныеДанныеИзHexСтроки(Отпечаток));
        
    ДД_Подпись =  МенеджерКриптографии.Подписать(ТекстДляПодписи,ФайлСПодписями,Сертификат);
    
    Возврат ПолучитьСтрокуИзДвоичныхДанных(ДД_Подпись,"windows-1251");

КонецФункции // ПодписатьТекстОтсоединеннаяПодпись()

Но получаю ошибку:

ОбщийМодуль.vvoИнтеграция.Модуль(986)}: Ошибка при вызове метода контекста (Подписать)
    ДД_Подпись =  МенеджерКриптографии.Подписать(ТекстДляПодписи,ФайлСПодписями,Сертификат);
по причине:
Ошибка операции с файлом.
по причине:
Каталог не обнаружен 'PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0id2luZG93cy0xMjUxIiA\Pg0KPNTg6esgwuXw8c\w7uM9ItHB6NEzIiDC5fDx1O7w7D0iNS4wMSIgyOTU4OnrPSJPTl9OU.... Там очень длинный текст.

Как заставить его работать?
25 Тихий омут
 
16.05.22
03:37
Использую для ГИИС, но может, найдёшь что полезное.
Стр - ссылка на эл. справочника СертификатыКлючейЭлектроннойПодписиИШифрования
Код на базе БСП 3.1.5.385
КриптоСертификат=Новый СертификатКриптографии(Стр.ДанныеСертификата.Получить()); //двоичные данные сертификата
МенеджерКриптографии=Новый МенеджерКриптографии(Стр.СертификатИмяПрограммы,"",Стр.СертификатТипПрограммы); //из того же справочника
ПараметрыXMLDSig=ЭлектроннаяПодписьСлужебныйКлиентСервер.ПараметрыXMLDSig();
МенеджерКриптографии.ПарольДоступаКЗакрытомуКлючу="****";
ПодписанныйПакет=ЭлектроннаяПодписьСлужебный.Подписать(ТелоПакета,ПараметрыXMLDSig,КриптоСертификат,МенеджерКриптографии);
26 NorthWind
 
16.05.22
08:24
(24) судя по строке ошибки, вы задаете само содержимое подписи в виде строки base64 в качестве имени файла, и пытаетесь такой файл сохранить.
27 1СникСоСтажем
 
16.05.22
22:42
(26)

Добрый день! Именно так и есть, хотя бы подпись получил. Ниже код, как я это делаю.

Причем ФайлXML_ДД - двоичные данные XML файла. Если отправлять содержимое XML файла - то будет ошибка, описанная выше.

То есть вначале подпись, а потом уже ее в Base64/ На что еще обратить внимание? Подписываю файл продавца и титул покупателя одинаково

ПодписатьТекстОтсоединеннаяПодпись(ФайлXML_ДД,Отпечаток)

Функция ПодписатьТекстОтсоединеннаяПодпись(Файл_ДД,Отпечаток)
    
    МенеджерКриптографии = Новый МенеджерКриптографии("Crypto-Pro GOST R 34.10-2012 Cryptographic Service Provider","",80);
    Хранилище            = МенеджерКриптографии.ПолучитьХранилищеСертификатов(ТипХранилищаСертификатовКриптографии.ПерсональныеСертификаты);
    Сертификат           = Хранилище.НайтиПоОтпечатку(ПолучитьДвоичныеДанныеИзHexСтроки(Отпечаток));
        
    ДД_Подпись = МенеджерКриптографии.Подписать(Файл_ДД,Сертификат);
        
    Результат = Base64Строка(ДД_Подпись);
    
    Если Лев(Результат, 4) = "77u/" Тогда
        Результат = Сред(Результат, 5);
    КонецЕсли;
    
    Результат = СтрЗаменить(Результат, Символы.ПС, "");
    Результат = СтрЗаменить(Результат, Символы.ВК, "");

    Возврат Результат;

КонецФункции // ПодписатьТекстОтсоединеннаяПодпись()
Программист всегда исправляет последнюю ошибку.