Тип: ДвоичныеДанные, Неопределено. Содержит подписанные данные, хранящиеся в самой подписи. Если подпись отсоединенная и не хранит данных, то содержит Неопределено.
Полное описание, параметры и ограничения
ПодписьКриптографии.Данные (CryptoSignature.Data)
ПодписьКриптографии (CryptoSignature)
Данные (Data)
Доступен, начиная с версии 8.3.24.
Использование:
Только чтение.
Описание:
Тип: ДвоичныеДанные, Неопределено.
Содержит подписанные данные, хранящиеся в самой подписи. Если подпись отсоединенная и не хранит данных, то содержит Неопределено.
Доступность:
Тонкий клиент, веб-клиент, сервер, толстый клиент, внешнее соединение.
Использование в версии:
Доступен, начиная с версии 8.3.24.
Методическая информация
Практические примеры
Через Ждать: подписать свою карточку CMS, разобрать подписанта и расшифровать AES для себя
Один полный опыт связывает собственный legacyCSP-ключ текущего пользователя, точный DER-сертификат, два вида CMS и проверку неизменности байтов. Не выбирает случайный персональный сертификат и не считает чтение контейнера проверкой подписи.
Где и как запускать
Полный модуль основной управляемой формы УчебныйКриптоЦикл85 в собственной пустой базе; толстый управляемый Windows-клиент8.5.1.1343. PowerShell — только отдельный ручной подготовительный блок.
Подготовка
Создайте отдельную собственную WindowsVM и отдельного только учебного Windows-пользователя. Новая пустая база8.5.1.1343: имя конфигурации УчебныйКриптоЦикл85, русский встроенный язык, Такси, совместимость Не использовать. Не добавляйте предметные объекты, расширения или задания. Полный guard встроен в модуль. Рабочий пользователь/сервер/production исключены.
Внешняя обработка УчебныйКриптоЦикл85 → основная управляемая форма. Реквизиты: UUIDКомплекта85 Строка длиной36; КаталогДляКомплекта85 Строка длиной0, ТолькоПросмотр; Статус85 Строка длиной0, многострочное поле ТолькоПросмотр; ПодтверждаюУчебнуюVM85 Булево, начально Ложь. Создайте команду ВыполнитьКриптоЦикл85, одноименное действие/кнопку, ИзменяетСохраняемыеДанные выключено. Назначьте ПриСозданииНаСервере и ПриОткрытии. Вставьте весь основной блок в модуль этой формы; два варианта не вставляйте вместе.
Откройте обработку толстым управляемым Windows-клиентом8.5.1.1343 под тем же учебным пользователем, что запускает подготовку ключа. Разрешите режим синхронных вызовов: даже модуль с Ждать содержит синхронную подготовку и публичный DER-разбор. Веб/мобильный/тонкий/серверный положительный путь не заявляется.
Сохраните отдельный нижний 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. Не запускайте скрипт на основном компьютере/пользователе.
Этот этап действительно меняет хранилище только при ВАШЕМ ручном запуске: ключ NonExportable, KeyProtection None, хранится в текущем учебном пользователе, корневое доверие не добавляется. Если CSP/legacyRSA/private-key/provider/type/имя контейнера не совпали, скрипт останавливается без автоматического удаления; проверьте собственную VM. Не заменяйте CSP на CNG/KSP: контракт менеджера1С для этого урока выбран именно по имени провайдера24.
Впишите напечатанный UUID, вручную отметьте ПодтверждаюУчебнуюVM85 и нажмите команду. Имя файла строится из реального клиентского temp и UUID, другие пути в пульт не вводятся. Публичный DER и найденный по двоичному отпечатку сертификат должны точно совпасть. Наличие публичного DER недостаточно: ключ должен оставаться в CurrentUser My того же Windows-пользователя.
Выбор SHA256/RSA/AES256 допускается только если фактические массивы возможностей вашего провайдера содержат эти точные имена. Если написание отличается или алгоритм отсутствует, урок отказывает, не заменяет алгоритм на SHA1/3DES. Сначала изучите свой отчёт ИнформацияМодуляКриптографии; не подставляйте случайные имена/OID в код.
После опыта завершите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()
}
Разбор по шагам
Занято85 предотвращает параллельную команду; серверная процедура без контекста снова подтверждает пустую собственную конфигурацию. Клиент проверяетWindows/версию/явное подтверждение отдельной VM. Подготовка создает реальный менеджер и получает конкретное Личное текущего пользователя, не читает/не печатает все чужие сертификаты.
НайтиПоОтпечатку получает отпечаток нашего публичного DER, затем DER найденного объекта сравнивается побайтно через Base64. НайтиКлючевойКонтейнер получает Ложь: используется привязка именно найденного сертификата, расширенный перебор чужих ключей по открытым ключам не включается. Непустое имя контейнера — предварительное условие, не доказательство успешной подписи.
Исходная UTF8-карточка включает собственный UUID, Книгу, количество3 и цену250. ПолучитьДвоичныеДанныеИзСтроки превращает текст в настоящие байты; Строка как аргумент криптометода означала бы имя файла. Сертификат всегда передается явно: нет fallback на единственный или первый личный сертификат.
ВключениеДанныхВПодпись=Ложь дает отсоединенный CMS. ВключатьСертификатСубъекта вкладывает только наш сертификат, без попытки построения доверенной цепочки. Подписать/его обещание возвращает реальные ДвоичныеДанные; ПроверитьПодпись с ПроверятьДополнительныеАтрибуты=Ложь проверяет только криптографическую подпись, не доверие/отзыв/TSA.
У ПроверитьПодписьАсинх нет выходного параметра Сертификат: ее варианты отличаются от sync. Ждать возвращает Неопределено при успехе, ошибка приходит исключением; сертификаты извлекаются отдельным методом. Каждый обещающий вызов дожидается результата.
ПолучитьСертификатыИзПодписи возвращает массив с одним собственным сертификатом. ПолучитьКонтейнерПодписейКриптографии возвращает реальный контейнер и Массив Подписи. Все11 readonly-свойств одного ПодписьКриптографии разбираются: собственный сертификат/индекс0/CMS/OID, отсутствие payload у detached, отсутствие TSA/архивных меток, UTC-дата как неподтвержденная и только собственные элементы сертификатного массива.
Отрицательный тест меняет количество3 на4 при том же UUID и проверяет прежнюю подпись на новых байтах. Успешная предварительная проверка оригинала обязательна, затем ожидается исключение. Исключение может быть и другой ошибкой провайдера — модуль не называет любой отказ юридическим доказательством подделки. Отсутствие исключения означает провал учебной проверки.
ВключениеДанныхВПодпись=Истина создает вторую, присоединенную CMS на тех же своих байтах. Она проверяется своей перегрузкой, ПолучитьДанныеИзПодписи извлекает payload и проверяется точное равенство. У readonly Данные подписанта attached-контейнера также должны быть исходные байты, а не Неопределено.
Зашифровать получает Массив только нашего получателя; CMS-шифротекст возвращается в память. Расшифровать получает тот же явный сертификат и пустой пароль собственного CSP-контейнера без PIN. Результат сравнивается с исходными байтами, не просто считается успешным по отсутствию исключения. Сетей/TSP/OCSP/файловой записи из1С нет.
ПарольДоступаКЗакрытомуКлючу явно сбрасывается в собственном менеджере до опыта и после успеха/ошибки. Это настройка конкретного объекта, не доступ к чужому ключу и не механизм защиты памяти. Статус успеха устанавливается только после всех проверок. При ошибке печатается только безопасное общее сообщение; ключ/содержимое/ОписаниеОшибки не выводятся.
Как проверить результат
Положительный опыт вручную в своей VM: оба CMS разобраны как один подписант; attached-payload совпал, изменение количества отказано, расшифрованные AES-байты совпали. Это ожидаемый протокол, не уже полученный runtime-результат. Для другого режима используйте отдельную форму с полным соответствующим модулем и тот же собственный комплект.
Введите UUID другого опыта без его файлов, снимите подтверждение VM или измените имя пустой конфигурации: команда отказывает до подписи. Запуск под другимWindows-пользователем без собственного ключа должен завершиться неподтвержденным результатом; чужой ключ не подбирается.
При неподдерживаемом алгоритме или недоступном ключе нет итогового успеха. Наличие публичного сертификата, непустое имя контейнера и отсутствие ошибок парсинга не заменяют успешную проверку оригинальных байтов и точное сравнение расшифровки.
ПодписьКриптографии.Данные: detached=Неопределено, attached=собственная карточка. АрхивныеМеткиВремени по native могут быть Неопределено либо пустым массивом; не вызывайте Количество без проверки. UTC-дата НеподтвержденноеВремяПодписи не превращается в доверенную метку.
Во время ожидания повторное нажатие должно отказать по Занято85, флаг снимается после результата/общей ошибки. Закрытие клиента не гарантирует финальный обработчик; на учебной VM проверьте сохраненный свой сертификат/ключ вручную, автоматической очистки не обещается.
Ожидаемый результат
Полный собственный криптоцикл: реальная CMS-подпись с явным своим ключом, проверка оригинала, отказ измененным байтам, разбор detached/attached и возврат исходного AES-payload. Доверие сертификату, отзыв, доверенное время и юридическая сила здесь не подтверждаются.
На что обратить внимание
Сертификат из файла без ключа не может подписывать или расшифровывать. В этом уроке private key создает явная PowerShell-подготовка в отдельном CurrentUser My; файлом передается только публичный DER. До ручного запуска никакие ключи/сертификаты нами не создавались.
Ложь в проверке дополнительных атрибутов — ограниченный учебный тест математической подписи, а не рекомендация для production. Не принимайте этот режим за проверку доверия, отзыва, полномочий подписанта или соответствие юридическим требованиям.
Свой CMS намеренно без TSA/OCSP/архивных меток. НеподтвержденноеВремяПодписи — UTC-данные подписи, которым нельзя доверять как внешнему времени. Массив сертификатов из контейнера сам по себе не доказывает цепочку доверия.
Оба вида CMS задаются именно ВключениеДанныхВПодпись. Не применяйте ПолучитьДанныеИзПодписи к detached с ожиданием исходного payload. У API sync/await/callback разные выходные параметры; не переносите позицию Сертификат из sync в await.
На своей VM подготовка оставляет новый ключ и сертификат, даже если после их создания произошла ошибка экспорта DER. Не создавайте множество попыток без проверки собственного CurrentUser My. Изменения ОС не откатываются транзакцией базы1С; автоматического удаления здесь нет.
Алгоритмы выбираются по фактическим именам возможностей менеджера. OID у разобранной подписи — другой формат представления; не приравнивайте имя RSA к точному OID и не подменяйте отсутствующий AES256 устаревшим шифром.
Пример не запускался в вашей базе. Перед применением проверьте его в указанном учебном контексте.
Синхронно: подписать свою карточку CMS, разобрать подписанта и расшифровать AES для себя
Один полный опыт связывает собственный legacyCSP-ключ текущего пользователя, точный DER-сертификат, два вида CMS и проверку неизменности байтов. Не выбирает случайный персональный сертификат и не считает чтение контейнера проверкой подписи.
Где и как запускать
Полный модуль основной управляемой формы УчебныйКриптоЦикл85 в собственной пустой базе; толстый управляемый Windows-клиент8.5.1.1343. PowerShell — только отдельный ручной подготовительный блок.
Подготовка
Создайте отдельную собственную WindowsVM и отдельного только учебного Windows-пользователя. Новая пустая база8.5.1.1343: имя конфигурации УчебныйКриптоЦикл85, русский встроенный язык, Такси, совместимость Не использовать. Не добавляйте предметные объекты, расширения или задания. Полный guard встроен в модуль. Рабочий пользователь/сервер/production исключены.
Внешняя обработка УчебныйКриптоЦикл85 → основная управляемая форма. Реквизиты: UUIDКомплекта85 Строка длиной36; КаталогДляКомплекта85 Строка длиной0, ТолькоПросмотр; Статус85 Строка длиной0, многострочное поле ТолькоПросмотр; ПодтверждаюУчебнуюVM85 Булево, начально Ложь. Создайте команду ВыполнитьКриптоЦикл85, одноименное действие/кнопку, ИзменяетСохраняемыеДанные выключено. Назначьте ПриСозданииНаСервере и ПриОткрытии. Вставьте весь основной блок в модуль этой формы; два варианта не вставляйте вместе.
Откройте обработку толстым управляемым Windows-клиентом8.5.1.1343 под тем же учебным пользователем, что запускает подготовку ключа. Разрешите режим синхронных вызовов: даже модуль с Ждать содержит синхронную подготовку и публичный DER-разбор. Веб/мобильный/тонкий/серверный положительный путь не заявляется.
Сохраните отдельный нижний 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. Не запускайте скрипт на основном компьютере/пользователе.
Этот этап действительно меняет хранилище только при ВАШЕМ ручном запуске: ключ NonExportable, KeyProtection None, хранится в текущем учебном пользователе, корневое доверие не добавляется. Если CSP/legacyRSA/private-key/provider/type/имя контейнера не совпали, скрипт останавливается без автоматического удаления; проверьте собственную VM. Не заменяйте CSP на CNG/KSP: контракт менеджера1С для этого урока выбран именно по имени провайдера24.
Впишите напечатанный UUID, вручную отметьте ПодтверждаюУчебнуюVM85 и нажмите команду. Имя файла строится из реального клиентского temp и UUID, другие пути в пульт не вводятся. Публичный DER и найденный по двоичному отпечатку сертификат должны точно совпасть. Наличие публичного DER недостаточно: ключ должен оставаться в CurrentUser My того же Windows-пользователя.
Выбор SHA256/RSA/AES256 допускается только если фактические массивы возможностей вашего провайдера содержат эти точные имена. Если написание отличается или алгоритм отсутствует, урок отказывает, не заменяет алгоритм на SHA1/3DES. Сначала изучите свой отчёт ИнформацияМодуляКриптографии; не подставляйте случайные имена/OID в код.
После опыта завершите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()
}
Разбор по шагам
Занято85 предотвращает параллельную команду; серверная процедура без контекста снова подтверждает пустую собственную конфигурацию. Клиент проверяетWindows/версию/явное подтверждение отдельной VM. Подготовка создает реальный менеджер и получает конкретное Личное текущего пользователя, не читает/не печатает все чужие сертификаты.
НайтиПоОтпечатку получает отпечаток нашего публичного DER, затем DER найденного объекта сравнивается побайтно через Base64. НайтиКлючевойКонтейнер получает Ложь: используется привязка именно найденного сертификата, расширенный перебор чужих ключей по открытым ключам не включается. Непустое имя контейнера — предварительное условие, не доказательство успешной подписи.
Исходная UTF8-карточка включает собственный UUID, Книгу, количество3 и цену250. ПолучитьДвоичныеДанныеИзСтроки превращает текст в настоящие байты; Строка как аргумент криптометода означала бы имя файла. Сертификат всегда передается явно: нет fallback на единственный или первый личный сертификат.
ВключениеДанныхВПодпись=Ложь дает отсоединенный CMS. ВключатьСертификатСубъекта вкладывает только наш сертификат, без попытки построения доверенной цепочки. Подписать/его обещание возвращает реальные ДвоичныеДанные; ПроверитьПодпись с ПроверятьДополнительныеАтрибуты=Ложь проверяет только криптографическую подпись, не доверие/отзыв/TSA.
У синхронной ПроверитьПодпись результат сертификата передается третьим выходным параметром, не возвращаемым Булево. Модуль отдельно сверяет его DER.
ПолучитьСертификатыИзПодписи возвращает массив с одним собственным сертификатом. ПолучитьКонтейнерПодписейКриптографии возвращает реальный контейнер и Массив Подписи. Все11 readonly-свойств одного ПодписьКриптографии разбираются: собственный сертификат/индекс0/CMS/OID, отсутствие payload у detached, отсутствие TSA/архивных меток, UTC-дата как неподтвержденная и только собственные элементы сертификатного массива.
Отрицательный тест меняет количество3 на4 при том же UUID и проверяет прежнюю подпись на новых байтах. Успешная предварительная проверка оригинала обязательна, затем ожидается исключение. Исключение может быть и другой ошибкой провайдера — модуль не называет любой отказ юридическим доказательством подделки. Отсутствие исключения означает провал учебной проверки.
ВключениеДанныхВПодпись=Истина создает вторую, присоединенную CMS на тех же своих байтах. Она проверяется своей перегрузкой, ПолучитьДанныеИзПодписи извлекает payload и проверяется точное равенство. У readonly Данные подписанта attached-контейнера также должны быть исходные байты, а не Неопределено.
Зашифровать получает Массив только нашего получателя; CMS-шифротекст возвращается в память. Расшифровать получает тот же явный сертификат и пустой пароль собственного CSP-контейнера без PIN. Результат сравнивается с исходными байтами, не просто считается успешным по отсутствию исключения. Сетей/TSP/OCSP/файловой записи из1С нет.
ПарольДоступаКЗакрытомуКлючу явно сбрасывается в собственном менеджере до опыта и после успеха/ошибки. Это настройка конкретного объекта, не доступ к чужому ключу и не механизм защиты памяти. Статус успеха устанавливается только после всех проверок. При ошибке печатается только безопасное общее сообщение; ключ/содержимое/ОписаниеОшибки не выводятся.
Как проверить результат
Положительный опыт вручную в своей VM: оба CMS разобраны как один подписант; attached-payload совпал, изменение количества отказано, расшифрованные AES-байты совпали. Это ожидаемый протокол, не уже полученный runtime-результат. Для другого режима используйте отдельную форму с полным соответствующим модулем и тот же собственный комплект.
Введите UUID другого опыта без его файлов, снимите подтверждение VM или измените имя пустой конфигурации: команда отказывает до подписи. Запуск под другимWindows-пользователем без собственного ключа должен завершиться неподтвержденным результатом; чужой ключ не подбирается.
При неподдерживаемом алгоритме или недоступном ключе нет итогового успеха. Наличие публичного сертификата, непустое имя контейнера и отсутствие ошибок парсинга не заменяют успешную проверку оригинальных байтов и точное сравнение расшифровки.
ПодписьКриптографии.Данные: detached=Неопределено, attached=собственная карточка. АрхивныеМеткиВремени по native могут быть Неопределено либо пустым массивом; не вызывайте Количество без проверки. UTC-дата НеподтвержденноеВремяПодписи не превращается в доверенную метку.
Во время ожидания повторное нажатие должно отказать по Занято85, флаг снимается после результата/общей ошибки. Закрытие клиента не гарантирует финальный обработчик; на учебной VM проверьте сохраненный свой сертификат/ключ вручную, автоматической очистки не обещается.
Ожидаемый результат
Полный собственный криптоцикл: реальная CMS-подпись с явным своим ключом, проверка оригинала, отказ измененным байтам, разбор detached/attached и возврат исходного AES-payload. Доверие сертификату, отзыв, доверенное время и юридическая сила здесь не подтверждаются.
На что обратить внимание
Сертификат из файла без ключа не может подписывать или расшифровывать. В этом уроке private key создает явная PowerShell-подготовка в отдельном CurrentUser My; файлом передается только публичный DER. До ручного запуска никакие ключи/сертификаты нами не создавались.
Ложь в проверке дополнительных атрибутов — ограниченный учебный тест математической подписи, а не рекомендация для production. Не принимайте этот режим за проверку доверия, отзыва, полномочий подписанта или соответствие юридическим требованиям.
Свой CMS намеренно без TSA/OCSP/архивных меток. НеподтвержденноеВремяПодписи — UTC-данные подписи, которым нельзя доверять как внешнему времени. Массив сертификатов из контейнера сам по себе не доказывает цепочку доверия.
Оба вида CMS задаются именно ВключениеДанныхВПодпись. Не применяйте ПолучитьДанныеИзПодписи к detached с ожиданием исходного payload. У API sync/await/callback разные выходные параметры; не переносите позицию Сертификат из sync в await.
На своей VM подготовка оставляет новый ключ и сертификат, даже если после их создания произошла ошибка экспорта DER. Не создавайте множество попыток без проверки собственного CurrentUser My. Изменения ОС не откатываются транзакцией базы1С; автоматического удаления здесь нет.
Алгоритмы выбираются по фактическим именам возможностей менеджера. OID у разобранной подписи — другой формат представления; не приравнивайте имя RSA к точному OID и не подменяйте отсутствующий AES256 устаревшим шифром.
Пример не запускался в вашей базе. Перед применением проверьте его в указанном учебном контексте.