При интеграции 1С со сторонними сервисами (биржами, торговыми площадками, платежными шлюзами) разработчики часто сталкиваются с необходимостью перенести существующий код авторизации с языков C#, Python или PHP на встроенный язык 1С. Одной из самых частых проблем становится формирование подписи HMAC-SHA256 и верное кодирование передаваемых данных.
Рассмотрим подробно, как перевести алгоритм подписи и отправить HTTP-запрос в 1С, а также проанализируем основные подводные камни, которые могут привести к ошибке авторизации на стороне сервера.
Разберем по шагам, что происходит в исходном C#-коде:
apiKey), метод HTTP (POST), закодированный URI в нижнем регистре, nonce и MD5-хеш тела.apiSecret декодируется из формата Base64 в массив байтов. Это критически важный момент: если использовать сам текст секретного ключа без предварительного Base64-декодирования, сформированная подпись не пройдет проверку на сервере.Выясним основные причины, почему подпись, сформированная в 1С, может не совпадать с подписью из C#:
1. Кодировка строк: Все текстовые строки перед вычислением хеш-сумм должны переводиться в двоичные данные строго в кодировке UTF-8 без сигнатуры BOM.
2. Правила URL Encode: В C# метод HttpUtility.UrlEncode приводит специальный набор символов к определенному формату и затем переводит URI в нижний регистр .ToLower(). В 1С кодирование спецсимволов функции КодироватьСтроку должно строго соответствовать ожиданиям стороннего API.
Если сервер 1С или клиентский сеанс работает под управлением ОС Windows, мы можем применить стандартные классы .NET Framework прямо из кода 1С с помощью механизмов COM. Это решение максимально точно воспроизводит логику C#.
Рассмотрим пример функции вычисления HMAC-SHA256 через COM-объекты .NET:
Функция ПолучитьHMACSHA256_NET(СтрокаСообщения, СекретBase64)
// Создаем объекты .NET для работы с UTF-8 и HMAC-SHA256
КодировщикUTF8 = Новый COMОбъект("System.Text.UTF8Encoding");
// Декодируем Base64-секрет в массив байт, как в C# (Convert.FromBase64String)
ОбъектConvert = Новый COMОбъект("System.Convert");
ДвоичныйСекрет = ОбъектConvert.FromBase64String(СекретBase64);
// Инициализируем HMACSHA256 с декодированным ключом
HMAC = Новый COMОбъект("System.Security.Cryptography.HMACSHA256");
HMAC.Key = ДвоичныйСекрет;
// Переводим подписываемую строку в байты UTF-8
БайтыСообщения = КодировщикUTF8.GetBytes_4(СтрокаСообщения);
// Вычисляем хеш
БайтыХеша = HMAC.ComputeHash_2(БайтыСообщения);
// Возвращаем результат в формате Base64
Возврат ОбъектConvert.ToBase64String(БайтыХеша);
КонецФункции
Обратите внимание: Данный способ работает быстро и надежно, но ограничен исключительно операционной системой Windows. На серверах под управлением Linux данный метод вызовет ошибку создания COM-объекта.
Для обеспечения универсальности (работа в Linux и облачных сервисах) разберем создание HMAC-SHA256 с использованием стандартных объектов 1С: ХешированиеДанных и БуферДвоичныхДанных.
Алгоритм HMAC строится на выполнении побитовой операции исключающего ИЛИ (XOR) для ключа с константами ipad (байты 0x36) и opad (байты 0x5C).
Функция ПолучитьHMACSHA256_BSL(СтрокаСообщения, СекретBase64)
// 1. Получаем двоичные данные из Base64 ключа
ДвоичныйКлюч = ПолучитьДвоичныеДанныеИзBase64Строки(СекретBase64);
БуферКлюча = Новый БуферДвоичныхДанных(ДвоичныйКлюч);
РазмерБлока = 64; // Размер блока SHA-256 в байтах
If БуферКлюча.Количество() > РазмерБлока Тогда
// Если ключ длиннее размера блока, хешируем его
ХешерКлюча = Новый ХешированиеДанных(ХешФункция.SHA256);
ХешерКлюча.Добавить(БуферКлюча);
БуферКлюча = Новый БуферДвоичныхДанных(ХешерКлюча.ХешСумма);
EndIf;
// Дополняем ключ нулями до длины блока
РабочийКлюч = Новый БуферДвоичныхДанных(РазмерБлока);
БуферКлюча.Копировать(0, РабочийКлюч, 0, БуферКлюча.Количество());
// Создаем внутренний (IPAD) и внешний (OPAD) буферы
БуферIPAD = Новый БуферДвоичныхДанных(РазмерБлока);
БуферOPAD = Новый БуферДвоичныхДанных(РазмерБлока);
Для Индекс = 0 По РазмерБлока - 1 Цикл
БайтКлюча = РабочийКлюч[Индекс];
БуферIPAD[Индекс] = БайтКлюча ВРазряд = 0x36;
БуферOPAD[Индекс] = БайтКлюча ВРазряд = 0x5C;
КонецЦикла;
// 2. Внутренний хеш: SHA256(IPAD + Сообщение)
ДвоичныеДанныеСообщения = ПолучитьДвоичныеДанныеИзСтроки(СтрокаСообщения, КодировкаТекста.UTF8, Ложь);
ПотокПамяти = Новый ПотокВПамяти;
ПотокПамяти.Записать(БуферIPAD, 0, РазмерБлока);
ПотокПамяти.Записать(Новый БуферДвоичныхДанных(ДвоичныеДанныеСообщения), 0, ДвоичныеДанныеСообщения.Размер());
ПотокПамяти.Перейти(0, СмещениеПотока.Начало);
ХешерВнутренний = Новый ХешированиеДанных(ХешФункция.SHA256);
ХешерВнутренний.Добавить(ПотокПамяти);
ВнутреннийХеш = ХешерВнутренний.ХешСумма;
// 3. Внешний хеш: SHA256(OPAD + ВнутреннийХеш)
ПотокПамятиВнешний = Новый ПотокВПамяти;
ПотокПамятиВнешний.Записать(БуферOPAD, 0, РазмерБлока);
ПотокПамятиВнешний.Записать(Новый БуферДвоичныхДанных(ВнутреннийХеш), 0, ВнутреннийХеш.Размер());
ПотокПамятиВнешний.Перейти(0, СмещениеПотока.Начало);
ХешерВнешний = Новый ХешированиеДанных(ХешФункция.SHA256);
ХешерВнешний.Добавить(ПотокПамятиВнешний);
// 4. Возвращаем Итоговый HMAC в Base64
Возврат ПолучитьBase64СтрокуИзДвоичныхДанных(ХешерВнешний.ХешСумма);
КонецФункции
Теперь объединим подготовку данных, расчет MD5 тела, генерацию Nonce и отправку запроса через объект HTTPСоединение.
Функция ОтправитьЗапросApi(URLЗапроса, APIKey, APISecret, СтруктураТела)
// Переводим тело запроса в JSON
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку();
ЗаписатьJSON(ЗаписьJSON, СтруктураТела);
СтрокаJSON = ЗаписьJSON.Закрыть();
// 1. Хеш MD5 от тела запроса в Base64
ДвоичныеДанныеТела = ПолучитьДвоичныеДанныеИзСтроки(СтрокаJSON, КодировкаТекста.UTF8, Ложь);
ХешерMD5 = Новый ХешированиеДанных(ХешФункция.MD5);
ХешерMD5.Добавить(ДвоичныеДанныеТела);
MD5Base64 = ПолучитьBase64СтрокуИзДвоичныхДанных(ХешерMD5.ХешСумма);
// 2. Генерация Nonce (GUID без разделителей)
Nonce = СтрЗаменить(Строка(Новый УникальныйИдентификатор), "-", "");
// 3. Подготовка URL и сырой подписываемой строки
КодированныйURL = НРегистр(КодироватьСтроку(URLЗапроса, СпособКодированияСтроки.URLКодированиеЗаголовков));
СтрокаСигнатуры = APIKey + "POST" + КодированныйURL + Nonce + MD5Base64;
// 4. Расчет подписи HMAC-SHA256
HMACПодпись = ПолучитьHMACSHA256_BSL(СтрокаСигнатуры, APISecret);
// 5. Формирование HTTP Запроса
Заголовки = Новый Соответствие;
Заголовки.Вставить("Content-Type", "application/json");
// Заголовок авторизации вида "amx key:signature:nonce"
ЗначениеAuth = СтрШаблон("%1:%2:%3", APIKey, HMACПодпись, Nonce);
Заголовки.Вставить("Authorization", "amx " + ЗначениеAuth);
HTTPЗапрос = Новый HTTPЗапрос(URLЗапроса, Заголовки);
HTTPЗапрос.УстановитьТелоИзСтроки(СтрокаJSON, КодировкаТекста.UTF8, ИспользованиеByteOrderMark.НеИспользовать);
// 6. Отправка
Соединение = Новый HTTPСоединение("www.aaa.ru", 443, , , , , Новый ЗащищенноеСоединениеOpenSSL);
Ответ = Соединение.ОтправитьДляОбработки(HTTPЗапрос);
Возврат Ответ.ПолучитьТелоКакСтроку();
КонецФункции
Таким образом, мы полностью воссоздали алгоритм работы оригинального C#-кода. При использовании чистого кода BSL ваша обработка или торговый робот смогут успешно выполнять авторизацию на любых платформах под управлением Windows и Linux.