СертификатыУдостоверяющихЦентровФайл.По умолчанию

Конструкторы

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

Синтаксис

Новый СертификатыУдостоверяющихЦентровФайл(<ИмяФайла>, <Пароль>)
Полное описание, параметры и ограничения

СертификатыУдостоверяющихЦентровФайл.По умолчанию

СертификатыУдостоверяющихЦентровФайл (FileCertificationAuthorityCertificates)

На основании файла

Доступен, начиная с версии 8.3.1.

Синтаксис:
Новый СертификатыУдостоверяющихЦентровФайл(<ИмяФайла>, <Пароль>)
Параметры:

<ИмяФайла> (обязательный)
Тип: Строка.
Полное имя файла сертификатов удостоверяющих центров.

<Пароль> (необязательный)
Тип: Строка.
Пароль файла сертификатов удостоверяющих центров.
Описание:

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

Использование в версии:

Доступен, начиная с версии 8.3.1.

Методическая информация

Практические примеры

Четыре настоящих TLS-паспорта: собственный офлайн-план 17 → 12 + 5, без соединения

Реальный public-CA file source и КриптоПро/OpenSSL-конструкторы. Readonly поля определяют конкретные отказы собственного потребителя; полный finite passport и исходный публичный файл читаются повторно. Никакого TLS, доверия или подписи результат не обещает.

Где и как запускать

Полный модуль своей ОбщаяФорма.ПаспортTLS85. Только Windows thick-managed8.5.1.1343; native profile/fileCA/hash локальны, директивы и полный serverguard приложены.

Подготовка

  1. Только новая собственная изолированная Windows VM и собственная НОВАЯ пустая файловая ИБ по точному пути C:\1c-labs\OwnTLSProfileGuard85_932d19b2-0a48-4a03-ab90-dd39aed82704\IB. Платформа8.5.1.1343, русский встроенный язык. Все дальнейшие запуск/создание публичного сертификата/файлов выполняете вы вручную только там; агент их не выполнял.

  2. В Конфигураторе у корня: Имя=УчебныйПаспортTLS85, РежимСовместимости=НеИспользовать, ОсновнойРежимЗапуска=УправляемоеПриложение, Такси. Прикладные справочники/документы/регистры/планы/константы/обработки/отчеты, службы, общие реквизиты, задания, подписки и расширения не добавляйте. Не открывайте рабочую ИБ. Сервер проверяет metadata enum через Метаданные.СвойстваОбъектов, не через недоступный bare enum.

  3. Роли → единственная ПаспортTLS85: АдминистрированиеДанных, Администрирование, АктивныеПользователи, ТолстыйКлиент и просмотр/использование своей общей формы. Администрирование с необходимым АдминистрированиеДанных нужно для ПолучитьПользователей/Роли, АктивныеПользователи для описаний соединений. Администрирование → Пользователи: только TLS85Ученик с единственной ПаспортTLS85. Пароль выбирает человек и не помещает в пример. Это только лабораторные права, не рекомендация production.

  4. ОбщиеМодули → ПаспортПрофилей85: ГлобальныйНет, СерверДа, ВызовСервераДа, КлиентОбычноеПриложениеНет, КлиентУправляемоеПриложениеНет, ВнешнееСоединениеНет, ПривилегированныйНет, ПовторноеИспользованиеВозвращаемыхЗначений=НеИспользовать. Вставьте COMMON целиком. Свой общий модуль проверяет точный File-путь/версию/NoUse/схему/пользователя/роль/один сеанс; секретные данные и сертификаты не читает.

  5. ОбщиеФормы → управляемая ПаспортTLS85. Реквизиты: КаталогДляCA85 Строка длиной0, поле ТолькоПросмотрДа; UUIDКомплекта85 Строка36 с обычным полем ввода; Статус85 Строка длиной0 с многострочным полем ТолькоПросмотрДа. Данные каждого поля равны имени соответствующего реквизита. Команда ПроверитьПрофили85, кнопка, действие ПроверитьПрофили85. Вставьте CODE целиком и назначьте настоящие ПриСозданииНаСервере2/ПриОткрытии1. Команду/события не вызывайте вручную.

  6. Конфигурация → Модуль управляемого приложения: APP целиком, настоящее ПриНачалеРаботыСистемы0. Обновите только свою CF, закройте Конфигуратор и другие соединения. Запустите собственную ИБ толстым управляемым Windows-клиентом под TLS85Ученик. Пульт показывает реальный клиентский временный каталог; не угадывайте его по переменным окружения. Native TLS/file-source объекты и enum остаются локально на клиенте; сервер получает только вызов проверки своего сеанса без этих объектов.

  7. В этой же собственной VM подготовьте Python3 и cryptography48.0.0. Нижний полный Python-блок сохраните как own_tls85_public_ca.py в своем учебном каталоге, НЕ в модуле1С. Запустите вручную python own_tls85_public_ca.py --base "точный КаталогДляCA85, показанный своим пультом". Не используйте никакие существующие сертификаты человека, контейнеры, клиентский PKCS12, пароль, ОС-хранилище или чужую папку. Pythonhelper создаст новый UUID-каталог и только ca.pem/manifest.json; введите показанный UUID в UUIDКомплекта85.

  8. Helper генерирует свой временный RSA CA-ключ только в памяти, самоподписанный публичный CA с ownUUID/CN, BasicConstraints ca=True/path_length0, keyCertSign/crlSign и семь суток от фактическогоUTC. Он выполняет настоящий PEM roundtrip и проверку своей self-signature ДО записи публичного файла. Ключ не сериализуется, клиентский сертификат/ключ не создаются и доверие ОС не импортируется. Это не гарантирует стирания копий ключа из памяти Python; после ручной подготовки завершите этот учебный процесс. mode0700 не является обещанием WindowsACL.

  9. Конструктор КриптоПро может отказать при отсутствующем/несовместимом модуле криптографии. Подготовка установленной вами учебной VM не считается гарантией поддержки провайдера или алгоритмов. При отказе СТОП: не подставляйте OpenSSL/другие сертификаты автоматически, не снижайте режим. В установленной справке прямо указан отказ на Linux/macOS без CryptoPro CPS; здесь выбран только Windows, а не утверждение, что Windows всегда гарантирует успех. Сертификат RSA используется только как собственный публичный источник данного офлайн-паспорта; успешное криптографическое применение этим не доказано.

  10. Четыре реальные конфигурации: CSP без CA/Strict/2 заявки; CSP со своим CA/Auto/3; CSP со своим CA/Strict/5; OpenSSL со своим CA/Strict/7. Клиентский сертификат у ВСЕХ явно Неопределено. Наш учебный потребитель требует собственный CA-файл и явный эффективныйStrict, поэтому принимает только5+7=12 из17, отклоняет2+3=5. Это правило только собственного плана, НЕ универсальная рекомендация безопасности и НЕ подтверждение TLS/цепочки/отзыва/доступа.

  11. CAНеопределено означает отсутствие проверки сертификата сервера по native контракту даже при Strict. РежимНеопределено отдельно означает Auto, а не отсутствие CA; исходное nullable значение сохраняется в паспорте. Auto зависит от IgnoreServerCertificatesChainRevocationSoftFail в conf.cfg, по справке defaulttrue/мягкий режим: код не читает и не меняет этот файл. Strict при недоступном сервере проверки отзыва приводит к ошибке реальной проверки. Этот урок НЕ обещает offlinehandshake, CRL/OCSP, mTLS, SMTP, подпись или интернет-запрос.

  12. Нажмите только настоящую кнопку ПроверитьПрофили85. По реальным getter-полям проверятся четыре полных случая, два полных семипольных чтения каждого профиля, все собственные количества и суммы17/12/5. После повторного SHA256 своего публичногоCA и повторного паспорта своего сеанса появится успешная сводка. JSON reader: первичная ошибка сохраняется сразу; если штатный Close уже начат, повторного Close нет. Иначе разрешена ровно одна cleanup-попытка; ее ошибка сообщается отдельно, состояние читателя после ошибки считается неизвестным. При любом отказе остаетсяСТОП; новый ручной опыт требует закрыть пульт, выяснить причину и при поврежденном комплекте создать новый собственный UUID-комплект, не автоматический retry/откат.

  13. Сверка publicUUID/маркера/имени/SHA обнаруживает случайные изменения, но не доказывает авторство против злоумышленника, способного менять и манифест, и файл. Чтения/конструкторы не атомарны; не должно быть чужих процессов, меняющих свою папку. ИмяФайла/type/passport не доказывают загрузку сертификата, пароль, срок, доверие или работу lazy native reader во время будущегоhandshake. ОС-хранилища не просматриваются. После ручного опыта завершите собственные процессы и вручную удалите только напечатанную собственную UUID-папку либо утилизируйте лабораторную VM; программа автоматически ничего не удаляет.

