КонтейнерПодписейКриптографии.Подписи

Свойства · Signatures

Тип: Массив. Массив элементов типа ПодписьКриптографии.

Полное описание, параметры и ограничения

КонтейнерПодписейКриптографии.Подписи (CryptoSignaturesContainer.Signatures)

КонтейнерПодписейКриптографии (CryptoSignaturesContainer)

Подписи (Signatures)

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

Использование:
Только чтение.
Описание:
Тип: Массив.
Массив элементов типа ПодписьКриптографии.
Доступность:

Тонкий клиент, сервер, толстый клиент, внешнее соединение.

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

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

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

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

Через Ждать: подписать свою карточку CMS, разобрать подписанта и расшифровать AES для себя

Один полный опыт связывает собственный legacyCSP-ключ текущего пользователя, точный DER-сертификат, два вида CMS и проверку неизменности байтов. Не выбирает случайный персональный сертификат и не считает чтение контейнера проверкой подписи.

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

Полный модуль основной управляемой формы УчебныйКриптоЦикл85 в собственной пустой базе; толстый управляемый Windows-клиент8.5.1.1343. PowerShell — только отдельный ручной подготовительный блок.

Подготовка

  1. Создайте отдельную собственную WindowsVM и отдельного только учебного Windows-пользователя. Новая пустая база8.5.1.1343: имя конфигурации УчебныйКриптоЦикл85, русский встроенный язык, Такси, совместимость Не использовать. Не добавляйте предметные объекты, расширения или задания. Полный guard встроен в модуль. Рабочий пользователь/сервер/production исключены.

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

  3. Откройте обработку толстым управляемым Windows-клиентом8.5.1.1343 под тем же учебным пользователем, что запускает подготовку ключа. Разрешите режим синхронных вызовов: даже модуль с Ждать содержит синхронную подготовку и публичный DER-разбор. Веб/мобильный/тонкий/серверный положительный путь не заявляется.

  4. Сохраните отдельный нижний PowerShell-блок как own_crypto85_fixture.ps1 на своей VM. В Windows PowerShell5.1 под учебным пользователем выполните .\own_crypto85_fixture.ps1 -BaseDirectory "точный клиентский каталог из КаталогДляКомплекта85". Скрипт создаст новый UUID, собственный self-signedRSA2048 сертификат в CurrentUser My и legacyCSP-контейнер с KeySpec KeyExchange/тип24; экспортирует только публичный DER в новый OwnCrypto85-UUID. Не запускайте скрипт на основном компьютере/пользователе.

  5. Этот этап действительно меняет хранилище только при ВАШЕМ ручном запуске: ключ NonExportable, KeyProtection None, хранится в текущем учебном пользователе, корневое доверие не добавляется. Если CSP/legacyRSA/private-key/provider/type/имя контейнера не совпали, скрипт останавливается без автоматического удаления; проверьте собственную VM. Не заменяйте CSP на CNG/KSP: контракт менеджера1С для этого урока выбран именно по имени провайдера24.

  6. Впишите напечатанный UUID, вручную отметьте ПодтверждаюУчебнуюVM85 и нажмите команду. Имя файла строится из реального клиентского temp и UUID, другие пути в пульт не вводятся. Публичный DER и найденный по двоичному отпечатку сертификат должны точно совпасть. Наличие публичного DER недостаточно: ключ должен оставаться в CurrentUser My того же Windows-пользователя.

  7. Выбор SHA256/RSA/AES256 допускается только если фактические массивы возможностей вашего провайдера содержат эти точные имена. Если написание отличается или алгоритм отсутствует, урок отказывает, не заменяет алгоритм на SHA1/3DES. Сначала изучите свой отчёт ИнформацияМодуляКриптографии; не подставляйте случайные имена/OID в код.

  8. После опыта завершите1С и утилизируйте собственную учебную VM/учебного пользователя: личный сертификат и ключ не удаляются автоматически. Не удаляйте системные контейнеры по префиксу и не очищайте Личное целиком. Пароль/PIN/PFX в1С не передаются; свой учебный программный CSP-ключ без интерактивной защиты используется только внутри отдельной VM.

&НаКлиенте
Перем Занято85;

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

&НаКлиенте
Процедура ПриОткрытии(Отказ)
    Занято85 = Ложь;
    КаталогДляКомплекта85 = КаталогВременныхФайлов();
КонецПроцедуры

&НаКлиенте
Функция ПодготовитьСвойОпыт85()
    ПроверитьСеанс85();
    #Если ТолстыйКлиентУправляемоеПриложение Тогда
        Система = Новый СистемнаяИнформация;
        Если (Система.ТипПлатформы <> ТипПлатформы.Windows_x86
            И Система.ТипПлатформы <> ТипПлатформы.Windows_x86_64) Или Не ПодтверждаюУчебнуюVM85 Тогда
            ВызватьИсключение "Нужна собственная изолированная Windows VM и толстый управляемый клиент8.5.1.1343";
        КонецЕсли;
        СвойUUID = Новый УникальныйИдентификатор(UUIDКомплекта85);
        ТекстUUID = НРег(Строка(СвойUUID));
        Если UUIDКомплекта85 <> ТекстUUID Тогда
            ВызватьИсключение "Нужен точный UUID своей PowerShell-подготовки";
        КонецЕсли;
        БазаФайлов = КаталогВременныхФайлов();
        Если Прав(БазаФайлов, 1) <> "\" Тогда
            БазаФайлов = БазаФайлов + "\";
        КонецЕсли;
        ИмяDER = БазаФайлов + "OwnCrypto85-" + ТекстUUID + "\own-public.cer";
        СвойФайл = Новый Файл(ИмяDER);
        Если Не СвойФайл.Существует() Или Не СвойФайл.ЭтоФайл() Тогда
            ВызватьИсключение "Нет своего публичного DER-файла";
        КонецЕсли;
        Если СвойФайл.Размер() <= 0 Или СвойФайл.Размер() > 65536 Тогда
            ВызватьИсключение "Свой DER пуст или слишком велик";
        КонецЕсли;
        Публичный = Новый СертификатКриптографии(Новый ДвоичныеДанные(ИмяDER));
        Менеджер = Новый МенеджерКриптографии("Microsoft Enhanced RSA and AES Cryptographic Provider", "", 24,
            ИспользованиеИнтерактивногоРежимаКриптографии.НеИспользовать);
        Информация = Менеджер.ПолучитьИнформациюМодуляКриптографии();
        Если ТипЗнч(Информация) <> Тип("ИнформацияМодуляКриптографии") Тогда
            ВызватьИсключение "Свой провайдер не вернул возможности";
        КонецЕсли;
        Если Информация.АлгоритмыХеширования.Найти("SHA256") = Неопределено
            Или Информация.АлгоритмыПодписи.Найти("RSA") = Неопределено
            Или Информация.АлгоритмыШифрования.Найти("AES256") = Неопределено Тогда
            ВызватьИсключение "Нужны подтвержденные названия SHA256/RSA/AES256 этого провайдера. Не подставляйте неизвестные имена";
        КонецЕсли;
        Менеджер.АлгоритмХеширования = "SHA256";
        Менеджер.АлгоритмПодписи = "RSA";
        Менеджер.АлгоритмШифрования = "AES256";
        Менеджер.ВключениеСертификатовВПодпись = РежимВключенияСертификатовКриптографии.ВключатьСертификатСубъекта;
        Менеджер.ПарольДоступаКЗакрытомуКлючу = "";
        Хранилище = Менеджер.ПолучитьХранилищеСертификатов(ТипХранилищаСертификатовКриптографии.ПерсональныеСертификаты,
            РасположениеХранилищаСертификатовКриптографии.ДанныеПользователяОС);
        Кандидат = Хранилище.НайтиПоОтпечатку(Публичный.Отпечаток);
        Если ТипЗнч(Кандидат) <> Тип("СертификатКриптографии") Тогда
            ВызватьИсключение "Нет своего сертификата с привязкой к ключу в Личном текущего пользователя";
        КонецЕсли;
        ПроверитьСвойСертификат85(Кандидат, Публичный);
        ТекстДанных = "OwnCrypto85:" + ТекстUUID + "|Книга|Количество=3|Цена=250";
        Данные = ПолучитьДвоичныеДанныеИзСтроки(ТекстДанных, КодировкаТекста.UTF8);
        Возврат Новый Структура("Менеджер,Сертификат,Данные,UUID", Менеджер, Кандидат, Данные, ТекстUUID);
    #Иначе
        ВызватьИсключение "Полный урок поддерживает только толстый управляемый клиент Windows, не сервер, тонкий, веб или мобильный";
    #КонецЕсли
