Перейти к содержимому
Tez Base

RSA-шифрование в 1С без внешних компонент: чтобы код работал и на Linux, и в тонком клиенте

· Опубликовано: · Обновлено:

Интеграции и обмены

Задача из платёжной интеграции, знакомая всем, кто подключал эквайринг или платёжный шлюз к 1С. Шлюз принимает данные карты только зашифрованными: отдаёт свой публичный ключ RSA, вы шифруете им номер карты и шлёте шифртекст, а расшифровывает его уже сам шлюз своим приватным ключом. Схема правильная, сырой номер карты нигде в открытом виде не летает. Вопрос один: как в 1С выполнить это шифрование публичным ключом.

Ответ, который вы найдёте по площадкам, почти всегда одинаковый и почти всегда подведёт вас в проде. Разберём, почему, и что с этим делать.

Разбор самой реализации, с длинной арифметикой и делением Кнута на встроенном языке, опубликован у нас на Инфостарте.

Типовое решение через COM и почему оно ломается

Стандартный совет: взять из .NET класс RSACryptoServiceProvider через COM.

КриптоПровайдер = Новый COMОбъект("System.Security.Cryptography.RSACryptoServiceProvider");
КриптоПровайдер.FromXmlString(ПубличныйКлючXML);
Шифр = КриптоПровайдер.Encrypt(Данные, Ложь);

Оно работает. Ровно до момента, когда вы выносите этот код на другую машину. .NET-классы по умолчанию не зарегистрированы для COM, и на голой системе первый же Новый COMОбъект(...) отвечает:

0x80040154: Класс не зарегистрирован

Лечится это регистрацией сборки через regasm, то есть административной вознёй на каждой машине, где будет крутиться код. А на Linux-сервере 1С и в тонком клиенте COM нет вообще, и лечить нечего: код просто не заведётся. Для обработки, которую вы отдаёте заказчику или коллегам, это плохая опора, у половины она не запустится. И проблема не в RSA, а в самой ставке на COM: любой код, который зовёт .NET через COM, привязан к Windows с зарегистрированной сборкой, и молча умирает при первом появлении Linux или тонкого клиента.

Альтернатива: RSA целиком на встроенном языке 1С

Отсюда инженерное решение: написать RSA на чистом BSL, без единой внешней компоненты, чтобы один и тот же код шёл где угодно, в толстом клиенте, в тонком и на Linux-сервере одинаково. RSA по сути одна формула: шифрование это c = m^e mod n, расшифровка m = c^d mod n, возведение в степень по модулю, и всё.

Первым пугает размер. n для боевого ключа это число на 600 с лишним десятичных цифр, а отдельного типа “длинное целое”, как BigInteger в .NET, в 1С нет. Когда мы собирали демо, исходили из того, что Число на таких значениях начнёт терять разряды, и написали длинную арифметику сами: число хранится массивом кусочков по основанию миллион, сложение, вычитание, умножение и деление идут в столбик, поверх них возведение в степень по модулю методом square-and-multiply.

Потом мы проверили саму посылку на сервере 1С 8.3.27, и она не подтвердилась. Число в памяти держит такие значения точно. 2 в степени 2048 (617 цифр), умноженное само на себя, даёт все 1 234 цифры произведения, а частное и остаток через % сходятся с эталоном BigInt цифра в цифру. Возведение в степень по 2048-битному модулю с показателем, в котором 1 024 единичных бита, заняло 173 мс. Шифрование с экспонентой 65537 уложилось в 1 мс. Оба результата тоже сверены с BigInt.

Выходит, для RSA в 1С хватает встроенного типа, а своя длинная арифметика годится как учебный разбор. Из этой истории вышло три урока, и они полезны шире самого RSA.

Урок 1: число держит все цифры, а строка режет

На экране это выглядит ровно как потеря разрядов. Строка(X) и Формат(X, "ЧГ=0") для 2 в степени 2048 возвращают только последние 309 цифр: старшие отрезаются молча, без ошибки и без многоточия. Все 617 цифр отдаёт XMLСтрока(X). Обратное преобразование потерь не даёт: и Число(Стр), и XMLЗначение(Тип("Число"), Стр) читают 617-значную строку точно.

Для криптографии это опаснее самой арифметики. Большое число хочется записать в лог, сохранить строкой или сравнить с эталоном как текст, и на любом из этих шагов Строка() или Формат() подменит его хвостом. Результат будет похож на правильный, но неверен, и в логе ни одной ошибки. Правило простое: большие числа переводим в текст только через XMLСтрока, а в тестах сверяем длину строки с ожидаемой.

Урок 2: скорость решает алгоритм, а не микрооптимизация

Первая рабочая версия движка на массивах считала модульное умножение приёмом “сдвиг-и-сложение”, который обходится без деления, только сложения и вычитания. Красиво, но расшифровка 160-битного ключа занимала 25 секунд, потому что у приёма сложность растёт квадратично по числу бит и на каждый бит идёт полноценная операция над большим числом.

Замена на честное умножение плюс деление с остатком (классический алгоритм Кнута) дала ускорение примерно в 8 раз: то же самое стало считаться за пару секунд. Разница не в том, что мы “подкрутили” старый способ, а в том, что сменили алгоритм: теперь на шаг возведения в степень приходится одно умножение и одно деление вместо сотен сложений. Урок общий: когда что-то работает на порядок медленнее ожидаемого, ищите не микрооптимизацию, а неверный алгоритм в основе.