&НаКлиенте
Перем Остановлен85;

&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
    ПаспортПрофилей85.ПроверитьСвоюИБ85();
    UUIDКомплекта85 = "";
    Статус85 = "Подготовьте только собственный публичный CA. TLS-соединение здесь не устанавливается.";
КонецПроцедуры

&НаКлиенте
Процедура ПриОткрытии(Отказ)
    Если Остановлен85 <> Неопределено Тогда
        ВызватьИсключение "Повторная инициализация не сбрасывает остановку";
    КонецЕсли;
    ПроверитьСреду85();
    КаталогДляCA85 = КаталогВременныхФайлов();
    Если Прав(КаталогДляCA85, 1) <> "\" Тогда
        КаталогДляCA85 = КаталогДляCA85 + "\";
    КонецЕсли;
    Остановлен85 = Ложь;
КонецПроцедуры

&НаКлиенте
Процедура ПроверитьПрофили85(Команда)
    Если Остановлен85 <> Ложь Тогда
        ВызватьИсключение "СТОП: закройте свой пульт, выясните причину, не повторяйте автоматически";
    КонецЕсли;
    Остановлен85 = Истина;
    Статус85 = "Успешный паспорт пока не подтвержден";
    Попытка
        ПроверитьСреду85();
        База = КаталогВременныхФайлов();
        Если Прав(База, 1) <> "\" Тогда
            База = База + "\";
        КонецЕсли;
        Если База <> КаталогДляCA85 Или ТипЗнч(UUIDКомплекта85) <> Тип("Строка") Или СтрДлина(UUIDКомплекта85) <> 36 Тогда
            ВызватьИсключение "Нужны фактический собственный клиентский каталог и UUID комплекта";
        КонецЕсли;
        СвойUUID = Новый УникальныйИдентификатор(UUIDКомплекта85);
        Если UUIDКомплекта85 <> НРег(Строка(СвойUUID)) Тогда
            ВызватьИсключение "Нужен канонический UUID своего ручного public-CA helper";
        КонецЕсли;
        СвойКаталог = База + "OwnTLSProfile85-" + UUIDКомплекта85 + "\";
        ИмяCA = СвойКаталог + "ca.pem";
        ИмяМанифеста = СвойКаталог + "manifest.json";
        ПроверитьФайл85(ИмяМанифеста, 16384);
        ПроверитьФайл85(ИмяCA, 65536);
        Читатель = Новый ЧтениеJSON;
        ЗакрытиеНачато85 = Ложь;
        Попытка
            Читатель.ОткрытьФайл(ИмяМанифеста);
            Манифест = ПрочитатьJSON(Читатель);
            ЗакрытиеНачато85 = Истина;
            Читатель.Закрыть();
        Исключение
            ПервичнаяОшибка85 = ОписаниеОшибки();
            ОшибкаЗакрытия85 = "";
            Если Не ЗакрытиеНачато85 Тогда
                Попытка
                    Читатель.Закрыть();
                Исключение
                    ОшибкаЗакрытия85 = ОписаниеОшибки();
                КонецПопытки;
            КонецЕсли;
            Если ОшибкаЗакрытия85 <> "" Тогда
                ВызватьИсключение "СТОП: первичная ошибка JSON: " + ПервичнаяОшибка85
                    + "; отдельная ошибка закрытия: " + ОшибкаЗакрытия85 + "; состояние читателя неизвестно";
            КонецЕсли;
            ВызватьИсключение "СТОП: первичная ошибка JSON: " + ПервичнаяОшибка85
                + "; состояние читателя после ошибки неизвестно, повторного Close и retry нет";
        КонецПопытки;
        Если ТипЗнч(Манифест) <> Тип("Структура") Или Манифест.Количество() <> 5 Тогда
            ВызватьИсключение "Нужен полный собственный пятиполный манифест";
        КонецЕсли;
        Если Не Манифест.Свойство("lesson", Метка) Или ТипЗнч(Метка) <> Тип("Строка") Или Метка <> "OwnTLSProfile85-public-CA-only"
            Или Не Манифест.Свойство("identity", UUIDМанифеста) Или ТипЗнч(UUIDМанифеста) <> Тип("Строка") Или UUIDМанифеста <> UUIDКомплекта85
            Или Не Манифест.Свойство("ca_certificate_only", ТолькоПубличныйCA) Или ТипЗнч(ТолькоПубличныйCA) <> Тип("Булево") Или ТолькоПубличныйCA <> Истина
            Или Не Манифест.Свойство("private_key_written", КлючЗаписан) Или ТипЗнч(КлючЗаписан) <> Тип("Булево") Или КлючЗаписан <> Ложь
            Или Не Манифест.Свойство("ca_sha256", ХешCA) Или ТипЗнч(ХешCA) <> Тип("Строка") Или СтрДлина(ХешCA) <> 64 Тогда
            ВызватьИсключение "Манифест не описывает только свой публичный CA";
        КонецЕсли;
        Если ХешФайла85(ИмяCA) <> ХешCA Тогда
            ВызватьИсключение "Собственный CA изменился до native factory";
        КонецЕсли;
        СвоиЦентры = Новый СертификатыУдостоверяющихЦентровФайл(ИмяCA);
        Если ТипЗнч(СвоиЦентры) <> Тип("СертификатыУдостоверяющихЦентровФайл") Или СвоиЦентры.ИмяФайла <> ИмяCA Тогда
            ВызватьИсключение "Штатный источник потерял точное имя собственного CA";
        КонецЕсли;
        Профили = Новый Массив;
        Профили.Добавить(Новый Структура("Код,Заявок,Профиль,Тип,Режим,CAЕсть", "CSP_NO_CA_STRICT", 2,
            Новый ЗащищенноеСоединениеКриптоПро(Неопределено, Неопределено, РежимПроверкиОтзываTLSСертификатаСервера.Строгий),
            Тип("ЗащищенноеСоединениеКриптоПро"), РежимПроверкиОтзываTLSСертификатаСервера.Строгий, Ложь));
        Профили.Добавить(Новый Структура("Код,Заявок,Профиль,Тип,Режим,CAЕсть", "CSP_OWN_CA_AUTO", 3,
            Новый ЗащищенноеСоединениеКриптоПро(Неопределено, СвоиЦентры, РежимПроверкиОтзываTLSСертификатаСервера.Авто),
            Тип("ЗащищенноеСоединениеКриптоПро"), РежимПроверкиОтзываTLSСертификатаСервера.Авто, Истина));
        Профили.Добавить(Новый Структура("Код,Заявок,Профиль,Тип,Режим,CAЕсть", "CSP_OWN_CA_STRICT", 5,
            Новый ЗащищенноеСоединениеКриптоПро(Неопределено, СвоиЦентры, РежимПроверкиОтзываTLSСертификатаСервера.Строгий),
            Тип("ЗащищенноеСоединениеКриптоПро"), РежимПроверкиОтзываTLSСертификатаСервера.Строгий, Истина));
        Профили.Добавить(Новый Структура("Код,Заявок,Профиль,Тип,Режим,CAЕсть", "OPENSSL_OWN_CA_STRICT", 7,
            Новый ЗащищенноеСоединениеOpenSSL(Неопределено, СвоиЦентры, РежимПроверкиОтзываTLSСертификатаСервера.Строгий),
            Тип("ЗащищенноеСоединениеOpenSSL"), РежимПроверкиОтзываTLSСертификатаСервера.Строгий, Истина));
        Если Профили.Количество() <> 4 Тогда
            ВызватьИсключение "Нужны четыре полных собственных случая";
        КонецЕсли;
        Коды = СтрРазделить("CSP_NO_CA_STRICT,CSP_OWN_CA_AUTO,CSP_OWN_CA_STRICT,OPENSSL_OWN_CA_STRICT", ",");
        Количества = Новый Массив;
        Количества.Добавить(2);
        Количества.Добавить(3);
        Количества.Добавить(5);
        Количества.Добавить(7);
        Всего = 0;
        Принято = 0;
        Отклонено = 0;
        Текст = "Только собственный офлайн-план, НЕ проверка TLS/доверия/подписи:";
        Для Индекс = 0 По 3 Цикл
            Р = Профили[Индекс];
            Если ТипЗнч(Р) <> Тип("Структура") Или Р.Количество() <> 6 Или Р.Код <> Коды[Индекс]
                Или ТипЗнч(Р.Заявок) <> Тип("Число") Или Р.Заявок <> Количества[Индекс]
                Или Р.Тип <> ?(Индекс = 3, Тип("ЗащищенноеСоединениеOpenSSL"), Тип("ЗащищенноеСоединениеКриптоПро"))
                Или Р.Режим <> ?(Индекс = 1, РежимПроверкиОтзываTLSСертификатаСервера.Авто, РежимПроверкиОтзываTLSСертификатаСервера.Строгий)
                Или ТипЗнч(Р.CAЕсть) <> Тип("Булево") Или Р.CAЕсть <> (Индекс <> 0) Тогда
                ВызватьИсключение "Изменился полный собственный случай";
            КонецЕсли;
            Было = Паспорт85(Р.Профиль, ИмяCA);
            Если Было.Тип <> Р.Тип Или Было.ЭффективныйРежим <> Р.Режим Или Не Было.КлиентНеопределен
                Или Было.CAНеопределен = Р.CAЕсть Или Было.ИмяCA <> ?(Р.CAЕсть, ИмяCA, "") Тогда
                ВызватьИсключение "Native поля не совпали с конкретными аргументами constructor";
            КонецЕсли;
            Всего = Всего + Р.Заявок;
            Если Было.Принято Тогда
                Принято = Принято + Р.Заявок;
            Иначе
                Отклонено = Отклонено + Р.Заявок;
            КонецЕсли;
            Текст = Текст + Символы.ПС + Р.Код + ": заявок=" + Строка(Р.Заявок)
                + "; исходный native режим=" + Строка(Было.ИсходныйРежим)
                + "; CA отсутствует=" + Строка(Было.CAНеопределен)
                + "; наш потребитель принял=" + Строка(Было.Принято);
            Стало = Паспорт85(Р.Профиль, ИмяCA);
            СверитьПаспорта85(Было, Стало);
        КонецЦикла;
        Если Всего <> 17 Или Принято <> 12 Или Отклонено <> 5 Или Принято + Отклонено <> Всего Тогда
            ВызватьИсключение "Фактический офлайн-план не равен 17/12/5";
        КонецЕсли;
        ПроверитьФайл85(ИмяCA, 65536);
        Если ХешФайла85(ИмяCA) <> ХешCA Или СвоиЦентры.ИмяФайла <> ИмяCA Тогда
            ВызватьИсключение "Собственный публичный источник изменился после проверки";
        КонецЕсли;
        ПроверитьСреду85();
        Статус85 = Текст + Символы.ПС + "Всего=" + Строка(Всего) + "; принято нашим планом=" + Строка(Принято)
            + "; отклонено=" + Строка(Отклонено)
            + Символы.ПС + "НЕТ HTTP-запроса, handshake, проверки цепочки/отзыва или входа по сертификату."
            + Символы.ПС + "Strict без CA не включает проверку сервера. Auto зависит от conf.cfg. Strict может отказать при недоступном сервере отзыва.";
        Остановлен85 = Ложь;
    Исключение
        Остановлен85 = Истина;
        Статус85 = "СТОП. Паспорт не подтвержден. Не снижайте режим и не подменяйте КриптоПро на OpenSSL автоматически.";
        ВызватьИсключение;
    КонецПопытки;