КонецФункции

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

&НаКлиенте
Процедура ПроверитьСвойКонтейнер85(Контейнер, Сертификат, Данные, Присоединенная)
    Если ТипЗнч(Контейнер) <> Тип("КонтейнерПодписейКриптографии") Тогда
        ВызватьИсключение "Парсер не вернул контейнер подписей";
    КонецЕсли;
    Подписи = Контейнер.Подписи;
    Если ТипЗнч(Подписи) <> Тип("Массив") Или Подписи.Количество() <> 1 Тогда
        ВызватьИсключение "У своего CMS должен быть ровно один подписант";
    КонецЕсли;
    Подпись = Подписи[0];
    Если ТипЗнч(Подпись) <> Тип("ПодписьКриптографии") Или Подпись.Индекс <> 0 Тогда
        ВызватьИсключение "Не подтвержден первый собственный подписант";
    КонецЕсли;
    ПроверитьСвойСертификат85(Подпись.СертификатПодписи, Сертификат);
    Если Подпись.ТипПодписи <> ТипПодписиКриптографии.CMS Или ПустаяСтрока(Подпись.АлгоритмПодписи)
        Или ПустаяСтрока(Подпись.АлгоритмХеширования) Тогда
        ВызватьИсключение "Нет типа CMS или OID алгоритмов в своем контейнере";
    КонецЕсли;
    Если Присоединенная Тогда
        ПроверитьСвоиБайты85(Подпись.Данные, Данные);
    ИначеЕсли Подпись.Данные <> Неопределено Тогда
        ВызватьИсключение "Отсоединенная подпись неожиданно содержит payload";
    КонецЕсли;
    АрхивныеМетки = Подпись.АрхивныеМеткиВремени;
    Если АрхивныеМетки <> Неопределено Тогда
        Если ТипЗнч(АрхивныеМетки) <> Тип("Массив") Или АрхивныеМетки.Количество() <> 0 Тогда
            ВызватьИсключение "В своем обычном CMS не ожидаются архивные метки";
        КонецЕсли;
    КонецЕсли;
    Если Подпись.МеткаВремениПодписи <> Неопределено Или Подпись.МеткаВремениДанныхПроверкиПодписи <> Неопределено Тогда
        ВызватьИсключение "Обычный CMS не должен подменяться доверенной меткой времени";
    КонецЕсли;
    Если ТипЗнч(Подпись.НеподтвержденноеВремяПодписи) <> Тип("Дата") Тогда
        ВызватьИсключение "Неподтвержденное UTC-время не является датой";
    КонецЕсли;
    Цепочка = Подпись.СертификатыПроверкиПодписи;
    Если ТипЗнч(Цепочка) <> Тип("Массив") Тогда
        ВызватьИсключение "Ожидался массив сертификатов из своего CMS";
    КонецЕсли;
    Для Каждого Участник Из Цепочка Цикл
        ПроверитьСвойСертификат85(Участник, Сертификат);
    КонецЦикла;
    Сообщить("Разобран один собственный CMS. OID: " + Подпись.АлгоритмПодписи + "; " + Подпись.АлгоритмХеширования
        + ". UTC-время неподтвержденное, доверие не проверено");
КонецПроцедуры

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

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

&НаСервереБезКонтекста
Процедура ПроверитьСвойПустойСтенд()
    Если Метаданные.Справочники.Количество() <> 0 Или Метаданные.Документы.Количество() <> 0 Или Метаданные.Перечисления.Количество() <> 0 Или Метаданные.РегистрыСведений.Количество() <> 0 Или Метаданные.РегистрыНакопления.Количество() <> 0 Или Метаданные.РегистрыБухгалтерии.Количество() <> 0 Или Метаданные.РегистрыРасчета.Количество() <> 0 Или Метаданные.ПланыВидовХарактеристик.Количество() <> 0 Или Метаданные.ПланыВидовРасчета.Количество() <> 0 Или Метаданные.ПланыСчетов.Количество() <> 0 Или Метаданные.ПланыОбмена.Количество() <> 0 Или Метаданные.Последовательности.Количество() <> 0 Или Метаданные.БизнесПроцессы.Количество() <> 0 Или Метаданные.Задачи.Количество() <> 0 Или Метаданные.ОбщиеРеквизиты.Количество() <> 0 Или Метаданные.ПодпискиНаСобытия.Количество() <> 0 Или Метаданные.РегламентныеЗадания.Количество() <> 0 Или Метаданные.ВнешниеИсточникиДанных.Количество() <> 0 Тогда
        ВызватьИсключение "Нужен только свой новый пустой учебный стенд";
    КонецЕсли;
    Если Метаданные.ЕстьИзмененияРасширениямиКонфигурации() Тогда
        ВызватьИсключение "Свой сеанс не должен изменяться расширениями";
    КонецЕсли;
    Источники = Новый Массив;
    Источники.Добавить(ИсточникРасширенийКонфигурации.БазаДанных);
    Источники.Добавить(ИсточникРасширенийКонфигурации.СеансАктивные);
    Источники.Добавить(ИсточникРасширенийКонфигурации.СеансОтключенные);
    Для Каждого Источник Из Источники Цикл
        Расширения = РасширенияКонфигурации.Получить(, Источник);
        Если ТипЗнч(Расширения) <> Тип("Массив") Тогда
            ВызватьИсключение "Native список расширений должен иметь тип Массив";
        КонецЕсли;
        Если Расширения.Количество() <> 0 Тогда
            ВызватьИсключение "В своём новом стенде нет расширений базы или сеанса";
        КонецЕсли;
    КонецЦикла;
    Если ТранзакцияАктивна() Тогда
        ВызватьИсключение "Чужая или вложенная активная транзакция не допускается";
    КонецЕсли;