Второй раз тот же урок сработал сильнее первого. Самый аккуратный столбик на встроенном языке проигрывает умножению, которое платформа выполняет у себя внутри: движок на массивах расшифровывает учебный ключ в 256 бит около 3 секунд, а на встроенном Число возведение в степень по модулю 2048 бит занимает 173 мс.

Урок 3: свою криптографию нельзя проверять “по кругу”

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

Поэтому проверка должна быть тройной:

  1. Классический учебный вектор. Самый маленький осмысленный RSA (n=3233, e=17, d=413): число 65 обязано зашифроваться в 2790 и расшифроваться обратно в 65. Это проверяет саму формулу на эталонных значениях из учебника.
  2. Внешний эталон, байт в байт. Тот же шифртекст для того же ключа независимо считается стандартной арифметикой больших чисел, и результат сверяется символ в символ в base64, по всем входам: и по номеру карты, и по кириллице, и по адресу почты. Вот это настоящее доказательство совместимости: наш RSA даёт ровно тот результат, что и стандартная библиотека, знак в знак, значит шлюз его поймёт.
  3. Круговой прогон. И уже поверх этого обычная сверка “зашифровали публичным, расшифровали приватным, получили исходное” на разных строках.

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

Где нативному RSA место, а где нет

Честно про границы, потому что вопрос про прод возникает сразу. Демо учебное: короткий ключ ради скорости и без PKCS#1-паддинга, это показ принципа, а не боевая криптобиблиотека. Его движок на массивах сверен с эталоном, но медленный, и под боевую длину ключа арифметику разумнее отдать встроенному Число. Сверка с внешним эталоном при этом нужна та же самая.

И тут вся суть реальной интеграции: расшифровку делает шлюз, а вам нужно только шифрование публичным ключом. С типовой экспонентой 65537 оно на встроенном Число и ключе 2048 бит заняло в нашем замере 1 мс. Для практической задачи “зашифровать номер карты перед отправкой” нативный BSL-подход рабочий: миллисекунда на шифрование, и код идёт везде, где есть платформа, без регистрации сборок на каждой машине.

Отдельно про подпись запроса, которую шлюзы требуют вместе с шифрованием: она в 1С нативна изначально. SHA256 даёт платформенный объект ХешированиеДанных, base64 встроенные функции, никакого COM. Одна тонкость: ХешированиеДанных доступен на сервере, а не на клиенте, поэтому подпись считается серверным вызовом, зато работает и на Linux тоже.

Посмотреть, как это устроено

Весь цикл (шифрование карты публичным ключом, расшифровка приватным как это сделал бы шлюз, сборка и подпись тела запроса) мы собрали в маленькую обработку на одной кнопке, целиком на встроенном языке, без COM и внешних компонент. Её можно скачать и покрутить на Инфостарте: работает на любой конфигурации от 8.3.14, ставить ничего не надо, код открыт. Полезна, чтобы вживую увидеть, как RSA выглядит без “чёрного ящика” внешней компоненты.

Тот же принцип “криптография и обмен на чистом BSL, без привязки к платформе машины” мы применяем в реальных интеграциях. Если вам нужно подключить платёжный шлюз, эквайринг или другой внешний сервис к 1С так, чтобы решение работало и на Windows, и на Linux-сервере, посмотрите нашу услугу обмена данными 1С: мы делаем интеграции переносимыми и проверяем совместимость с эталоном, а не “у нас на стенде заработало”.

Обмен требует подписи, а компоненту на Linux не поставить

Сделаем шифрование и подпись средствами платформы, чтобы код работал и на Windows, и на Linux

Стоимость
450 000 - 600 000 ₸ТЗ и диагностика, без налогов; типовая задача закрывается в тот же срок, подсистема "ОбменПоHTTP" под ключ - по смете единым счётом
Срок
от 3 дней

Рано писать? Измерьте сами

RSA на встроенном языке, а не через .NET

Открыть обработку

INFOSTART TECH EVENT 2026: конференция для 1с-специалистов. если собираетесь, регистрируйтесь по нашей ссылке. Регистрация →

Читайте также

Регистрация изменений в плане обмена 1С: почему правка не встала в очередь

Между записью объекта и строкой в таблице изменений узла в БСП стоят пять ступеней: подписка на событие, включённая синхронизация, выборочная регистрация, авторегистрация и правила регистрации. Разбираем каждую по коду типовой УТ 11.5.22, запрос к очереди узла, замер на стенде и границу того, что показывает файл правил.

Обмен 1С с внешним сервисом падает по таймауту: сервис занят или лежит, и когда повтор создаёт дубль

Снаружи занятый сервис и лежащий выглядят одинаково: запрос висит, обмен падает по таймауту. Разбираем, как различить их тремя замерами, почему читающая проверка живости врёт, какой таймаут у HTTPСоединение по умолчанию и почему повтор создания без проверки состояния плодит дубли. Числа из ночной синхронизации, которая четыре ночи подряд укладывала принимающий сервис.

В карточке 1С одно значение, в выгрузке другое: кто пишет это поле

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