КонецПроцедуры

&НаКлиенте
Функция Паспорт85(Профиль, ИмяCA)
    ТипПрофиля = ТипЗнч(Профиль);
    Если ТипПрофиля = Тип("ЗащищенноеСоединениеКриптоПро") Тогда
        КриптоПро = Профиль;
        Клиент = КриптоПро.СертификатКлиента;
        Центры = КриптоПро.СертификатыУдостоверяющихЦентров;
        ИсходныйРежим = КриптоПро.РежимПроверкиОтзываTLSСертификатаСервера;
    ИначеЕсли ТипПрофиля = Тип("ЗащищенноеСоединениеOpenSSL") Тогда
        OpenSSL = Профиль;
        Клиент = OpenSSL.СертификатКлиента;
        Центры = OpenSSL.СертификатыУдостоверяющихЦентров;
        ИсходныйРежим = OpenSSL.РежимПроверкиОтзываTLSСертификатаСервера;
    Иначе
        ВызватьИсключение "Нельзя подставлять чужой/неизвестный native профиль";
    КонецЕсли;
    Если Клиент <> Неопределено Тогда
        ВызватьИсключение "Этот собственный потребитель не использует клиентские сертификаты или ключи";
    КонецЕсли;
    Если ИсходныйРежим = Неопределено Тогда
        ЭффективныйРежим = РежимПроверкиОтзываTLSСертификатаСервера.Авто;
    Иначе
        Если ТипЗнч(ИсходныйРежим) <> Тип("РежимПроверкиОтзываTLSСертификатаСервера") Тогда
            ВызватьИсключение "Native режим имеет неожиданный тип";
        КонецЕсли;
        ЭффективныйРежим = ИсходныйРежим;
    КонецЕсли;
    CAНеопределен = Центры = Неопределено;
    ИмяФактическогоCA = "";
    Если Не CAНеопределен Тогда
        Если ТипЗнч(Центры) <> Тип("СертификатыУдостоверяющихЦентровФайл") Тогда
            ВызватьИсключение "Допустим только источник собственного публичного CA, не хранилище ОС";
        КонецЕсли;
        ИмяФактическогоCA = Центры.ИмяФайла;
        Если ТипЗнч(ИмяФактическогоCA) <> Тип("Строка") Или ИмяФактическогоCA <> ИмяCA Тогда
            ВызватьИсключение "Источник указывает не на собственный публичный файл";
        КонецЕсли;
    КонецЕсли;
    ПринятоПотребителем = Не CAНеопределен И ЭффективныйРежим = РежимПроверкиОтзываTLSСертификатаСервера.Строгий;
    Возврат Новый Структура("Тип,ИсходныйРежим,ЭффективныйРежим,КлиентНеопределен,CAНеопределен,ИмяCA,Принято",
        ТипПрофиля, ИсходныйРежим, ЭффективныйРежим, Клиент = Неопределено, CAНеопределен, ИмяФактическогоCA, ПринятоПотребителем);
КонецФункции

&НаКлиенте
Процедура СверитьПаспорта85(Было, Стало)
    Если ТипЗнч(Было) <> Тип("Структура") Или ТипЗнч(Стало) <> Тип("Структура") Или Было.Количество() <> 7 Или Стало.Количество() <> 7 Тогда
        ВызватьИсключение "Сверяется полный собственный семипольный паспорт";
    КонецЕсли;
    Для Каждого Поле Из Было Цикл
        Если Не Стало.Свойство(Поле.Ключ, Значение) Тогда
            ВызватьИсключение "В свежем паспорте отсутствует поле";
        КонецЕсли;
        Если ТипЗнч(Поле.Значение) <> ТипЗнч(Значение) Или Поле.Значение <> Значение Тогда
            ВызватьИсключение "Native доступный паспорт изменился: СТОП, не откат";
        КонецЕсли;
    КонецЦикла;
КонецПроцедуры

&НаКлиенте
Процедура ПроверитьФайл85(ИмяФайла, Максимум)
    Файл85 = Новый Файл(ИмяФайла);
    Если Не Файл85.Существует() Или Не Файл85.ЭтоФайл() Тогда
        ВызватьИсключение "Нет собственного файла ручной подготовки";
    КонецЕсли;
    Размер = Файл85.Размер();
    Если Размер <= 0 Или Размер > Максимум Тогда
        ВызватьИсключение "Свой файл пуст или слишком велик";
    КонецЕсли;