КонецПроцедуры
&НаКлиенте
Асинх Процедура ВыполнитьКриптоЦикл85(Команда)
    Если Занято85 Тогда
        Сообщить("Предыдущий опыт еще не завершен");
        Возврат;
    КонецЕсли;
    Занято85 = Истина;
    Статус85 = "Результат не подтвержден";
    Менеджер = Неопределено;
    Попытка
        Опыт = ПодготовитьСвойОпыт85();
        Менеджер = Опыт.Менеджер;
        ИмяКонтейнера = Ждать Менеджер.НайтиКлючевойКонтейнерАсинх(Опыт.Сертификат, Ложь);
        Если ТипЗнч(ИмяКонтейнера) <> Тип("Строка") Или ПустаяСтрока(ИмяКонтейнера) Тогда
            ВызватьИсключение "Свой сертификат не связан с доступным ключевым контейнером";
        КонецЕсли;
        Менеджер.ВключениеДанныхВПодпись = Ложь;
        Отсоединенная = Ждать Менеджер.ПодписатьАсинх(Опыт.Данные, Опыт.Сертификат, ТипПодписиКриптографии.CMS);
        Если ТипЗнч(Отсоединенная) <> Тип("ДвоичныеДанные") Тогда
            ВызватьИсключение "Подпись в память не вернула байты";
        КонецЕсли;
        ИтогПроверки = Ждать Менеджер.ПроверитьПодписьАсинх(Опыт.Данные, Отсоединенная, Ложь);
        Если ИтогПроверки <> Неопределено Тогда
            ВызватьИсключение "Проверка не должна возвращать Булево или сертификат";
        КонецЕсли;
        Сертификаты = Ждать Менеджер.ПолучитьСертификатыИзПодписиАсинх(Отсоединенная);
        Если ТипЗнч(Сертификаты) <> Тип("Массив") Или Сертификаты.Количество() <> 1 Тогда
            ВызватьИсключение "Из своего CMS не извлечен единственный свой сертификат";
        КонецЕсли;
        ПроверитьСвойСертификат85(Сертификаты[0], Опыт.Сертификат);
        КонтейнерОтсоединенной = Ждать Менеджер.ПолучитьКонтейнерПодписейКриптографииАсинх(Отсоединенная);
        ПроверитьСвойКонтейнер85(КонтейнерОтсоединенной, Опыт.Сертификат, Опыт.Данные, Ложь);
        Измененные = ПолучитьДвоичныеДанныеИзСтроки("OwnCrypto85:" + Опыт.UUID + "|Книга|Количество=4|Цена=250", КодировкаТекста.UTF8);
        ИзменениеОтказано = Ложь;
        Попытка
            ИтогНевернойПроверки = Ждать Менеджер.ПроверитьПодписьАсинх(Измененные, Отсоединенная, Ложь);
            Если ИтогНевернойПроверки <> Неопределено Тогда
                ВызватьИсключение "Неожиданный результат";
            КонецЕсли;
        Исключение
            ИзменениеОтказано = Истина;
        КонецПопытки;
        Если Не ИзменениеОтказано Тогда
            ВызватьИсключение "Измененная карточка ошибочно прошла проверку";
        КонецЕсли;
        Менеджер.ВключениеДанныхВПодпись = Истина;
        Присоединенная = Ждать Менеджер.ПодписатьАсинх(Опыт.Данные, Опыт.Сертификат, ТипПодписиКриптографии.CMS);
        ИтогПрисоединенной = Ждать Менеджер.ПроверитьПодписьАсинх(Присоединенная, Ложь);
        Если ИтогПрисоединенной <> Неопределено Тогда
            ВызватьИсключение "Присоединенная проверка не возвращает Булево";
        КонецЕсли;
        Извлеченные = Ждать Менеджер.ПолучитьДанныеИзПодписиАсинх(Присоединенная);
        ПроверитьСвоиБайты85(Извлеченные, Опыт.Данные);
        КонтейнерПрисоединенной = Ждать Менеджер.ПолучитьКонтейнерПодписейКриптографииАсинх(Присоединенная);
        ПроверитьСвойКонтейнер85(КонтейнерПрисоединенной, Опыт.Сертификат, Опыт.Данные, Истина);
        Получатели = Новый Массив;
        Получатели.Добавить(Опыт.Сертификат);
        Зашифрованные = Ждать Менеджер.ЗашифроватьАсинх(Опыт.Данные, Получатели);
        Если ТипЗнч(Зашифрованные) <> Тип("ДвоичныеДанные") Тогда
            ВызватьИсключение "Шифрование в память не вернуло байты";
        КонецЕсли;
        Расшифрованные = Ждать Менеджер.РасшифроватьАсинх(Зашифрованные, Опыт.Сертификат, "");
        ПроверитьСвоиБайты85(Расшифрованные, Опыт.Данные);
        Статус85 = "Своя карточка: CMS-подписи проверены, измененный payload отказан, AES-расшифровка совпала. Доверие сертификату и TSA не проверены";
        Сообщить(Статус85);
    Исключение
        Сообщить("Криптоцикл не подтвержден. Проверьте собственный UUID, провайдер, поддерживаемые алгоритмы и ключ. Чужие ключи не ищутся; секреты не выводятся");
    КонецПопытки;
    Если Менеджер <> Неопределено Тогда
        Менеджер.ПарольДоступаКЗакрытомуКлючу = "";
    КонецЕсли;
    Занято85 = Ложь;
КонецПроцедуры
PowerShell: own_crypto85_fixture.ps1

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

Ручная подготовка: создает новый собственный CSP-ключ и сертификат CurrentUser My, сохраняет только публичный DER. Здесь не выполнялся; хранилища не менялись.

