При интеграции 1С с современными внешними API (такими как Google API, Apple, Firebase или сервисы банков) часто требуется передавать данные в формате JWT (JSON Web Token). В платформе 1С, начиная с версии 8.3.21, появился встроенный объект ТокенДоступа, который должен упрощать эту задачу. Однако на практике разработчики сталкиваются с серьезным ограничением: при добавлении данных в ПолезнаяНагрузка (payload) числа автоматически преобразуются в строки. Для многих API это критично, так как поля exp (время истечения), iat (время выпуска) и другие должны быть строго числовыми (Unix Timestamp).
В этой статье мы подробно разберем, почему стандартный механизм ТокенДоступа может не подходить для ваших задач, и как реализовать полноценное формирование и подпись JWT токена вручную, сохранив при этом правильную типизацию данных.
Рассмотрим стандартный код, который кажется логичным:
Токен = Новый ТокенДоступа();
Токен.ПолезнаяНагрузка.Вставить("iat", 1672531200); // Ожидаем число
При проверке такого токена на сервисах типа jwt.io мы увидим, что число превратилось в строку: "iat": "1672531200". Платформа 1С при сериализации объекта во внутренний JSON выполняет неявное приведение типов. Если принимающая сторона (API) ожидает строго integer, авторизация будет отклонена с ошибкой формата. Кроме того, при работе с большими числами (больше 999) платформа может добавить неразрывные пробелы как разделители триад, что окончательно ломает структуру токена.
Чтобы полностью контролировать структуру и типы данных в токене, нам придется пройти путь формирования JWT самостоятельно. Напомним, что JWT состоит из трех частей, разделенных точкой: Header.Payload.Signature.
Проанализируем пошаговый алгоритм реализации этой задачи в среде Windows.
Вместо использования объекта ТокенДоступа, сформируем JSON-строки самостоятельно. Мы можем использовать объект ЗаписьJSON, который позволяет точно управлять форматом вывода.
// 1. Формируем Заголовок (Header)
ЗаголовокСтруктура = Новый Структура();
ЗаголовокСтруктура.Вставить("alg", "RS256");
ЗаголовокСтруктура.Вставить("typ", "JWT");
ЗаписьJSON = Новый ЗаписьJSON();
ПараметрыЗаписи = Новый ПараметрыЗаписиJSON(,,, ЭлементыРазделенияСтрокJSON.Нет);
ЗаписьJSON.УстановитьСтроку(ПараметрыЗаписи);
ЗаписатьJSON(ЗаписьJSON, ЗаголовокСтруктура);
СтрокаЗаголовка = ЗаписьJSON.Закрыть();
// 2. Формируем Полезную нагрузку (Payload)
НагрузкаСтруктура = Новый Структура();
НагрузкаСтруктура.Вставить("iat", 1704067200); // Теперь это гарантированно число
НагрузкаСтруктура.Вставить("iss", "my-service-account");
ЗаписьJSON = Новый ЗаписьJSON();
ЗаписьJSON.УстановитьСтроку(ПараметрыЗаписи);
ЗаписатьJSON(ЗаписьJSON, НагрузкаСтруктура);
СтрокаНагрузки = ЗаписьJSON.Закрыть();
Стандарт JWT требует использования кодировки Base64URL. Она отличается от обычной Base64 тем, что символы + заменяются на -, / на _, а символы заполнения = удаляются.
В современных версиях 1С (8.3.16+) мы можем использовать встроенный метод КодированиеДанных.Base64URL. Если же ваша версия старше или вы хотите реализовать это вручную для полного контроля, используем следующий подход:
Функция ЗакодироватьBase64URL(ВходнаяСтрока)
ДвоичныеДанные = ПолучитьДвоичныеДанныеИзСтроки(ВходнаяСтрока);
Результат = Base64Строка(ДвоичныеДанные);
// Преобразование в формат URL
Результат = СтрЗаменить(Результат, "+", "-");
Результат = СтрЗаменить(Результат, "/", "_");
Результат = СтрЗаменить(Результат, "=", "");
// Удаление спецсимволов переноса строки
Результат = СтрЗаменить(Результат, Символы.ВК, "");
Результат = СтрЗаменить(Результат, Символы.ПС, "");
Возврат Результат;
КонецФункции
Часть1 = ЗакодироватьBase64URL(СтрокаЗаголовка);
Часть2 = ЗакодироватьBase64URL(СтрокаНагрузки);
ИтоговаяСтрокаДляПодписи = Часть1 + "." + Часть2;
Это самый сложный этап. Для алгоритма RS256 нам нужно создать хеш SHA256 от строки Header.Payload и подписать его закрытым ключом RSA. В ОС Windows это удобно сделать через COM-объект System.Security.Cryptography.RSACryptoServiceProvider из состава .NET Framework.
Важно: Закрытый ключ должен быть в формате XML. Если ваш ключ в формате .pem, его предварительно нужно конвертировать в XML-структуру, которую понимает .NET.
// Инициализируем криптопровайдер
RSACSP = Новый COMОбъект("System.Security.Cryptography.RSACryptoServiceProvider");
RSACSP.FromXmlString(ЗакрытыйКлючXML);
// Вычисляем хеш
Хеширование = Новый ХешированиеДанных(ХешФункция.SHA256);
Хеширование.Добавить(ИтоговаяСтрокаДляПодписи);
ХешСумма = Хеширование.ХешСумма;
// Подписываем хеш. Обратите внимание, что мы передаем бинарные данные хеша
// В 1С для передачи в COM-объект удобно использовать ADODB.Stream
ИмяВременногоФайла = ПолучитьИмяВременногоФайла();
ХешСумма.Записать(ИмяВременногоФайла);
Поток = Новый COMОбъект("ADODB.Stream");
Поток.Open();
Поток.Type = 1; // Binary
Поток.LoadFromFile(ИмяВременногоФайла);
ДвоичныеДанныеХеша = Поток.Read(-1);
Поток.Close();
// Формируем подпись
ПодписьДвоичныеДанные = RSACSP.SignHash(ДвоичныеДанныеХеша, "SHA256");
// Сохраняем результат подписи обратно в ДвоичныеДанные 1С
Поток.Open();
Поток.Write(ПодписьДвоичныеДанные);
Поток.SaveToFile(ИмяВременногоФайла, 2); // Overwrite
Поток.Close();
ДвоичныеДанныеПодписи = Новый ДвоичныеДанные(ИмяВременногоФайла);
УдалитьФайлы(ИмяВременногоФайла);
// Кодируем подпись в Base64URL
Часть3 = ЗакодироватьBase64URL(ДвоичныеДанныеПодписи); // Используем перегрузку функции для ДД
ИтоговыйТокен = ИтоговаяСтрокаДляПодписи + "." + Часть3;
Проанализируем ситуацию, если ваш сервер 1С работает на Linux. В этом случае COM-объекты (такие как RSACryptoServiceProvider или ADODB.Stream) будут недоступны. Для решения этой задачи на Linux рекомендуется:
МенеджерКриптографии. Он требует установленного в системе криптопровайдера, например, OpenSSL.ЗапуститьПриложение.При формировании JSON вручную (как мы делали в Шаге 1) следует помнить о нескольких нюансах:
Строка(Число) 1С может вставить неразрывный пробел (например, "1 000"). Всегда используйте Формат(Число, "ЧГ=0") или объект ЗаписьJSON, чтобы избежать этого.exp (expiration time) не только является числом, но и не слишком сильно опережает текущее время сервера. Обычно разница не должна превышать 3600 секунд (1 час).Таким образом, мы выяснили причину проблемы с типами данных в объекте ТокенДоступа и рассмотрели надежный способ создания JWT токена "с нуля". Использование ручного формирования JSON в сочетании с системными средствами криптографии позволяет создавать токены, полностью соответствующие международным стандартам и требованиям любых внешних API.