КонецПроцедуры

&НаКлиенте
Функция ХешФайла85(ИмяФайла)
    #Если ТолстыйКлиентУправляемоеПриложение Тогда
        Данные = Новый ДвоичныеДанные(ИмяФайла);
        Хеш = Новый ХешированиеДанных(ХешФункция.SHA256);
        Хеш.Добавить(Данные);
        Если ТипЗнч(Хеш.ХешСумма) <> Тип("ДвоичныеДанные") Тогда
            ВызватьИсключение "SHA256 должен вернуть двоичные данные, не число CRC32";
        КонецЕсли;
        Возврат НРег(ПолучитьHexСтрокуИзДвоичныхДанных(Хеш.ХешСумма));
    #Иначе
        ВызватьИсключение "Этот синхронный hash-процесс только для толстого управляемого клиента";
    #КонецЕсли
КонецФункции

&НаКлиенте
Процедура ПроверитьСреду85()
    ПроверитьСеанс85();
    #Если ТолстыйКлиентУправляемоеПриложение Тогда
        Система = Новый СистемнаяИнформация;
        Если (Система.ТипПлатформы <> ТипПлатформы.Windows_x86 И Система.ТипПлатформы <> ТипПлатформы.Windows_x86_64) Тогда
            ВызватьИсключение "Нужен только свой Windows thick-managed клиент 8.5.1.1343";
        КонецЕсли;
    #Иначе
        ВызватьИсключение "Не переносить урок в thin/web/mobile клиент";
    #КонецЕсли
КонецПроцедуры

&НаСервереБезКонтекста
Процедура ПроверитьСеанс85()
    ПаспортПрофилей85.ПроверитьСвоюИБ85();
КонецПроцедуры
Полный серверный паспорт собственной пустой CF/пользователя/роли/сеанса

ОбщиеМодули → ПаспортПрофилей85 → Модуль целиком

ServerДа/ServerCallДа, обаClientНет, неглобальный/непривилегированный/безcache. Native TLS объекты не получают RPC.

Процедура ПроверитьСвоюИБ85() Экспорт
    Если СтрокаСоединенияИнформационнойБазы() <> "File=""C:\1c-labs\OwnTLSProfileGuard85_932d19b2-0a48-4a03-ab90-dd39aed82704\IB"";" Тогда
        ВызватьИсключение "Только новая собственная файловая ИБ по точному учебному пути";
    КонецЕсли;
    Если Метаданные.РежимСовместимости <> Метаданные.СвойстваОбъектов.РежимСовместимости.НеИспользовать Или ПривилегированныйРежим() Или ТранзакцияАктивна() Тогда
        ВызватьИсключение "Версия/своя конфигурация/NoUse/непривилегированный контекст не совпали";
    КонецЕсли;
    Если Метаданные.Справочники.Количество() <> 0 Или Метаданные.Документы.Количество() <> 0
        Или Метаданные.Константы.Количество() <> 0 Или Метаданные.Перечисления.Количество() <> 0
        Или Метаданные.РегистрыСведений.Количество() <> 0 Или Метаданные.РегистрыНакопления.Количество() <> 0
        Или Метаданные.РегистрыБухгалтерии.Количество() <> 0 Или Метаданные.РегистрыРасчета.Количество() <> 0
        Или Метаданные.ПланыСчетов.Количество() <> 0 Или Метаданные.ПланыВидовХарактеристик.Количество() <> 0
        Или Метаданные.ПланыВидовРасчета.Количество() <> 0 Или Метаданные.ПланыОбмена.Количество() <> 0
        Или Метаданные.БизнесПроцессы.Количество() <> 0 Или Метаданные.Задачи.Количество() <> 0
        Или Метаданные.Последовательности.Количество() <> 0 Или Метаданные.ОбщиеРеквизиты.Количество() <> 0
        Или Метаданные.ВнешниеИсточникиДанных.Количество() <> 0 Или Метаданные.HTTPСервисы.Количество() <> 0
        Или Метаданные.WebСервисы.Количество() <> 0 Или Метаданные.РегламентныеЗадания.Количество() <> 0
        Или Метаданные.ПодпискиНаСобытия.Количество() <> 0 Или Метаданные.Отчеты.Количество() <> 0
        Или Метаданные.Обработки.Количество() <> 0 Тогда
        ВызватьИсключение "Только новая пустая прикладная модель без интеграций и заданий";
    КонецЕсли;
    Если Метаданные.ЕстьИзмененияРасширениямиКонфигурации()
        Или РасширенияКонфигурации.Получить(, ИсточникРасширенийКонфигурации.БазаДанных).Количество() <> 0
        Или РасширенияКонфигурации.Получить(, ИсточникРасширенийКонфигурации.СеансАктивные).Количество() <> 0
        Или РасширенияКонфигурации.Получить(, ИсточникРасширенийКонфигурации.СеансОтключенные).Количество() <> 0 Тогда
        ВызватьИсключение "Не должно быть расширений базы или сеанса";
    КонецЕсли;
    Если Метаданные.ОбщиеМодули.Количество() <> 1 Или Метаданные.ОбщиеФормы.Количество() <> 1
        Или Метаданные.ОбщиеФормы.Найти("ПаспортTLS85") = Неопределено Или Метаданные.Роли.Количество() <> 1 Тогда
        ВызватьИсключение "Нужны только свой общий модуль, пульт и роль";
    КонецЕсли;
    Модуль = Метаданные.ОбщиеМодули.Найти("ПаспортПрофилей85");
    Если Модуль = Неопределено Тогда
        ВызватьИсключение "Нет собственного серверного паспорта";
    КонецЕсли;
    Если Модуль.Глобальный Или Не Модуль.Сервер Или Не Модуль.ВызовСервера
        Или Модуль.КлиентОбычноеПриложение Или Модуль.КлиентУправляемоеПриложение
        Или Модуль.ВнешнееСоединение Или Модуль.Привилегированный Тогда
        ВызватьИсключение "Паспорт должен быть только неглобальным непривилегированным серверным модулем";
    КонецЕсли;
    Если Модуль.ПовторноеИспользованиеВозвращаемыхЗначений <> Метаданные.СвойстваОбъектов.ПовторноеИспользованиеВозвращаемыхЗначений.НеИспользовать Тогда
        ВызватьИсключение "Для неглобального модуля кеширование должно быть отключено";
    КонецЕсли;
    Если Не ПравоДоступа("Администрирование", Метаданные) Или Не ПравоДоступа("АдминистрированиеДанных", Метаданные)
        Или Не ПравоДоступа("АктивныеПользователи", Метаданные) Тогда
        ВызватьИсключение "Только свой лабораторный администратор с просмотром соединений";
    КонецЕсли;
    Пользователь = ПользователиИнформационнойБазы.ТекущийПользователь();
    Если Пользователь = Неопределено Или ТипЗнч(Пользователь) <> Тип("ПользовательИнформационнойБазы") Тогда
        ВызватьИсключение "Нет настоящего текущего пользователя";
    КонецЕсли;
    Если Пользователь.Имя <> "TLS85Ученик" Тогда
        ВызватьИсключение "Другой пользователь не допускается";
    КонецЕсли;
    Пользователи = ПользователиИнформационнойБазы.ПолучитьПользователей();
    Если ТипЗнч(Пользователи) <> Тип("Массив") Или Пользователи.Количество() <> 1 Тогда
        ВызватьИсключение "Должен быть только свой учебный пользователь";
    КонецЕсли;
    Свой = Пользователи[0];
    Если ТипЗнч(Свой) <> Тип("ПользовательИнформационнойБазы") Или Свой.Имя <> Пользователь.Имя
        Или Свой.УникальныйИдентификатор <> Пользователь.УникальныйИдентификатор Тогда
        ВызватьИсключение "Свежий список не совпал с собственным пользователем сеанса";
    КонецЕсли;
    СвояРоль = Метаданные.Роли.Найти("ПаспортTLS85");
    Если СвояРоль = Неопределено Или Не Свой.Роли.Содержит(СвояРоль) Тогда
        ВызватьИсключение "Нет собственной учебной роли";
    КонецЕсли;
    ЧислоРолей = 0;
    Для Каждого Роль Из Свой.Роли Цикл
        Если Роль <> СвояРоль Тогда
            ВызватьИсключение "Чужая роль не допускается";
        КонецЕсли;
        ЧислоРолей = ЧислоРолей + 1;
    КонецЦикла;
    Если ЧислоРолей <> 1 Или ПолучитьСоединенияИнформационнойБазы().Количество() <> 1 Тогда
        ВызватьИсключение "Нужны единственная собственная роль и одно соединение";
    КонецЕсли;