param([Parameter(Mandatory=$true)][string]$BaseDirectory)
$ErrorActionPreference = 'Stop'
if ([Environment]::OSVersion.Platform -ne [PlatformID]::Win32NT) {throw 'Use only your isolated Windows VM'}
$taskBase = (Resolve-Path -LiteralPath $BaseDirectory).Path
if (-not (Test-Path -LiteralPath $taskBase -PathType Container)) {throw 'Copy the real client temporary directory from your own1C form'}
$taskIdentity = [guid]::NewGuid().ToString()
$taskFolder = Join-Path $taskBase ('OwnCrypto85-' + $taskIdentity)
if (Test-Path -LiteralPath $taskFolder) {throw 'Own UUID directory already exists'}
$taskSubject = 'CN=OwnCrypto85-' + $taskIdentity
$taskParameters = @{
    Type = 'Custom'
    Subject = $taskSubject
    FriendlyName = 'OwnCrypto85-' + $taskIdentity
    Provider = 'Microsoft Enhanced RSA and AES Cryptographic Provider'
    Container = 'OwnCrypto85-' + $taskIdentity
    KeyAlgorithm = 'RSA'
    KeyLength = 2048
    KeySpec = 'KeyExchange'
    KeyUsage = @('DigitalSignature','KeyEncipherment')
    KeyExportPolicy = 'NonExportable'
    KeyProtection = 'None'
    HashAlgorithm = 'SHA256'
    NotAfter = (Get-Date).AddDays(7)
    CertStoreLocation = 'Cert:\CurrentUser\My'
}
$taskCertificate = New-SelfSignedCertificate @taskParameters
if ($taskCertificate.Subject -ne $taskSubject -or -not $taskCertificate.HasPrivateKey) {throw 'Own certificate identity/private key not confirmed; inspect your VM manually'}
$taskKey = $taskCertificate.PrivateKey
if ($taskKey -isnot [System.Security.Cryptography.RSACryptoServiceProvider]) {throw 'Expected own legacy RSA CSP key, not CNG'}
try {
    $taskKeyInfo = $taskKey.CspKeyContainerInfo
    if ($taskKeyInfo.ProviderName -ne $taskParameters.Provider -or $taskKeyInfo.ProviderType -ne 24 -or $taskKeyInfo.MachineKeyStore) {throw 'Wrong provider, type or account store'}
    if ($taskKeyInfo.KeyContainerName -ne $taskParameters.Container) {throw 'Wrong own key container'}
    New-Item -ItemType Directory -Path $taskFolder | Out-Null
    $taskDer = $taskCertificate.Export([System.Security.Cryptography.X509Certificates.X509ContentType]::Cert)
    $taskFile = Join-Path $taskFolder 'own-public.cer'
    $taskStream = [System.IO.File]::Open($taskFile,[System.IO.FileMode]::CreateNew,[System.IO.FileAccess]::Write,[System.IO.FileShare]::None)
    try { $taskStream.Write($taskDer,0,$taskDer.Length) } finally { $taskStream.Dispose() }
    Write-Output ('Own UUID: ' + $taskIdentity)
    Write-Output ('Own public DER: ' + $taskFile)
    Write-Output ('Own subject: ' + $taskCertificate.Subject)
    Write-Output ('Own thumbprint: ' + $taskCertificate.Thumbprint)
    Write-Output 'A new private key remains in CurrentUser My. No root trust, network, export of private key or automatic cleanup.'
} finally {
    $taskKey.Dispose()
    $taskCertificate.Dispose()
}

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

  1. Занято85 предотвращает параллельную команду; серверная процедура без контекста снова подтверждает пустую собственную конфигурацию. Клиент проверяетWindows/версию/явное подтверждение отдельной VM. Подготовка создает реальный менеджер и получает конкретное Личное текущего пользователя, не читает/не печатает все чужие сертификаты.

  2. НайтиПоОтпечатку получает отпечаток нашего публичного DER, затем DER найденного объекта сравнивается побайтно через Base64. НайтиКлючевойКонтейнер получает Ложь: используется привязка именно найденного сертификата, расширенный перебор чужих ключей по открытым ключам не включается. Непустое имя контейнера — предварительное условие, не доказательство успешной подписи.

  3. Исходная UTF8-карточка включает собственный UUID, Книгу, количество3 и цену250. ПолучитьДвоичныеДанныеИзСтроки превращает текст в настоящие байты; Строка как аргумент криптометода означала бы имя файла. Сертификат всегда передается явно: нет fallback на единственный или первый личный сертификат.

  4. ВключениеДанныхВПодпись=Ложь дает отсоединенный CMS. ВключатьСертификатСубъекта вкладывает только наш сертификат, без попытки построения доверенной цепочки. Подписать/его обещание возвращает реальные ДвоичныеДанные; ПроверитьПодпись с ПроверятьДополнительныеАтрибуты=Ложь проверяет только криптографическую подпись, не доверие/отзыв/TSA.

  5. У ПроверитьПодписьАсинх нет выходного параметра Сертификат: ее варианты отличаются от sync. Ждать возвращает Неопределено при успехе, ошибка приходит исключением; сертификаты извлекаются отдельным методом. Каждый обещающий вызов дожидается результата.

  6. ПолучитьСертификатыИзПодписи возвращает массив с одним собственным сертификатом. ПолучитьКонтейнерПодписейКриптографии возвращает реальный контейнер и Массив Подписи. Все11 readonly-свойств одного ПодписьКриптографии разбираются: собственный сертификат/индекс0/CMS/OID, отсутствие payload у detached, отсутствие TSA/архивных меток, UTC-дата как неподтвержденная и только собственные элементы сертификатного массива.

  7. Отрицательный тест меняет количество3 на4 при том же UUID и проверяет прежнюю подпись на новых байтах. Успешная предварительная проверка оригинала обязательна, затем ожидается исключение. Исключение может быть и другой ошибкой провайдера — модуль не называет любой отказ юридическим доказательством подделки. Отсутствие исключения означает провал учебной проверки.

  8. ВключениеДанныхВПодпись=Истина создает вторую, присоединенную CMS на тех же своих байтах. Она проверяется своей перегрузкой, ПолучитьДанныеИзПодписи извлекает payload и проверяется точное равенство. У readonly Данные подписанта attached-контейнера также должны быть исходные байты, а не Неопределено.

  9. Зашифровать получает Массив только нашего получателя; CMS-шифротекст возвращается в память. Расшифровать получает тот же явный сертификат и пустой пароль собственного CSP-контейнера без PIN. Результат сравнивается с исходными байтами, не просто считается успешным по отсутствию исключения. Сетей/TSP/OCSP/файловой записи из1С нет.

  10. ПарольДоступаКЗакрытомуКлючу явно сбрасывается в собственном менеджере до опыта и после успеха/ошибки. Это настройка конкретного объекта, не доступ к чужому ключу и не механизм защиты памяти. Статус успеха устанавливается только после всех проверок. При ошибке печатается только безопасное общее сообщение; ключ/содержимое/ОписаниеОшибки не выводятся.

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

  1. Положительный опыт вручную в своей VM: оба CMS разобраны как один подписант; attached-payload совпал, изменение количества отказано, расшифрованные AES-байты совпали. Это ожидаемый протокол, не уже полученный runtime-результат. Для другого режима используйте отдельную форму с полным соответствующим модулем и тот же собственный комплект.

  2. Введите UUID другого опыта без его файлов, снимите подтверждение VM или измените имя пустой конфигурации: команда отказывает до подписи. Запуск под другимWindows-пользователем без собственного ключа должен завершиться неподтвержденным результатом; чужой ключ не подбирается.

  3. При неподдерживаемом алгоритме или недоступном ключе нет итогового успеха. Наличие публичного сертификата, непустое имя контейнера и отсутствие ошибок парсинга не заменяют успешную проверку оригинальных байтов и точное сравнение расшифровки.

  4. ПодписьКриптографии.Данные: detached=Неопределено, attached=собственная карточка. АрхивныеМеткиВремени по native могут быть Неопределено либо пустым массивом; не вызывайте Количество без проверки. UTC-дата НеподтвержденноеВремяПодписи не превращается в доверенную метку.

  5. Во время ожидания повторное нажатие должно отказать по Занято85, флаг снимается после результата/общей ошибки. Закрытие клиента не гарантирует финальный обработчик; на учебной VM проверьте сохраненный свой сертификат/ключ вручную, автоматической очистки не обещается.

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

Полный собственный криптоцикл: реальная CMS-подпись с явным своим ключом, проверка оригинала, отказ измененным байтам, разбор detached/attached и возврат исходного AES-payload. Доверие сертификату, отзыв, доверенное время и юридическая сила здесь не подтверждаются.

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

  1. Сертификат из файла без ключа не может подписывать или расшифровывать. В этом уроке private key создает явная PowerShell-подготовка в отдельном CurrentUser My; файлом передается только публичный DER. До ручного запуска никакие ключи/сертификаты нами не создавались.

  2. Ложь в проверке дополнительных атрибутов — ограниченный учебный тест математической подписи, а не рекомендация для production. Не принимайте этот режим за проверку доверия, отзыва, полномочий подписанта или соответствие юридическим требованиям.

  3. Свой CMS намеренно без TSA/OCSP/архивных меток. НеподтвержденноеВремяПодписи — UTC-данные подписи, которым нельзя доверять как внешнему времени. Массив сертификатов из контейнера сам по себе не доказывает цепочку доверия.

  4. Оба вида CMS задаются именно ВключениеДанныхВПодпись. Не применяйте ПолучитьДанныеИзПодписи к detached с ожиданием исходного payload. У API sync/await/callback разные выходные параметры; не переносите позицию Сертификат из sync в await.

  5. На своей VM подготовка оставляет новый ключ и сертификат, даже если после их создания произошла ошибка экспорта DER. Не создавайте множество попыток без проверки собственного CurrentUser My. Изменения ОС не откатываются транзакцией базы1С; автоматического удаления здесь нет.

  6. Алгоритмы выбираются по фактическим именам возможностей менеджера. OID у разобранной подписи — другой формат представления; не приравнивайте имя RSA к точному OID и не подменяйте отсутствующий AES256 устаревшим шифром.

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