КонецПроцедуры
Полный штатный запуск своего управляемого пульта

Конфигурация → Модуль управляемого приложения

Настоящий native ПриНачалеРаботыСистемы0, GetForm3526 общей формы; не manualdelivery событий.

Процедура ПриНачалеРаботыСистемы()
    СвояФорма = ПолучитьФорму("ОбщаяФорма.ПаспортTLS85");
    Если СвояФорма = Неопределено Тогда
        ВызватьИсключение "Нет настоящего собственного управляемого пульта";
    КонецЕсли;
    СвояФорма.Открыть();
КонецПроцедуры
Полная будущая ручная подготовка ТОЛЬКО нового публичного CA

Отдельный own_tls85_public_ca.py в собственной новой изолированной Windows VM, НЕ модуль1С

Python3/cryptography48.0.0, вручную. Создаёт только новый ownUUID/ca.pem/manifest; ключтолькоRAM/noOSstore/noTLS. Здесь НЕ запускался.

import argparse
import hashlib
import importlib.metadata
import json
import os
import uuid
from datetime import datetime, timedelta, timezone
from pathlib import Path

from cryptography import x509
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.x509.oid import NameOID


def prepare_public_ca(base):
    if os.name != "nt" or importlib.metadata.version("cryptography") != "48.0.0":
        raise RuntimeError("Only your isolated Windows VM with cryptography48.0.0")
    base = Path(base).resolve(strict=True)
    if not base.is_dir():
        raise ValueError("Use the actual client temp directory shown by your own1C form")
    identity = str(uuid.uuid4())
    key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
    now = datetime.now(timezone.utc)
    name = x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, "OwnTLSProfile85 CA " + identity)])
    ca = (x509.CertificateBuilder().subject_name(name).issuer_name(name)
          .public_key(key.public_key()).serial_number(x509.random_serial_number())
          .not_valid_before(now - timedelta(minutes=5)).not_valid_after(now + timedelta(days=7))
          .add_extension(x509.BasicConstraints(ca=True, path_length=0), critical=True)
          .add_extension(x509.KeyUsage(digital_signature=True, content_commitment=False,
                                      key_encipherment=False, data_encipherment=False,
                                      key_agreement=False, key_cert_sign=True, crl_sign=True,
                                      encipher_only=None, decipher_only=None), critical=True)
          .sign(key, hashes.SHA256()))
    ca.verify_directly_issued_by(ca)
    payload = ca.public_bytes(serialization.Encoding.PEM)
    reloaded = x509.load_pem_x509_certificate(payload)
    reloaded.verify_directly_issued_by(reloaded)
    if reloaded.subject != name or reloaded.issuer != name:
        raise RuntimeError("Actual public certificate lost the own UUID subject")
    if not reloaded.extensions.get_extension_for_class(x509.BasicConstraints).value.ca:
        raise RuntimeError("Actual public certificate must be a CA")
    if reloaded.fingerprint(hashes.SHA256()) != ca.fingerprint(hashes.SHA256()):
        raise RuntimeError("Actual PEM public roundtrip changed the certificate")
    folder = base / ("OwnTLSProfile85-" + identity)
    folder.mkdir(mode=0o700, exist_ok=False)
    manifest = dict(lesson="OwnTLSProfile85-public-CA-only", identity=identity,
                    ca_sha256=hashlib.sha256(payload).hexdigest(),
                    ca_certificate_only=True, private_key_written=False)
    for filename, content in (("ca.pem", payload),
                              ("manifest.json", json.dumps(manifest, ensure_ascii=True, indent=2).encode("utf-8"))):
        with (folder / filename).open("xb") as stream:
            stream.write(content)
    print("Own public-CA UUID:", identity)
    print("Own public-only folder:", folder)
    print("No private key/client certificate/OS import/network connection produced.")


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("--base", required=True)
    arguments = parser.parse_args()
    prepare_public_ca(arguments.base)


if __name__ == "__main__":
    main()

Разбор по шагам

  1. Подготовьте собственную новую CF/VM/роль/пульт и вручную создайте только publicCA новым полным helper.

  2. ExactUUID/путь/размер/полный5field manifest/SHA проверяются ДО native fileCA/TLS constructors.

  3. Создаются3КриптоПро+1OpenSSL. В каждой из4строк проверяются тип/код/количество/реальные3ROполя/предполагаемыйCA и режим.

  4. Native Undefined режима нормализуется в Auto по documentedконтракту, но rawnullable значение не теряется. UndefinedCA независим и означает отсутствие проверки сервера.

  5. Только actualCAownfilename+effectiveStrict принимаются нашим офлайн-потребителем:5+7, не2+3. Все суммы считаются из настоящих строк.

  6. Второй полный7field passport каждого native объекта, свежий SHApublicCA и паспорт собственной ИБ завершают опыт без сетевого обращения.

Как проверить результат

  1. Client certificate реальных объектов должен бытьUndefined: никакого неизвестного client/private source.

  2. CA при наличии только FileCertificationAuthorityCertificates с точным ownUUIDfilename; OS-store/чужой файл/неизвестный тип запрещены.

  3. Readonly свойства не присваиваются; wrongBool/другой enum в constructor третьего аргумента не допускаются.

  4. Проверены полные4строки и17/12/5, не подставленные goldenсуммы. Все7scalar fields паспорта сравниваются по ключам/типам/значениям.

  5. Изменение своегоCA/манифеста/схемы/пользователя/снимка или ошибкаCSP оставляетSTOP без fallback/downgrade.

  6. Положительное ownplan решение не называется verifiedcertificate или успешным TLS.

Ожидаемый результат

При ручном выполнении в своей подготовленной VM создаются4конфигурационных объекта; только наш потребитель планирует12заявок и отклоняет5. Сеть/handshake/CRL/OCSP/цепочка/аутентификация/подпись НЕ выполнялись и НЕ подтверждены.

На что обратить внимание

  1. Строгий отзыв не включает CA-проверку при Undefined источнике. Авто зависит от actualconf.cfg, а не наших предположений.

  2. Нативные type примеры содержат typoКритоПро и другое имя enum: используйте точныйctor365 и ServerTLSCertificateRevocationCheckMode, не копируйте ошибки справки.

  3. Веб-клиент игнорирует первые2OpenSSL аргумента, поэтому он исключен; HashingData.Append здесь синхронный и толькоthick-managed Windows.

  4. Новый публичныйCA/SHA/паспорт не являются prooftrust или гарантией поддержки RSA/GOST/провайдера. Privatekey никогда не сериализуется; стирание памяти не обещано.

  5. Не обещаны атомарность/откат/универсальныйTLSуспех. Все кодовые блоки только написаны/статически проверены, реальные подготовка/сертификаты/файлы/соединения агентом не выполнялись.

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

Подготовить свой CA и клиентский PKCS12 с закрытым ключом, затем собрать файловый TLS-профиль

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

Где и как запускать

Полный модуль основной управляемой формы УчебныеФайловыеTLS85. Собственный толстый управляемый Windows-клиент8.5.1.1343; не серверный модуль. Python отдельно, в своей VM.