Синхронно: подписать свою карточку CMS, разобрать подписанта и расшифровать AES для себя

Один полный опыт связывает собственный legacyCSP-ключ текущего пользователя, точный DER-сертификат, два вида CMS и проверку неизменности байтов. Не выбирает случайный персональный сертификат и не считает чтение контейнера проверкой подписи.

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

Полный модуль основной управляемой формы УчебныйКриптоЦикл85 в собственной пустой базе; толстый управляемый Windows-клиент8.5.1.1343. PowerShell — только отдельный ручной подготовительный блок.

Подготовка

  1. Создайте отдельную собственную WindowsVM и отдельного только учебного Windows-пользователя. Новая пустая база8.5.1.1343: имя конфигурации УчебныйКриптоЦикл85, русский встроенный язык, Такси, совместимость Не использовать. Не добавляйте предметные объекты, расширения или задания. Полный guard встроен в модуль. Рабочий пользователь/сервер/production исключены.

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

  3. Откройте обработку толстым управляемым Windows-клиентом8.5.1.1343 под тем же учебным пользователем, что запускает подготовку ключа. Разрешите режим синхронных вызовов: даже модуль с Ждать содержит синхронную подготовку и публичный DER-разбор. Веб/мобильный/тонкий/серверный положительный путь не заявляется.

  4. Сохраните отдельный нижний PowerShell-блок как own_crypto85_fixture.ps1 на своей VM. В Windows PowerShell5.1 под учебным пользователем выполните .\own_crypto85_fixture.ps1 -BaseDirectory "точный клиентский каталог из КаталогДляКомплекта85". Скрипт создаст новый UUID, собственный self-signedRSA2048 сертификат в CurrentUser My и legacyCSP-контейнер с KeySpec KeyExchange/тип24; экспортирует только публичный DER в новый OwnCrypto85-UUID. Не запускайте скрипт на основном компьютере/пользователе.

  5. Этот этап действительно меняет хранилище только при ВАШЕМ ручном запуске: ключ NonExportable, KeyProtection None, хранится в текущем учебном пользователе, корневое доверие не добавляется. Если CSP/legacyRSA/private-key/provider/type/имя контейнера не совпали, скрипт останавливается без автоматического удаления; проверьте собственную VM. Не заменяйте CSP на CNG/KSP: контракт менеджера1С для этого урока выбран именно по имени провайдера24.

  6. Впишите напечатанный UUID, вручную отметьте ПодтверждаюУчебнуюVM85 и нажмите команду. Имя файла строится из реального клиентского temp и UUID, другие пути в пульт не вводятся. Публичный DER и найденный по двоичному отпечатку сертификат должны точно совпасть. Наличие публичного DER недостаточно: ключ должен оставаться в CurrentUser My того же Windows-пользователя.

  7. Выбор SHA256/RSA/AES256 допускается только если фактические массивы возможностей вашего провайдера содержат эти точные имена. Если написание отличается или алгоритм отсутствует, урок отказывает, не заменяет алгоритм на SHA1/3DES. Сначала изучите свой отчёт ИнформацияМодуляКриптографии; не подставляйте случайные имена/OID в код.

  8. После опыта завершите1С и утилизируйте собственную учебную VM/учебного пользователя: личный сертификат и ключ не удаляются автоматически. Не удаляйте системные контейнеры по префиксу и не очищайте Личное целиком. Пароль/PIN/PFX в1С не передаются; свой учебный программный CSP-ключ без интерактивной защиты используется только внутри отдельной VM.

&НаКлиенте
Перем Занято85;

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

&НаКлиенте
Процедура ПриОткрытии(Отказ)
    Занято85 = Ложь;
    КаталогДляКомплекта85 = КаталогВременныхФайлов();
КонецПроцедуры

&НаКлиенте
Функция ПодготовитьСвойОпыт85()
    ПроверитьСеанс85();
    #Если ТолстыйКлиентУправляемоеПриложение Тогда
        Система = Новый СистемнаяИнформация;
        Если (Система.ТипПлатформы <> ТипПлатформы.Windows_x86
            И Система.ТипПлатформы <> ТипПлатформы.Windows_x86_64) Или Не ПодтверждаюУчебнуюVM85 Тогда
            ВызватьИсключение "Нужна собственная изолированная Windows VM и толстый управляемый клиент8.5.1.1343";
        КонецЕсли;
        СвойUUID = Новый УникальныйИдентификатор(UUIDКомплекта85);
        ТекстUUID = НРег(Строка(СвойUUID));
        Если UUIDКомплекта85 <> ТекстUUID Тогда
            ВызватьИсключение "Нужен точный UUID своей PowerShell-подготовки";
        КонецЕсли;
        БазаФайлов = КаталогВременныхФайлов();
        Если Прав(БазаФайлов, 1) <> "\" Тогда
            БазаФайлов = БазаФайлов + "\";
        КонецЕсли;
        ИмяDER = БазаФайлов + "OwnCrypto85-" + ТекстUUID + "\own-public.cer";
        СвойФайл = Новый Файл(ИмяDER);
        Если Не СвойФайл.Существует() Или Не СвойФайл.ЭтоФайл() Тогда
            ВызватьИсключение "Нет своего публичного DER-файла";
        КонецЕсли;
        Если СвойФайл.Размер() <= 0 Или СвойФайл.Размер() > 65536 Тогда
            ВызватьИсключение "Свой DER пуст или слишком велик";
        КонецЕсли;
        Публичный = Новый СертификатКриптографии(Новый ДвоичныеДанные(ИмяDER));
        Менеджер = Новый МенеджерКриптографии("Microsoft Enhanced RSA and AES Cryptographic Provider", "", 24,
            ИспользованиеИнтерактивногоРежимаКриптографии.НеИспользовать);
        Информация = Менеджер.ПолучитьИнформациюМодуляКриптографии();
        Если ТипЗнч(Информация) <> Тип("ИнформацияМодуляКриптографии") Тогда
            ВызватьИсключение "Свой провайдер не вернул возможности";
        КонецЕсли;
        Если Информация.АлгоритмыХеширования.Найти("SHA256") = Неопределено
            Или Информация.АлгоритмыПодписи.Найти("RSA") = Неопределено
            Или Информация.АлгоритмыШифрования.Найти("AES256") = Неопределено Тогда
            ВызватьИсключение "Нужны подтвержденные названия SHA256/RSA/AES256 этого провайдера. Не подставляйте неизвестные имена";
        КонецЕсли;
        Менеджер.АлгоритмХеширования = "SHA256";
        Менеджер.АлгоритмПодписи = "RSA";
        Менеджер.АлгоритмШифрования = "AES256";
        Менеджер.ВключениеСертификатовВПодпись = РежимВключенияСертификатовКриптографии.ВключатьСертификатСубъекта;
        Менеджер.ПарольДоступаКЗакрытомуКлючу = "";
        Хранилище = Менеджер.ПолучитьХранилищеСертификатов(ТипХранилищаСертификатовКриптографии.ПерсональныеСертификаты,
            РасположениеХранилищаСертификатовКриптографии.ДанныеПользователяОС);
        Кандидат = Хранилище.НайтиПоОтпечатку(Публичный.Отпечаток);
        Если ТипЗнч(Кандидат) <> Тип("СертификатКриптографии") Тогда
            ВызватьИсключение "Нет своего сертификата с привязкой к ключу в Личном текущего пользователя";
        КонецЕсли;
        ПроверитьСвойСертификат85(Кандидат, Публичный);
        ТекстДанных = "OwnCrypto85:" + ТекстUUID + "|Книга|Количество=3|Цена=250";
        Данные = ПолучитьДвоичныеДанныеИзСтроки(ТекстДанных, КодировкаТекста.UTF8);
        Возврат Новый Структура("Менеджер,Сертификат,Данные,UUID", Менеджер, Кандидат, Данные, ТекстUUID);
    #Иначе
        ВызватьИсключение "Полный урок поддерживает только толстый управляемый клиент Windows, не сервер, тонкий, веб или мобильный";
    #КонецЕсли
КонецФункции

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

&НаКлиенте
Процедура ПроверитьСвойКонтейнер85(Контейнер, Сертификат, Данные, Присоединенная)
    Если ТипЗнч(Контейнер) <> Тип("КонтейнерПодписейКриптографии") Тогда
        ВызватьИсключение "Парсер не вернул контейнер подписей";
    КонецЕсли;
    Подписи = Контейнер.Подписи;
    Если ТипЗнч(Подписи) <> Тип("Массив") Или Подписи.Количество() <> 1 Тогда
        ВызватьИсключение "У своего CMS должен быть ровно один подписант";
    КонецЕсли;
    Подпись = Подписи[0];
    Если ТипЗнч(Подпись) <> Тип("ПодписьКриптографии") Или Подпись.Индекс <> 0 Тогда
        ВызватьИсключение "Не подтвержден первый собственный подписант";
    КонецЕсли;
    ПроверитьСвойСертификат85(Подпись.СертификатПодписи, Сертификат);
    Если Подпись.ТипПодписи <> ТипПодписиКриптографии.CMS Или ПустаяСтрока(Подпись.АлгоритмПодписи)
        Или ПустаяСтрока(Подпись.АлгоритмХеширования) Тогда
        ВызватьИсключение "Нет типа CMS или OID алгоритмов в своем контейнере";
    КонецЕсли;
    Если Присоединенная Тогда
        ПроверитьСвоиБайты85(Подпись.Данные, Данные);
    ИначеЕсли Подпись.Данные <> Неопределено Тогда
        ВызватьИсключение "Отсоединенная подпись неожиданно содержит payload";
    КонецЕсли;
    АрхивныеМетки = Подпись.АрхивныеМеткиВремени;
    Если АрхивныеМетки <> Неопределено Тогда
        Если ТипЗнч(АрхивныеМетки) <> Тип("Массив") Или АрхивныеМетки.Количество() <> 0 Тогда
            ВызватьИсключение "В своем обычном CMS не ожидаются архивные метки";
        КонецЕсли;
    КонецЕсли;
    Если Подпись.МеткаВремениПодписи <> Неопределено Или Подпись.МеткаВремениДанныхПроверкиПодписи <> Неопределено Тогда
        ВызватьИсключение "Обычный CMS не должен подменяться доверенной меткой времени";
    КонецЕсли;
    Если ТипЗнч(Подпись.НеподтвержденноеВремяПодписи) <> Тип("Дата") Тогда
        ВызватьИсключение "Неподтвержденное UTC-время не является датой";
    КонецЕсли;
    Цепочка = Подпись.СертификатыПроверкиПодписи;
    Если ТипЗнч(Цепочка) <> Тип("Массив") Тогда
        ВызватьИсключение "Ожидался массив сертификатов из своего CMS";
    КонецЕсли;
    Для Каждого Участник Из Цепочка Цикл
        ПроверитьСвойСертификат85(Участник, Сертификат);
    КонецЦикла;
    Сообщить("Разобран один собственный CMS. OID: " + Подпись.АлгоритмПодписи + "; " + Подпись.АлгоритмХеширования
        + ". UTC-время неподтвержденное, доверие не проверено");
КонецПроцедуры

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

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

&НаСервереБезКонтекста
Процедура ПроверитьСвойПустойСтенд()
    Если Метаданные.Справочники.Количество() <> 0 Или Метаданные.Документы.Количество() <> 0 Или Метаданные.Перечисления.Количество() <> 0 Или Метаданные.РегистрыСведений.Количество() <> 0 Или Метаданные.РегистрыНакопления.Количество() <> 0 Или Метаданные.РегистрыБухгалтерии.Количество() <> 0 Или Метаданные.РегистрыРасчета.Количество() <> 0 Или Метаданные.ПланыВидовХарактеристик.Количество() <> 0 Или Метаданные.ПланыВидовРасчета.Количество() <> 0 Или Метаданные.ПланыСчетов.Количество() <> 0 Или Метаданные.ПланыОбмена.Количество() <> 0 Или Метаданные.Последовательности.Количество() <> 0 Или Метаданные.БизнесПроцессы.Количество() <> 0 Или Метаданные.Задачи.Количество() <> 0 Или Метаданные.ОбщиеРеквизиты.Количество() <> 0 Или Метаданные.ПодпискиНаСобытия.Количество() <> 0 Или Метаданные.РегламентныеЗадания.Количество() <> 0 Или Метаданные.ВнешниеИсточникиДанных.Количество() <> 0 Тогда
        ВызватьИсключение "Нужен только свой новый пустой учебный стенд";
    КонецЕсли;
    Если Метаданные.ЕстьИзмененияРасширениямиКонфигурации() Тогда
        ВызватьИсключение "Свой сеанс не должен изменяться расширениями";
    КонецЕсли;
    Источники = Новый Массив;
    Источники.Добавить(ИсточникРасширенийКонфигурации.БазаДанных);
    Источники.Добавить(ИсточникРасширенийКонфигурации.СеансАктивные);
    Источники.Добавить(ИсточникРасширенийКонфигурации.СеансОтключенные);
    Для Каждого Источник Из Источники Цикл
        Расширения = РасширенияКонфигурации.Получить(, Источник);
        Если ТипЗнч(Расширения) <> Тип("Массив") Тогда
            ВызватьИсключение "Native список расширений должен иметь тип Массив";
        КонецЕсли;
        Если Расширения.Количество() <> 0 Тогда
            ВызватьИсключение "В своём новом стенде нет расширений базы или сеанса";
        КонецЕсли;
    КонецЦикла;
    Если ТранзакцияАктивна() Тогда
        ВызватьИсключение "Чужая или вложенная активная транзакция не допускается";
    КонецЕсли;