Подготовка

  1. Используйте только собственную изолированную WindowsVM и новую пустую базу8.5.1.1343 с именем конфигурации УчебныеФайловыеTLS85, русский встроенный язык, Такси, совместимость Не использовать. Предметные объекты/расширения/задания запрещены guard. Не импортируйте production-сертификаты и не используйте рабочие ключи.

  2. Создайте внешнюю обработку УчебныеФайловыеTLS85, основную управляемую форму. Реквизиты: КаталогДляКомплекта85 Строка длиной0, ТолькоПросмотр; UUIDКомплекта85 Строка длиной36; ПарольКомплекта85 Строка длиной64; Статус85 Строка длиной0, ТолькоПросмотр. У поля пароля включите РежимПароля, не сохраняйте значение настройками/при закрытии. Команда ПроверитьФайловыйПрофиль85, одноименное действие, кнопка, ИзменяетСохраняемыеДанные выключено. Назначьте ПриСозданииНаСервере и ПриОткрытии; основной блок вставьте целиком в модуль этой формы.

  3. Откройте свою обработку толстым управляемым клиентом Windows8.5.1.1343. КаталогДляКомплекта85 показывает фактический клиентский временный каталог1С. Не угадывайте его по TEMP/Python: у приложений пути могут различаться. Скопируйте этот точный путь для аргумента --base следующего скрипта.

  4. На той же собственной VM подготовьте Python3 и отдельное виртуальное окружение: python -m venv .venv; затем .venv\Scripts\python.exe -m pip install cryptography==48.0.0. Эти команды выполняете вы вручную; пакеты в вашем рабочем окружении здесь не устанавливались. Сохраните отдельный нижний блок Python как own_tls85_fixture.py в собственной учебной папке. Не вставляйте Python в модуль1С.

  5. В терминале этой VM запустите .venv\Scripts\python.exe own_tls85_fixture.py --base "точный путь КаталогДляКомплекта85 из вашей формы". Придумайте новый только учебный пароль из16-64 печатных ASCII-символов и повторите его: getpass не выводит ввод. Скрипт сам создает свежий UUID-каталог OwnTLS85-UUID, ca.pem, client.p12 и manifest.json, не перезаписывает существующие файлы. Вводите реальный показанный UUID в UUIDКомплекта85, пароль — в поле пароля формы.

  6. Скрипт — полноценная подготовка источников: CA имеет BasicConstraints ca=True/path_length0 и право подписи; клиент имеет ca=False, CLIENT_AUTH и свой RSA-ключ, подписан собственным CA. PKCS12 содержит закрытый ключ, клиентский сертификат и CA. Скрипт перечитывает собственный PKCS12 и сверяет ключ и отпечатки до записи. В ca.pem только публичный CA, приватный ключ CA вообще не сохраняется. Сертификаты действуют семь суток от фактического UTC-времени запуска.

  7. Держите каталог только в отдельной VM под собственным учебным пользователем; mode0700 не является обещанием WindowsACL. PKCS12-пароль не заменяет защиту каталога и не предназначен для отправки ключа другим людям. Не импортируйте CA в доверие ОС и не устанавливайте личный сертификат. После опыта закройте форму и вручную удалите только напечатанный собственный UUID-каталог или утилизируйте учебную VM. Автоматического удаления/сети в примерах нет.

&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
    ПроверитьСвойПустойСтенд();
    UUIDКомплекта85 = "";
    ПарольКомплекта85 = "";
    Статус85 = "Свой комплект еще не выбран";
КонецПроцедуры

&НаКлиенте
Процедура ПриОткрытии(Отказ)
    КаталогДляКомплекта85 = КаталогВременныхФайлов();
    Если Прав(КаталогДляКомплекта85, 1) <> "\" Тогда
        КаталогДляКомплекта85 = КаталогДляКомплекта85 + "\";
    КонецЕсли;
КонецПроцедуры

&НаКлиенте
Процедура ПроверитьФайловыйПрофиль85(Команда)
    ЛокальныйПароль = ПарольКомплекта85;
    ПарольКомплекта85 = "";
    Статус85 = "Проверка не подтверждена";
    Попытка
        ПроверитьСеанс85();
        #Если ТолстыйКлиентУправляемоеПриложение Тогда
            Система = Новый СистемнаяИнформация;
            Если (Система.ТипПлатформы <> ТипПлатформы.Windows_x86 И Система.ТипПлатформы <> ТипПлатформы.Windows_x86_64) Тогда
                ВызватьИсключение "Нужен собственный толстый управляемый Windows-клиент8.5.1.1343";
            КонецЕсли;
            Если СтрДлина(ЛокальныйПароль) < 16 Или СтрДлина(ЛокальныйПароль) > 64 Тогда
                ВызватьИсключение "Введите пароль своего учебного комплекта";
            КонецЕсли;
            СвойUUID = Новый УникальныйИдентификатор(UUIDКомплекта85);
            ТекстUUID = НРег(Строка(СвойUUID));
            Если UUIDКомплекта85 <> ТекстUUID Тогда
                ВызватьИсключение "Нужен точный UUID, выведенный своим Python-скриптом";
            КонецЕсли;
            БазаФайлов = КаталогВременныхФайлов();
            Если Прав(БазаФайлов, 1) <> "\" Тогда
                БазаФайлов = БазаФайлов + "\";
            КонецЕсли;
            СвойКаталог = БазаФайлов + "OwnTLS85-" + ТекстUUID + "\";
            ИмяCA = СвойКаталог + "ca.pem";
            ИмяКлиента = СвойКаталог + "client.p12";
            ИмяМанифеста = СвойКаталог + "manifest.json";
            ПроверитьСвойФайл85(ИмяМанифеста, 16384);
            ПроверитьСвойФайл85(ИмяCA, 65536);
            ПроверитьСвойФайл85(ИмяКлиента, 262144);
            Читатель = Новый ЧтениеJSON;
            Попытка
                Читатель.ОткрытьФайл(ИмяМанифеста);
                Манифест = ПрочитатьJSON(Читатель);
                Читатель.Закрыть();
            Исключение
                Читатель.Закрыть();
                ВызватьИсключение;
            КонецПопытки;
            Если ТипЗнч(Манифест) <> Тип("Структура") Тогда
                ВызватьИсключение "Не получен собственный манифест";
            КонецЕсли;
            Если Не Манифест.Свойство("lesson", Метка) Или Метка <> "OwnTLS85-file-source"
                Или Не Манифест.Свойство("identity", UUIDМанифеста) Или UUIDМанифеста <> ТекстUUID Тогда
                ВызватьИсключение "Манифест другого опыта";
            КонецЕсли;
            Если Не Манифест.Свойство("client_private_key_included", ЕстьКлюч) Или ЕстьКлюч <> Истина
                Или Не Манифест.Свойство("ca_certificate_only", ТолькоСертификатCA) Или ТолькоСертификатCA <> Истина
                Или Не Манифест.Свойство("imported_into_os", Импортирован) Или Импортирован <> Ложь Тогда
                ВызватьИсключение "Паспорт собственного учебного комплекта неверен";
            КонецЕсли;
            Если Не Манифест.Свойство("ca_sha256", ХешCA) Или ТипЗнч(ХешCA) <> Тип("Строка")
                Или Не Манифест.Свойство("client_sha256", ХешКлиента) Или ТипЗнч(ХешКлиента) <> Тип("Строка") Тогда
                ВызватьИсключение "Нет собственных контрольных сумм";
            КонецЕсли;
            Если ПолучитьСвойХеш85(ИмяCA) <> ХешCA Или ПолучитьСвойХеш85(ИмяКлиента) <> ХешКлиента Тогда
                ВызватьИсключение "Файлы изменились после создания манифеста";
            КонецЕсли;
            ЦентрыФайл = Новый СертификатыУдостоверяющихЦентровФайл(ИмяCA);
            КлиентФайл = Новый СертификатКлиентаФайл(ИмяКлиента, ЛокальныйПароль);
            ЛокальныйПароль = "";
            Если ЦентрыФайл.ИмяФайла <> ИмяCA Или КлиентФайл.ИмяФайла <> ИмяКлиента Тогда
                ВызватьИсключение "Источники не сохранили свои имена файлов";
            КонецЕсли;
            Профиль = Новый ЗащищенноеСоединениеOpenSSL(КлиентФайл, ЦентрыФайл);
            Если ТипЗнч(Профиль.СертификатКлиента) <> Тип("СертификатКлиентаФайл")
                Или ТипЗнч(Профиль.СертификатыУдостоверяющихЦентров) <> Тип("СертификатыУдостоверяющихЦентровФайл") Тогда
                ВызватьИсключение "TLS потерял собственные файловые источники";
            КонецЕсли;
            Если Профиль.СертификатКлиента.ИмяФайла <> ИмяКлиента
                Или Профиль.СертификатыУдостоверяющихЦентров.ИмяФайла <> ИмяCA Тогда
                ВызватьИсключение "TLS содержит другие файловые источники";
            КонецЕсли;
            Статус85 = "Собственный файловый TLS-профиль подготовлен. Сеть, доверие серверу, отзыв и mTLS-вход не проверены";
            Сообщить(Статус85);
        #Иначе
            ВызватьИсключение "Для синхронного SHA256 нужен толстый управляемый клиент. Урок не переносится в тонкий/веб-клиент";
        #КонецЕсли
    Исключение
        ЛокальныйПароль = "";
        Сообщить("Проверка не подтверждена. Проверьте свою VM, UUID, файлы, формат PKCS12 и пароль. Пароль и приватный ключ не выводятся");
    КонецПопытки;
    ЛокальныйПароль = "";