КонецПроцедуры
&НаКлиенте
Процедура ВыполнитьКриптоЦикл85(Команда)
    Если Занято85 Тогда
        Сообщить("Предыдущий опыт еще не завершен");
        Возврат;
    КонецЕсли;
    Занято85 = Истина;
    Статус85 = "Результат не подтвержден";
    Менеджер = Неопределено;
    Попытка
        Опыт = ПодготовитьСвойОпыт85();
        Менеджер = Опыт.Менеджер;
        ИмяКонтейнера = Менеджер.НайтиКлючевойКонтейнер(Опыт.Сертификат, Ложь);
        Если ТипЗнч(ИмяКонтейнера) <> Тип("Строка") Или ПустаяСтрока(ИмяКонтейнера) Тогда
            ВызватьИсключение "Свой сертификат не связан с доступным ключевым контейнером";
        КонецЕсли;
        Менеджер.ВключениеДанныхВПодпись = Ложь;
        Отсоединенная = Менеджер.Подписать(Опыт.Данные, Опыт.Сертификат, ТипПодписиКриптографии.CMS);
        Если ТипЗнч(Отсоединенная) <> Тип("ДвоичныеДанные") Тогда
            ВызватьИсключение "Подпись в память не вернула байты";
        КонецЕсли;
        СертификатПроверки = Неопределено;
        Менеджер.ПроверитьПодпись(Опыт.Данные, Отсоединенная, СертификатПроверки, Ложь);
        ПроверитьСвойСертификат85(СертификатПроверки, Опыт.Сертификат);
        Сертификаты = Менеджер.ПолучитьСертификатыИзПодписи(Отсоединенная);
        Если ТипЗнч(Сертификаты) <> Тип("Массив") Или Сертификаты.Количество() <> 1 Тогда
            ВызватьИсключение "Из своего CMS не извлечен единственный свой сертификат";
        КонецЕсли;
        ПроверитьСвойСертификат85(Сертификаты[0], Опыт.Сертификат);
        КонтейнерОтсоединенной = Менеджер.ПолучитьКонтейнерПодписейКриптографии(Отсоединенная);
        ПроверитьСвойКонтейнер85(КонтейнерОтсоединенной, Опыт.Сертификат, Опыт.Данные, Ложь);
        Измененные = ПолучитьДвоичныеДанныеИзСтроки("OwnCrypto85:" + Опыт.UUID + "|Книга|Количество=4|Цена=250", КодировкаТекста.UTF8);
        ИзменениеОтказано = Ложь;
        Попытка
            Менеджер.ПроверитьПодпись(Измененные, Отсоединенная, , Ложь);
        Исключение
            ИзменениеОтказано = Истина;
        КонецПопытки;
        Если Не ИзменениеОтказано Тогда
            ВызватьИсключение "Измененная карточка ошибочно прошла проверку";
        КонецЕсли;
        Менеджер.ВключениеДанныхВПодпись = Истина;
        Присоединенная = Менеджер.Подписать(Опыт.Данные, Опыт.Сертификат, ТипПодписиКриптографии.CMS);
        СертификатПрисоединенной = Неопределено;
        Менеджер.ПроверитьПодпись(Присоединенная, СертификатПрисоединенной, Ложь);
        ПроверитьСвойСертификат85(СертификатПрисоединенной, Опыт.Сертификат);
        Извлеченные = Менеджер.ПолучитьДанныеИзПодписи(Присоединенная);
        ПроверитьСвоиБайты85(Извлеченные, Опыт.Данные);
        КонтейнерПрисоединенной = Менеджер.ПолучитьКонтейнерПодписейКриптографии(Присоединенная);
        ПроверитьСвойКонтейнер85(КонтейнерПрисоединенной, Опыт.Сертификат, Опыт.Данные, Истина);
        Получатели = Новый Массив;
        Получатели.Добавить(Опыт.Сертификат);
        Зашифрованные = Менеджер.Зашифровать(Опыт.Данные, Получатели);
        Если ТипЗнч(Зашифрованные) <> Тип("ДвоичныеДанные") Тогда
            ВызватьИсключение "Шифрование в память не вернуло байты";
        КонецЕсли;
        Расшифрованные = Менеджер.Расшифровать(Зашифрованные, Опыт.Сертификат, "");
        ПроверитьСвоиБайты85(Расшифрованные, Опыт.Данные);
        Статус85 = "Своя карточка: CMS-подписи проверены, измененный payload отказан, AES-расшифровка совпала. Доверие сертификату и TSA не проверены";
        Сообщить(Статус85);
    Исключение
        Сообщить("Криптоцикл не подтвержден. Проверьте собственный UUID, провайдер, поддерживаемые алгоритмы и ключ. Чужие ключи не ищутся; секреты не выводятся");
    КонецПопытки;
    Если Менеджер <> Неопределено Тогда
        Менеджер.ПарольДоступаКЗакрытомуКлючу = "";
    КонецЕсли;
    Занято85 = Ложь;
КонецПроцедуры
PowerShell: own_crypto85_fixture.ps1

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

Ручная подготовка: создает новый собственный CSP-ключ и сертификат CurrentUser My, сохраняет только публичный DER. Здесь не выполнялся; хранилища не менялись.