КонецПроцедуры

&НаКлиенте
Процедура ПроверитьСвойФайл85(ИмяФайла, МаксимальныйРазмер)
    СвойФайл = Новый Файл(ИмяФайла);
    Если Не СвойФайл.Существует() Или Не СвойФайл.ЭтоФайл() Тогда
        ВызватьИсключение "Нет собственного файла опыта";
    КонецЕсли;
    Размер = СвойФайл.Размер();
    Если Размер <= 0 Или Размер > МаксимальныйРазмер Тогда
        ВызватьИсключение "Свой файл пуст или слишком велик";
    КонецЕсли;
КонецПроцедуры

&НаКлиенте
Функция ПолучитьСвойХеш85(ИмяФайла)
    #Если ТолстыйКлиентУправляемоеПриложение Тогда
    Данные = Новый ДвоичныеДанные(ИмяФайла);
    Хеш = Новый ХешированиеДанных(ХешФункция.SHA256);
    Хеш.Добавить(Данные);
    Возврат НРег(ПолучитьHexСтрокуИзДвоичныхДанных(Хеш.ХешСумма));
    #Иначе
    ВызватьИсключение "Синхронный хеш этого урока доступен только в толстом управляемом клиенте";
    #КонецЕсли
КонецФункции

&НаСервереБезКонтекста
Процедура ПроверитьСеанс85()
    ПроверитьСвойПустойСтенд();
КонецПроцедуры

&НаСервереБезКонтекста
Процедура ПроверитьСвойПустойСтенд()
    Если Метаданные.Справочники.Количество() <> 0 Или Метаданные.Документы.Количество() <> 0 Или Метаданные.Перечисления.Количество() <> 0 Или Метаданные.РегистрыСведений.Количество() <> 0 Или Метаданные.РегистрыНакопления.Количество() <> 0 Или Метаданные.РегистрыБухгалтерии.Количество() <> 0 Или Метаданные.РегистрыРасчета.Количество() <> 0 Или Метаданные.ПланыВидовХарактеристик.Количество() <> 0 Или Метаданные.ПланыВидовРасчета.Количество() <> 0 Или Метаданные.ПланыСчетов.Количество() <> 0 Или Метаданные.ПланыОбмена.Количество() <> 0 Или Метаданные.Последовательности.Количество() <> 0 Или Метаданные.БизнесПроцессы.Количество() <> 0 Или Метаданные.Задачи.Количество() <> 0 Или Метаданные.ОбщиеРеквизиты.Количество() <> 0 Или Метаданные.ПодпискиНаСобытия.Количество() <> 0 Или Метаданные.РегламентныеЗадания.Количество() <> 0 Или Метаданные.ВнешниеИсточникиДанных.Количество() <> 0 Тогда
        ВызватьИсключение "Нужен только свой новый пустой учебный стенд";
    КонецЕсли;
    Если Метаданные.ЕстьИзмененияРасширениямиКонфигурации() Тогда
        ВызватьИсключение "Свой сеанс не должен изменяться расширениями";
    КонецЕсли;
    Источники = Новый Массив;
    Источники.Добавить(ИсточникРасширенийКонфигурации.БазаДанных);
    Источники.Добавить(ИсточникРасширенийКонфигурации.СеансАктивные);
    Источники.Добавить(ИсточникРасширенийКонфигурации.СеансОтключенные);
    Для Каждого Источник Из Источники Цикл
        Расширения = РасширенияКонфигурации.Получить(, Источник);
        Если ТипЗнч(Расширения) <> Тип("Массив") Тогда
            ВызватьИсключение "Native список расширений должен иметь тип Массив";
        КонецЕсли;
        Если Расширения.Количество() <> 0 Тогда
            ВызватьИсключение "В своём новом стенде нет расширений базы или сеанса";
        КонецЕсли;
    КонецЦикла;
    Если ТранзакцияАктивна() Тогда
        ВызватьИсключение "Чужая или вложенная активная транзакция не допускается";
    КонецЕсли;
КонецПроцедуры
Python: own_tls85_fixture.py

Отдельный файл own_tls85_fixture.py в своей изолированной WindowsVM; не модуль1С

Python3 и cryptography48.0.0. Ручной запуск создает только новый собственный UUID-каталог, настоящие учебные ключи и сертификаты. Этот блок здесь не исполнялся.

import argparse
import getpass
import hashlib
import importlib.metadata
import json
import os
import sys
import uuid
from datetime import datetime, timedelta, timezone
from pathlib import Path

from cryptography import x509
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives.serialization import pkcs12
from cryptography.x509.oid import ExtendedKeyUsageOID, NameOID


def build_own_fixture(base, password):
    if os.name != "nt" or importlib.metadata.version("cryptography") != "48.0.0":
        raise RuntimeError("Use your isolated Windows VM and cryptography48.0.0")
    base = Path(base).resolve(strict=True)
    if not base.is_dir():
        raise ValueError("Copy the existing temporary directory from your own1C form")
    if not 16 <= len(password) <= 64 or not password.isascii() or not password.isprintable():
        raise ValueError("Use a new training-only printable ASCII password,16-64 characters")
    identity = str(uuid.uuid4())
    now = datetime.now(timezone.utc)
    expiry = now + timedelta(days=7)
    ca_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
    client_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
    ca_name = x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, "OwnTLS85 CA " + identity)])
    client_name = x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, "OwnTLS85 Client " + identity)])
    ca = (x509.CertificateBuilder().subject_name(ca_name).issuer_name(ca_name)
          .public_key(ca_key.public_key()).serial_number(x509.random_serial_number())
          .not_valid_before(now - timedelta(minutes=5)).not_valid_after(expiry)
          .add_extension(x509.BasicConstraints(ca=True, path_length=0), critical=True)
          .add_extension(x509.KeyUsage(digital_signature=True, content_commitment=False, key_encipherment=False,
                                      data_encipherment=False, key_agreement=False, key_cert_sign=True,
                                      crl_sign=True, encipher_only=None, decipher_only=None), critical=True)
          .sign(ca_key, hashes.SHA256()))
    client = (x509.CertificateBuilder().subject_name(client_name).issuer_name(ca_name)
              .public_key(client_key.public_key()).serial_number(x509.random_serial_number())
              .not_valid_before(now - timedelta(minutes=5)).not_valid_after(expiry)
              .add_extension(x509.BasicConstraints(ca=False, path_length=None), critical=True)
              .add_extension(x509.KeyUsage(digital_signature=True, content_commitment=False, key_encipherment=True,
                                          data_encipherment=False, key_agreement=False, key_cert_sign=False,
                                          crl_sign=False, encipher_only=None, decipher_only=None), critical=True)
              .add_extension(x509.ExtendedKeyUsage([ExtendedKeyUsageOID.CLIENT_AUTH]), critical=False)
              .sign(ca_key, hashes.SHA256()))
    client.verify_directly_issued_by(ca)
    ca_bytes = ca.public_bytes(serialization.Encoding.PEM)
    client_bytes = pkcs12.serialize_key_and_certificates(identity.encode("ascii"), client_key, client, [ca],
                                                       serialization.BestAvailableEncryption(password.encode("ascii")))
    loaded_key, loaded_client, loaded_ca = pkcs12.load_key_and_certificates(client_bytes, password.encode("ascii"))
    if loaded_key is None or loaded_client is None or len(loaded_ca) != 1:
        raise RuntimeError("OwnPKCS12 must contain a private key, client certificate and one CA")
    if loaded_key.public_key().public_numbers() != client_key.public_key().public_numbers():
        raise RuntimeError("OwnPKCS12 private key mismatch")
    if loaded_client.fingerprint(hashes.SHA256()) != client.fingerprint(hashes.SHA256()):
        raise RuntimeError("OwnPKCS12 client certificate mismatch")
    if loaded_ca[0].fingerprint(hashes.SHA256()) != ca.fingerprint(hashes.SHA256()):
        raise RuntimeError("OwnPKCS12 CA mismatch")
    folder = base / ("OwnTLS85-" + identity)
    folder.mkdir(mode=0o700, exist_ok=False)
    manifest = dict(lesson="OwnTLS85-file-source", identity=identity,
                    ca_sha256=hashlib.sha256(ca_bytes).hexdigest(), client_sha256=hashlib.sha256(client_bytes).hexdigest(),
                    client_private_key_included=True, ca_certificate_only=True, imported_into_os=False,
                    expires_utc=expiry.isoformat())
    for filename, payload in (("ca.pem", ca_bytes), ("client.p12", client_bytes),
                              ("manifest.json", json.dumps(manifest, ensure_ascii=True, indent=2).encode("utf-8"))):
        with (folder / filename).open("xb") as stream:
            stream.write(payload)
    return identity, folder, expiry


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("--base", required=True)
    arguments = parser.parse_args()
    if not sys.stdin.isatty():
        raise RuntimeError("Run interactively in your own VM; do not pipe or log a private password")
    password = getpass.getpass("New training-only PKCS12 password: ")
    confirmation = getpass.getpass("Repeat password: ")
    if password != confirmation:
        raise ValueError("Password confirmation differs")
    identity, folder, expiry = build_own_fixture(arguments.base, password)
    password = confirmation = ""
    print("Own UUID:", identity)
    print("Own folder:", folder)
    print("Expires UTC:", expiry.isoformat())
    print("No OS import or network connection. CA private key was never written to disk.")