param([Parameter(Mandatory=$true)][string]$BaseDirectory)
$ErrorActionPreference = 'Stop'
if ([Environment]::OSVersion.Platform -ne [PlatformID]::Win32NT) {throw 'Use only your isolated Windows VM'}
$taskBase = (Resolve-Path -LiteralPath $BaseDirectory).Path
if (-not (Test-Path -LiteralPath $taskBase -PathType Container)) {throw 'Copy the real client temporary directory from your own1C form'}
$taskIdentity = [guid]::NewGuid().ToString()
$taskFolder = Join-Path $taskBase ('OwnCrypto85-' + $taskIdentity)
if (Test-Path -LiteralPath $taskFolder) {throw 'Own UUID directory already exists'}
$taskSubject = 'CN=OwnCrypto85-' + $taskIdentity
$taskParameters = @{
    Type = 'Custom'
    Subject = $taskSubject
    FriendlyName = 'OwnCrypto85-' + $taskIdentity
    Provider = 'Microsoft Enhanced RSA and AES Cryptographic Provider'
    Container = 'OwnCrypto85-' + $taskIdentity
    KeyAlgorithm = 'RSA'
    KeyLength = 2048
    KeySpec = 'KeyExchange'
    KeyUsage = @('DigitalSignature','KeyEncipherment')
    KeyExportPolicy = 'NonExportable'
    KeyProtection = 'None'
    HashAlgorithm = 'SHA256'
    NotAfter = (Get-Date).AddDays(7)
    CertStoreLocation = 'Cert:\CurrentUser\My'
}
$taskCertificate = New-SelfSignedCertificate @taskParameters
if ($taskCertificate.Subject -ne $taskSubject -or -not $taskCertificate.HasPrivateKey) {throw 'Own certificate identity/private key not confirmed; inspect your VM manually'}
$taskKey = $taskCertificate.PrivateKey
if ($taskKey -isnot [System.Security.Cryptography.RSACryptoServiceProvider]) {throw 'Expected own legacy RSA CSP key, not CNG'}
try {
    $taskKeyInfo = $taskKey.CspKeyContainerInfo
    if ($taskKeyInfo.ProviderName -ne $taskParameters.Provider -or $taskKeyInfo.ProviderType -ne 24 -or $taskKeyInfo.MachineKeyStore) {throw 'Wrong provider, type or account store'}
    if ($taskKeyInfo.KeyContainerName -ne $taskParameters.Container) {throw 'Wrong own key container'}
    New-Item -ItemType Directory -Path $taskFolder | Out-Null
    $taskDer = $taskCertificate.Export([System.Security.Cryptography.X509Certificates.X509ContentType]::Cert)
    $taskFile = Join-Path $taskFolder 'own-public.cer'
    $taskStream = [System.IO.File]::Open($taskFile,[System.IO.FileMode]::CreateNew,[System.IO.FileAccess]::Write,[System.IO.FileShare]::None)
    try { $taskStream.Write($taskDer,0,$taskDer.Length) } finally { $taskStream.Dispose() }
    Write-Output ('Own UUID: ' + $taskIdentity)
    Write-Output ('Own public DER: ' + $taskFile)
    Write-Output ('Own subject: ' + $taskCertificate.Subject)
    Write-Output ('Own thumbprint: ' + $taskCertificate.Thumbprint)
    Write-Output 'A new private key remains in CurrentUser My. No root trust, network, export of private key or automatic cleanup.'
} finally {
    $taskKey.Dispose()
    $taskCertificate.Dispose()
}

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

  1. Занято85 предотвращает параллельную команду; серверная процедура без контекста снова подтверждает пустую собственную конфигурацию. Клиент проверяетWindows/версию/явное подтверждение отдельной VM. Подготовка создает реальный менеджер и получает конкретное Личное текущего пользователя, не читает/не печатает все чужие сертификаты.

  2. НайтиПоОтпечатку получает отпечаток нашего публичного DER, затем DER найденного объекта сравнивается побайтно через Base64. НайтиКлючевойКонтейнер получает Ложь: используется привязка именно найденного сертификата, расширенный перебор чужих ключей по открытым ключам не включается. Непустое имя контейнера — предварительное условие, не доказательство успешной подписи.

  3. Исходная UTF8-карточка включает собственный UUID, Книгу, количество3 и цену250. ПолучитьДвоичныеДанныеИзСтроки превращает текст в настоящие байты; Строка как аргумент криптометода означала бы имя файла. Сертификат всегда передается явно: нет fallback на единственный или первый личный сертификат.

  4. ВключениеДанныхВПодпись=Ложь дает отсоединенный CMS. ВключатьСертификатСубъекта вкладывает только наш сертификат, без попытки построения доверенной цепочки. Подписать/его обещание возвращает реальные ДвоичныеДанные; ПроверитьПодпись с ПроверятьДополнительныеАтрибуты=Ложь проверяет только криптографическую подпись, не доверие/отзыв/TSA.

  5. У синхронной ПроверитьПодпись результат сертификата передается третьим выходным параметром, не возвращаемым Булево. Модуль отдельно сверяет его DER.

  6. ПолучитьСертификатыИзПодписи возвращает массив с одним собственным сертификатом. ПолучитьКонтейнерПодписейКриптографии возвращает реальный контейнер и Массив Подписи. Все11 readonly-свойств одного ПодписьКриптографии разбираются: собственный сертификат/индекс0/CMS/OID, отсутствие payload у detached, отсутствие TSA/архивных меток, UTC-дата как неподтвержденная и только собственные элементы сертификатного массива.

  7. Отрицательный тест меняет количество3 на4 при том же UUID и проверяет прежнюю подпись на новых байтах. Успешная предварительная проверка оригинала обязательна, затем ожидается исключение. Исключение может быть и другой ошибкой провайдера — модуль не называет любой отказ юридическим доказательством подделки. Отсутствие исключения означает провал учебной проверки.

  8. ВключениеДанныхВПодпись=Истина создает вторую, присоединенную CMS на тех же своих байтах. Она проверяется своей перегрузкой, ПолучитьДанныеИзПодписи извлекает payload и проверяется точное равенство. У readonly Данные подписанта attached-контейнера также должны быть исходные байты, а не Неопределено.

  9. Зашифровать получает Массив только нашего получателя; CMS-шифротекст возвращается в память. Расшифровать получает тот же явный сертификат и пустой пароль собственного CSP-контейнера без PIN. Результат сравнивается с исходными байтами, не просто считается успешным по отсутствию исключения. Сетей/TSP/OCSP/файловой записи из1С нет.

  10. ПарольДоступаКЗакрытомуКлючу явно сбрасывается в собственном менеджере до опыта и после успеха/ошибки. Это настройка конкретного объекта, не доступ к чужому ключу и не механизм защиты памяти. Статус успеха устанавливается только после всех проверок. При ошибке печатается только безопасное общее сообщение; ключ/содержимое/ОписаниеОшибки не выводятся.

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

  1. Положительный опыт вручную в своей VM: оба CMS разобраны как один подписант; attached-payload совпал, изменение количества отказано, расшифрованные AES-байты совпали. Это ожидаемый протокол, не уже полученный runtime-результат. Для другого режима используйте отдельную форму с полным соответствующим модулем и тот же собственный комплект.

  2. Введите UUID другого опыта без его файлов, снимите подтверждение VM или измените имя пустой конфигурации: команда отказывает до подписи. Запуск под другимWindows-пользователем без собственного ключа должен завершиться неподтвержденным результатом; чужой ключ не подбирается.

  3. При неподдерживаемом алгоритме или недоступном ключе нет итогового успеха. Наличие публичного сертификата, непустое имя контейнера и отсутствие ошибок парсинга не заменяют успешную проверку оригинальных байтов и точное сравнение расшифровки.

  4. ПодписьКриптографии.Данные: detached=Неопределено, attached=собственная карточка. АрхивныеМеткиВремени по native могут быть Неопределено либо пустым массивом; не вызывайте Количество без проверки. UTC-дата НеподтвержденноеВремяПодписи не превращается в доверенную метку.

  5. Во время ожидания повторное нажатие должно отказать по Занято85, флаг снимается после результата/общей ошибки. Закрытие клиента не гарантирует финальный обработчик; на учебной VM проверьте сохраненный свой сертификат/ключ вручную, автоматической очистки не обещается.

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

Полный собственный криптоцикл: реальная CMS-подпись с явным своим ключом, проверка оригинала, отказ измененным байтам, разбор detached/attached и возврат исходного AES-payload. Доверие сертификату, отзыв, доверенное время и юридическая сила здесь не подтверждаются.

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

  1. Сертификат из файла без ключа не может подписывать или расшифровывать. В этом уроке private key создает явная PowerShell-подготовка в отдельном CurrentUser My; файлом передается только публичный DER. До ручного запуска никакие ключи/сертификаты нами не создавались.

  2. Ложь в проверке дополнительных атрибутов — ограниченный учебный тест математической подписи, а не рекомендация для production. Не принимайте этот режим за проверку доверия, отзыва, полномочий подписанта или соответствие юридическим требованиям.

  3. Свой CMS намеренно без TSA/OCSP/архивных меток. НеподтвержденноеВремяПодписи — UTC-данные подписи, которым нельзя доверять как внешнему времени. Массив сертификатов из контейнера сам по себе не доказывает цепочку доверия.

  4. Оба вида CMS задаются именно ВключениеДанныхВПодпись. Не применяйте ПолучитьДанныеИзПодписи к detached с ожиданием исходного payload. У API sync/await/callback разные выходные параметры; не переносите позицию Сертификат из sync в await.

  5. На своей VM подготовка оставляет новый ключ и сертификат, даже если после их создания произошла ошибка экспорта DER. Не создавайте множество попыток без проверки собственного CurrentUser My. Изменения ОС не откатываются транзакцией базы1С; автоматического удаления здесь нет.

  6. Алгоритмы выбираются по фактическим именам возможностей менеджера. OID у разобранной подписи — другой формат представления; не приравнивайте имя RSA к точному OID и не подменяйте отсутствующий AES256 устаревшим шифром.

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

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

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

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

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