if __name__ == "__main__":
    main()

Разбор по шагам

  1. Команда сначала копирует пароль в локальную клиентскую переменную и очищает поле. ПроверитьСеанс85 — НаСервереБезКонтекста: пароль и пути не передаются в серверную процедуру. Отдельно проверяются версия толстого клиента и Windows-платформа; этот полный синхронный вариант не обещается для тонкого/веб-клиента, поскольку ХешированиеДанных.Добавить здесь доступен в толстом клиенте.

  2. UUID разбирается настоящим конструктором УникальныйИдентификатор и сверяется с каноническим текстом Python. Произвольный путь, путь с .., URL, другой каталог или другой файл нельзя ввести в команду: все три имени строятся из фактического клиентского временного каталога, собственного UUID и фиксированных имен ca.pem/client.p12/manifest.json.

  3. Проверяем существование, ЭтоФайл и размеры до чтения:16КиБ для манифеста,64КиБ для CA,256КиБ для PKCS12. JSON читается через настоящий ЧтениеJSON, закрывается и при успехе, и при ошибке. Маркер/UUID/flags подтверждают ожидаемый формат собственного опыта, не криптографическое авторство чужого манифеста.

  4. SHA256 считается по реальным ДвоичныеДанные из файла, не по строке имени. Отдельный объект ХешированиеДанных получает алгоритм SHA256 и Добавить(Данные); его ХешСумма преобразуется в hex и нижний регистр. Два результата должны совпасть с собственным манифестом. Проверка обнаруживает случайное изменение файлов, но не защищает от человека, способного изменить и манифест, и файлы.

  5. СертификатыУдостоверяющихЦентровФайл получает полный путь ca.pem без пароля, потому что файл публичный. СертификатКлиентаФайл получает полный путь client.p12 и именно его пароль. Это принципиально разные источники: сертификат без закрытого ключа не заменяет клиентский PKCS12. ИмяФайла у обоих readonly и проверяется чтением, не присваиванием.

  6. ЗащищенноеСоединениеOpenSSL получает реальный КлиентФайл первым аргументом и реальные ЦентрыФайл вторым. Проверяются типы обоих источников и имена файлов уже через readonly-паспорт TLS. CA не пропускается даже для учебного клиента. Нет HTTPСоединение, запроса, адреса сервера или обмена.

  7. Пароль очищается из локальной переменной после создания источника и в общем обработчике исключения. Сообщения не содержат пароля, ключа, полных бинарных данных или ОписаниеОшибки. Очистка строк не гарантирует стирания всех копий из памяти1С/Python, внутреннего объекта и отладчика: после своего опыта завершите учебные процессы. Исключение оставляет неподтвержденный статус, успешный результат не подставляется.

Как проверить результат

  1. Положительный опыт выполняете вручную в своей VM: скрипт выдает свежий UUID и три собственных файла, затем форма с тем же UUID/паролем подтверждает файловые паспорта. В отладчике оба свойства ИмяФайла указывают именно на свой UUID-каталог. Сетевого трафика, импорта доверия и входа по сертификату не должно быть.

  2. Отрицательный опыт в своей VM: измените один байт копии собственного ca.pem, не меняя манифест. Должно отказать на SHA256 до конструкторов сертификатов. Для следующего полноценного опыта создайте новый UUID-комплект вместо восстановления/перезаписи старого.

  3. Введите другой UUID, неверный пароль, пустое поле, либо удалите только свой client.p12: результат не подтверждается и поле пароля очищается. Конструктор может читать источник лениво; совпадение имени и создание профиля сами по себе не доказывают, что пароль принят во время будущего handshake.

  4. Для проверки контекста откройте свою обработку тонким клиентом: учебная команда должна отказать до синхронного хеширования. Изменение имени конфигурации или появление расширения также должно остановить опыт. Не снимайте проверки ради рабочего компьютера.

Ожидаемый результат

Свой RSA-клиентский ключ и CA подготовлены отдельным скриптом, три файла связаны UUID/контрольными суммами, в1С собран и проверен файловый TLS-паспорт. Сеть, доверие удаленному серверу, отзыв, валидность цепочки во время соединения и mTLS-авторизация не подтверждаются этим офлайн-этапом.

На что обратить внимание

  1. Публичный сертификат без private key не годится для СертификатКлиентаФайл. Установленная8.5 выбирает PKCS12 по .PFX/.P12, PEM по .PEM, прочее по умолчанию PEM. Внутренний PXF КриптоПро не поддерживается. Не переименовывайте формат ради обхода ошибки.

  2. Скрипт использует BestAvailableEncryption и не понижает защиту до устаревших3DES/SHA1 ради совместимости. Поддержку полученного PKCS12 именно установленным OpenSSL1С надо подтвердить вручную. При несовместимости прекращайте опыт, не считайте паспорт доказательством чтения ключа. Пароль PKCS12 по официальной документации не является надежной границей безопасности; защита VM/каталога обязательна.

  3. Семидневная действительность, CLOCK/UTC, цепочка и отзыв не проверяются чтением ИмяФайла или manifest.expires_utc. Время запуска скрипта и1С должно быть корректным. CA-ключ намеренно не сохранен: этот комплект нельзя использовать для выпуска настоящих серверных/пользовательских сертификатов позже.

  4. Проверки размера/SHA и конструкторы читают файл отдельно: это не атомарная защита от подмены между чтениями. В отдельной VM не должно быть других процессов, меняющих собственный каталог. UUID/маркер — ограничение учебного workflow, не подтверждение владельца против злоумышленника.

  5. Ничего здесь не генерировалось и в1С не запускалось. Python-блок только скомпилирован/статически проверен; реальные ключи, файлы, хранилища и handshake не создавались и не выполнялись нами. После ручной подготовки комплект содержит секрет: не отправляйте client.p12, пароль и память отладчика в чат/репозиторий.

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

Не хотите разбираться сами?

Адаптируем это решение под вашу базу. Оценка бесплатно.

Оставить задачу

1С, 1С:Предприятие — товарные знаки ООО «1С». Сайт не является официальным представительством фирмы «1С» и не аффилирован с ней.