ОписаниеПодписиPDF.По умолчанию

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

Создает описание подписи.

Синтаксис

Новый ОписаниеПодписиPDF()
Полное описание, параметры и ограничения

ОписаниеПодписиPDF.По умолчанию

ОписаниеПодписиPDF (PDFSignatureDescription)

По умолчанию

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

Синтаксис:
Новый ОписаниеПодписиPDF()
Описание:

Создает описание подписи.

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

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

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

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

Ждать: ЗаписьPDF подписывает свой поток, ЧтениеPDF проверяет новые байты

Полный собственный потоковый PDF lifecycle: настоящая таблица/UUID → unsigned0descriptions → ЗаписьPDF/закрытие → новый signed reader1description → verify одной и всех с exact own DER. Самостоятельная manual CSP/Root VM-подготовка и отрицательное неизвестное имя; не getter-wrapper.

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

Полный модуль собственной управляемой формы толстого Windows-клиента8.5.1.1343/новой VM/пустой файловой ИБ, только memory PDF; futuremanual, runtime0.

Подготовка

  1. Создайте новую отдельную изолированную Windows VM, отдельного только учебного Windows-пользователя и новую пустую файловую ИБ платформы ровно8.5.1.1343. Имя корня конфигурации УчебнаяПоследовательнаяПодписьPDF85, русский встроенный язык, Такси, совместимость Не использовать. Рабочие пользователи, базы и реальные ключи запрещены.

  2. В этой конфигурации не должно быть предметных объектов, регистров, заданий, внешних источников или подписок; не устанавливайте расширения. Полный server guard каждого модуля проверяет metadata/три источника расширений/версию/транзакцию.

  3. Создайте собственную внешнюю обработку УчебнаяПоследовательнаяПодписьPDF85 с основной управляемой формой. Три урока самостоятельны: выбирайте целый sync, Ждать или callback модуль, не склеивайте одноименные процедуры.

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

  5. Для ВСЕХ трех PDF-уроков создайте ПодтверждаюСвоеRootДоверие85 Булево исходноЛожь. Own Root trust нужен как явно подготовленное локальное условие; математическая подпись PDF, цепочка, срок и отзыв — разные проверки.

  6. Запускайте обработку только толстым управляемым Windows-клиентом8.5.1.1343 под тем же новым учебным пользователем. Включите режим синхронных вызовов: даже Ждать/callback варианты имеют синхронную подготовку DER/провайдера/собственных хранилищ. Нельзя переносить ключи/менеджер на сервер.

  7. В модуль своей формы вставьте весь основной код выбранного урока. Он проверяет собственный локальный DER и legacy CSP, реальные имена SHA256/RSA/AES256 из возможностей; не выбирает случайный персональный сертификат.

  8. В своей новой VM сохраните полный вспомогательный PowerShell как own_crypto85_fixture.ps1. После открытия формы скопируйте точный клиентский КаталогДляКомплекта85 и вручную в Windows PowerShell5.1 выполните .\own_crypto85_fixture.ps1 -BaseDirectory "скопированный каталог". Только этот ВАШ ручной этап создает один новый selfsigned RSA2048 legacy CSP key/type24/KeyExchange в CurrentUser My и новый публичный DER OwnCrypto85-UUID.

  9. Закрытый ключ NonExportable, KeyProtectionNone — только учебный. Скрипт не устанавливает Root-доверие и не экспортирует private key. Запишите UUID/публичный thumbprint; UUID введите в форму без изменения регистра, а40hexсимволов thumbprint — в ОжидаемыйОтпечаток85. Native Отпечаток имеет тип ДвоичныеДанные/SHA1, поэтому в модуле он переводится в hex перед сравнением с PowerShell. CN субъекта/издателя DER обязательно равен OwnCrypto85-UUID. Не создавайте новые комплекты без проверки результатов прежней подготовки.

  10. Для проверки сертификата сохраните ОТДЕЛЬНЫЙ полный trust-PowerShell как trust_only_own_uuid_der.ps1 и вручную выполните .\trust_only_own_uuid_der.ps1 -BaseDirectory "скопированный каталог" -OwnUUID "точный свой UUID" -ConfirmIsolatedVMTrust, только на этой VM. Он требует0 совпадающих именно СВОИХ Root-cert по UUID subject/thumbprint, не0 всех встроенных Root; сверяет единственный свой My/private key и точный DER; добавляет только свой публичный DER; проверяет1точный own Root и локальную одноэлементную цепочку. Повторный импорт/замена запрещены.

  11. Отключите внешнюю сеть только у своей VM через ее настройки до учебных запусков. Собственный selfsigned не имеет CDP/AIA/TSA адресов, проверки отзыва явно пропускаются; это отдельная ограниченная локальная модель доверия, не production политика безопасности.

  12. После отдельного ручного own-DER/UUID/thumbprint Root-этапа включите дополнительное подтверждение формы. Не используйте AllowTest/IgnoreTime/IgnoreSignature ради результата. Собственный ROOT не превращает сертификат в сертификат квалифицированногоУЦ; никаких TSA/OCSP/CRL гарантий.

  13. Не закрывайте форму/клиент во время ожидания или callback-цепочки. Повторная команда отказывает по Занято85. Успех выставляется только после всех стадий/отрицательных тестов; закрытие процесса не гарантирует выполнение финального callback.

  14. Ручная подготовка оставляет свой сертификат/ключ, а trust-этап еще и свой Root; автоматического удаления/отката нет. Никакие ключи, подписи, хранилища, сети или 1С нами здесь не запускались и не менялись. Все результаты до вашего ручного опыта — ожидаемые, runtime0.

  15. PDF-исток каждый раз реально создается в памяти из собственного ТабличныйДокумент: OWN PDF85, Книга/3/250/750 и свойUUID. Проверяются1страница,0вложений, ориентация0 и фактические размеры для штампа10/45/70/15мм. Не требуется какой-либо готовый неизвестный PDF.

  16. ЗаписьPDF и ЧтениеPDF работают с собственными потоками в памяти. После signer закрывается именно writer, затем его поток дает новые подписанные байты; для reader открывается новый независимый поток чтения. Не используйте исходный неподписанный reader как доказательство результата.

  17. Штамп только учебный: УЧЕБНАЯ ПОДПИСЬ — OWN PDF85. Нативные успешные проверки подписи/DER не доказывают вид штампа: для visualQA пользователь может отдельно посмотреть свою выгруженную копию, здесь файл/просмотр не создавался.

  18. У ЧтениеPDF порядок ПроверитьПодпись = Имя, выходнойСертификат, Менеджер; у ДокументPDF порядок иной. В НачатьЗаписьПодписи порядок = Менеджер, Оповещение, Описание, Представление; не переносите позиционные параметры из других объектов.

&НаКлиенте
Асинх Процедура ВыполнитьКрипто85(Команда)
    Если Занято85 Тогда
        ВызватьИсключение "Свой предыдущий PDF опыт еще не завершен";
    КонецЕсли;
    Занято85 = Истина;
    Статус85 = "Собственная подпись PDF еще не подтверждена";
    К = Неопределено;
    Успех = Ложь;
    Попытка
        К = ПодготовитьСвойPDF85();
        ОткрытьСвоеЧтениеPDF85(К, К.Исток);
        До = Ждать К.Чтение.ПолучитьОписанияПодписейАсинх(К.Менеджер);
        ПроверитьСвоиОписанияPDF85(До, К, Ложь);
        ЗакрытьСвоеЧтениеPDF85(К);
        ОткрытьСвоюЗаписьPDF85(К);
        РезультатЗаписи = Ждать К.Запись.ЗаписатьПодписьАсинх(К.Менеджер, К.Штамп, К.Описание);
        Если РезультатЗаписи <> Неопределено Тогда
            ВызватьИсключение "WriteSignatureAsync должен завершиться Неопределено";
        КонецЕсли;
        ЗафиксироватьСвоюПодписьPDF85(К);
        ОткрытьСвоеЧтениеPDF85(К, К.Подписанные);
        После = Ждать К.Чтение.ПолучитьОписанияПодписейАсинх(К.Менеджер);
        ПроверитьСвоиОписанияPDF85(После, К, Истина);
        Сертификат = Ждать К.Чтение.ПроверитьПодписьАсинх(К.Описание.Имя, , К.Менеджер);
        ПроверитьСвойСертификат85(Сертификат, К.Сертификат);
        Сертификаты = Ждать К.Чтение.ПроверитьПодписиАсинх(К.Менеджер);
        ПроверитьСвоиСертификатыPDF85(Сертификаты, К);
        ОтказИмени = Ложь;
        НеСертификат = Неопределено;
        Попытка
            НеСертификат = Ждать К.Чтение.ПроверитьПодписьАсинх(К.Описание.Имя + "_missing", , К.Менеджер);
        Исключение
            ОтказИмени = Истина;
        КонецПопытки;
        Если Не ОтказИмени И НеСертификат <> Неопределено Тогда
            ВызватьИсключение "Отсутствующее имя подписи не должно подтверждать никакой сертификат";
        КонецЕсли;
        ЗакрытьСвоеЧтениеPDF85(К);
        Успех = Истина;
    Исключение
        Успех = Ложь;
    КонецПопытки;
    Чисто = ЗакрытьСвоиРесурсыPDF85(К);
    Если Успех И Чисто Тогда
        Статус85 = "UUID " + К.UUID + ": свой PDF descriptions0→1; writer подписал; новый reader подтвердил одну и все подписи с exact own DER; missing не дал подтвержденного сертификата; все потоки закрыты. TSA/отзыв/qualified не доказаны";
    Иначе
        Статус85 = "Своя PDF-подпись/чтение/verify/закрытие не подтверждены. Не отключайте защиту и не подбирайте другой ключ";
    КонецЕсли;
    Занято85 = Ложь;
КонецПроцедуры

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

&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
    ПроверитьСвойПустойСтенд();
    UUIDКомплекта85 = "";
    ОжидаемыйОтпечаток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));
        Субъект = Публичный.Субъект;
        Издатель = Публичный.Издатель;
        Если ТипЗнч(Субъект) <> Тип("ФиксированнаяСтруктура") Или ТипЗнч(Издатель) <> Тип("ФиксированнаяСтруктура") Тогда
            ВызватьИсключение "Native subject/issuer должны быть readonly структурами собственного DER";
        КонецЕсли;
        Если Не Субъект.Свойство("CN", ИмяСубъекта) Или Не Издатель.Свойство("CN", ИмяИздателя) Тогда
            ВызватьИсключение "Нужен реальный CN собственного selfsigned DER";
        КонецЕсли;
        Если ИмяСубъекта <> "OwnCrypto85-" + ТекстUUID Или ИмяИздателя <> ИмяСубъекта Тогда
            ВызватьИсключение "Публичный DER не принадлежит нашему собственному UUID selfsigned комплекту";
        КонецЕсли;
        Если СтрДлина(ОжидаемыйОтпечаток85) <> 40
            Или ВРег(ОжидаемыйОтпечаток85) <> ВРег(ПолучитьHexСтрокуИзДвоичныхДанных(Публичный.Отпечаток)) Тогда
            ВызватьИсключение "Нужен точный публичный SHA1 hex отпечаток из собственной ручной подготовки";
        КонецЕсли;
        Менеджер = Новый МенеджерКриптографии("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()
    ПроверитьСвойПустойСтенд();
КонецПроцедуры

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

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

&НаКлиенте
Функция СоздатьСвойPDF85(UUIDОпыта)
    Таблица = Новый ТабличныйДокумент;
    Таблица.КодЯзыка = "ru";
    Таблица.Область(1, 1).Текст = "OWN PDF85";
    Таблица.Область(2, 1).Текст = "Книга";
    Таблица.Область(2, 2).Текст = "3";
    Таблица.Область(2, 3).Текст = "250";
    Таблица.Область(2, 4).Текст = "750";
    Таблица.Область(3, 1).Текст = UUIDОпыта;
    Таблица.Область(, 1, , 1).ШиринаКолонки = 45;
    Поток = Новый ПотокВПамяти;
    Попытка
        Таблица.Записать(Поток, ТипФайлаТабличногоДокумента.PDF);
        Возврат Поток.ЗакрытьИПолучитьДвоичныеДанные();
    Исключение
        Ошибка = ОписаниеОшибки();
        Попытка
            Поток.Закрыть();
        Исключение
            ВызватьИсключение "Экспорт своего PDF и закрытие не подтверждены";
        КонецПопытки;
        ВызватьИсключение Ошибка;
    КонецПопытки;
КонецФункции

&НаКлиенте
Функция ПодготовитьСвойPDF85()
    К = ПодготовитьСвойОпыт85();
    ПроверитьСвоеКорневоеДоверие85(К);
    К.Менеджер.АдресаСерверовМетокВремени = Новый Массив;
    Если К.Менеджер.АдресаСерверовМетокВремени.Количество() <> 0 Тогда
        ВызватьИсключение "В учебном PDF нет TSA-адресов";
    КонецЕсли;
    Исток = СоздатьСвойPDF85(К.UUID);
    Если ТипЗнч(Исток) <> Тип("ДвоичныеДанные") Или Исток.Размер() <= 0 Тогда
        ВызватьИсключение "Нужен фактический собственный PDF-экспорт";
    КонецЕсли;
    ПотокИстока = Исток.ОткрытьПотокДляЧтения();
    Попытка
        Документ = Новый ДокументPDF;
        Документ.Прочитать(ПотокИстока);
    Исключение
        Ошибка = ОписаниеОшибки();
        Попытка
            ПотокИстока.Закрыть();
        Исключение
            ВызватьИсключение "Чтение своего PDF и закрытие не подтверждены";
        КонецПопытки;
        ВызватьИсключение Ошибка;
    КонецПопытки;
    ПотокИстока.Закрыть();
    Если Документ.КоличествоСтраниц() <> 1 Или Документ.Вложения.Количество() <> 0 Тогда
        ВызватьИсключение "В собственном опыте одна реальная страница и нет вложений";
    КонецЕсли;
    Страница = Документ.Страницы.Получить(0);
    Если Страница.Ориентация <> 0 Или Страница.Ширина - Страница.ПолеСлева - Страница.ПолеСправа < 80
        Или Страница.Высота - Страница.ПолеСверху - Страница.ПолеСнизу < 60 Тогда
        ВызватьИсключение "Реальная собственная страница не вмещает учебный штамп10/45/70/15мм";
    КонецЕсли;
    Описание = Новый ОписаниеПодписиPDF;
    Описание.Имя = "OwnPdf85_" + К.UUID;
    Описание.Сертификат = К.Сертификат;
    Описание.ТипПодписи = ТипПодписиPDF.Утверждающая;
    Описание.БлокировкаДокумента = Ложь;
    Описание.НомерСтраницы = 1;
    Описание.Лево = 10;
    Описание.Верх = 45;
    Описание.Ширина = 70;
    Описание.Высота = 15;
    Штамп = Новый ТабличныйДокумент;
    Штамп.КодЯзыка = "ru";
    Штамп.Область(1, 1).Текст = "УЧЕБНАЯ ПОДПИСЬ — OWN PDF85";
    К.Вставить("Исток", Исток);
    К.Вставить("Описание", Описание);
    К.Вставить("Штамп", Штамп);
    К.Вставить("Чтение", Неопределено);
    К.Вставить("Запись", Неопределено);
    К.Вставить("ПотокЧтения", Неопределено);
    К.Вставить("ПотокЗаписи", Неопределено);
    К.Вставить("Подписанные", Неопределено);
    Возврат К;
КонецФункции

&НаКлиенте
Процедура ОткрытьСвоеЧтениеPDF85(К, Байты)
    Если К.Чтение <> Неопределено Или К.ПотокЧтения <> Неопределено Тогда
        ВызватьИсключение "Сначала закройте свой предыдущий reader и его поток";
    КонецЕсли;
    К.ПотокЧтения = Байты.ОткрытьПотокДляЧтения();
    К.Чтение = Новый ЧтениеPDF(К.ПотокЧтения);
КонецПроцедуры

&НаКлиенте
Процедура ЗакрытьСвоеЧтениеPDF85(К)
    К.Чтение.Закрыть();
    К.Чтение = Неопределено;
    К.ПотокЧтения.Закрыть();
    К.ПотокЧтения = Неопределено;
КонецПроцедуры

&НаКлиенте
Процедура ОткрытьСвоюЗаписьPDF85(К)
    Если К.Запись <> Неопределено Или К.ПотокЗаписи <> Неопределено Тогда
        ВызватьИсключение "Свой writer нельзя повторно открыть поверх незавершенного опыта";
    КонецЕсли;
    К.ПотокЗаписи = Новый ПотокВПамяти(ПолучитьБуферДвоичныхДанныхИзДвоичныхДанных(К.Исток));
    К.ПотокЗаписи.Перейти(0, ПозицияВПотоке.Начало);
    К.Запись = Новый ЗаписьPDF(К.ПотокЗаписи);
КонецПроцедуры

&НаКлиенте
Процедура ЗафиксироватьСвоюПодписьPDF85(К)
    К.Запись.Закрыть();
    К.Запись = Неопределено;
    К.Подписанные = К.ПотокЗаписи.ЗакрытьИПолучитьДвоичныеДанные();
    К.ПотокЗаписи = Неопределено;
    Если ТипЗнч(К.Подписанные) <> Тип("ДвоичныеДанные") Или К.Подписанные.Размер() <= 0
        Или Base64Строка(К.Подписанные) = Base64Строка(К.Исток) Тогда
        ВызватьИсключение "Подпись должна изменить собственный PDF, а не вернуть исходные байты";
    КонецЕсли;
КонецПроцедуры

&НаКлиенте
Процедура ПроверитьСвоиОписанияPDF85(Описания, К, ДолжнаБытьПодпись)
    Если ТипЗнч(Описания) <> Тип("Массив") Тогда
        ВызватьИсключение "Native PDF descriptions должны быть массивом";
    КонецЕсли;
    Если Не ДолжнаБытьПодпись Тогда
        Если Описания.Количество() <> 0 Тогда
            ВызватьИсключение "До собственной записи подписи PDF обязан быть неподписанным";
        КонецЕсли;
        Возврат;
    КонецЕсли;
    Если Описания.Количество() <> 1 Тогда
        ВызватьИсключение "В own signed PDF должна быть ровно одна реальная подпись";
    КонецЕсли;
    Описание = Описания[0];
    Если ТипЗнч(Описание) <> Тип("ОписаниеПодписиPDF") Или Описание.Имя <> К.Описание.Имя Тогда
        ВызватьИсключение "Имя подписи должно происходить из фактически прочитанного собственного PDF";
    КонецЕсли;
    ПроверитьСвойСертификат85(Описание.Сертификат, К.Сертификат);
КонецПроцедуры

&НаКлиенте
Процедура ПроверитьСвоиСертификатыPDF85(Сертификаты, К)
    Если ТипЗнч(Сертификаты) <> Тип("Массив") Или Сертификаты.Количество() <> 1 Тогда
        ВызватьИсключение "Verify all должен подтвердить единственный собственный сертификат";
    КонецЕсли;
    ПроверитьСвойСертификат85(Сертификаты[0], К.Сертификат);
КонецПроцедуры

&НаКлиенте
Функция ЗакрытьСвоиРесурсыPDF85(К)
    Чисто = Истина;
    Если К = Неопределено Тогда
        Возврат Чисто;
    КонецЕсли;
    Для Каждого Имя Из СтрРазделить("Чтение,Запись,ПотокЧтения,ПотокЗаписи", ",") Цикл
        Если К[Имя] <> Неопределено Тогда
            Попытка
                К[Имя].Закрыть();
                К[Имя] = Неопределено;
            Исключение
                Чисто = Ложь;
            КонецПопытки;
        КонецЕсли;
    КонецЦикла;
    К.Менеджер.ПарольДоступаКЗакрытомуКлючу = "";
    Возврат Чисто;
КонецФункции
PowerShell: own_crypto85_fixture.ps1

Отдельная новая собственная Windows VM, не модуль1С.

Ручной selfsigned CSP/private-key/My/publicDER этап; здесь не запускался.

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()
}
PowerShell: trust_only_own_uuid_der.ps1

Только отдельная собственная новая Windows VM.

Отдельный manual exact own DER/UUID/thumbprint root этап,0ownдо/1exactпосле; фактических store/trust операций здесь0.

param(
    [Parameter(Mandatory=$true)][string]$BaseDirectory,
    [Parameter(Mandatory=$true)][string]$OwnUUID,
    [Parameter(Mandatory=$true)][switch]$ConfirmIsolatedVMTrust
)
$ErrorActionPreference = 'Stop'
if (-not $ConfirmIsolatedVMTrust -or [Environment]::OSVersion.Platform -ne [PlatformID]::Win32NT) {throw 'Only an explicitly confirmed own isolated Windows VM'}
$taskUUID = ([guid]::Parse($OwnUUID)).ToString()
if ($taskUUID -ne $OwnUUID -or $taskUUID -eq [guid]::Empty.ToString()) {throw 'Exact nonzero own UUID required'}
$taskBase = (Resolve-Path -LiteralPath $BaseDirectory).Path
$taskFolder = Join-Path $taskBase ('OwnCrypto85-' + $taskUUID)
$taskPublicFile = Join-Path $taskFolder 'own-public.cer'
$taskResolvedFolder = (Resolve-Path -LiteralPath $taskFolder).Path
if ($taskResolvedFolder -ne [System.IO.Path]::GetFullPath($taskFolder)) {throw 'Own UUID path was redirected'}
if ((Get-Item -LiteralPath $taskResolvedFolder).Attributes -band [System.IO.FileAttributes]::ReparsePoint) {throw 'No reparse-point own directory'}
$taskFileInfo = Get-Item -LiteralPath $taskPublicFile
if ($taskFileInfo.PSIsContainer -or $taskFileInfo.Length -le 0 -or $taskFileInfo.Length -gt 65536 -or ($taskFileInfo.Attributes -band [System.IO.FileAttributes]::ReparsePoint)) {throw 'Only own bounded public DER file'}
$taskPublic = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 -ArgumentList $taskPublicFile
$taskExpectedSubject = 'CN=OwnCrypto85-' + $taskUUID
$taskOwnDer = [Convert]::ToBase64String($taskPublic.RawData)
$taskMy = New-Object System.Security.Cryptography.X509Certificates.X509Store -ArgumentList 'My','CurrentUser'
$taskRoot = New-Object System.Security.Cryptography.X509Certificates.X509Store -ArgumentList 'Root','CurrentUser'
try {
    if ($taskPublic.Subject -ne $taskExpectedSubject -or $taskPublic.Issuer -ne $taskExpectedSubject) {throw 'Only exact own self-signed UUID certificate'}
    if ([DateTime]::UtcNow -lt $taskPublic.NotBefore.ToUniversalTime() -or [DateTime]::UtcNow -ge $taskPublic.NotAfter.ToUniversalTime()) {throw 'Own certificate is not currently valid'}
    $taskMy.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadOnly)
    $taskOwnMy = @($taskMy.Certificates | Where-Object {$_.Thumbprint -eq $taskPublic.Thumbprint -or $_.Subject -eq $taskExpectedSubject})
    if ($taskOwnMy.Count -ne 1 -or -not $taskOwnMy[0].HasPrivateKey -or [Convert]::ToBase64String($taskOwnMy[0].RawData) -ne $taskOwnDer) {throw 'Exact sole own My certificate/private-key binding not proven'}
    $taskRoot.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadWrite)
    $taskOwnRootBefore = @($taskRoot.Certificates | Where-Object {$_.Thumbprint -eq $taskPublic.Thumbprint -or $_.Subject -eq $taskExpectedSubject})
    if ($taskOwnRootBefore.Count -ne 0) {throw 'Own UUID root already exists; do not automatically repeat or replace trust'}
    $taskRoot.Add($taskPublic)
    $taskOwnRootAfter = @($taskRoot.Certificates | Where-Object {$_.Thumbprint -eq $taskPublic.Thumbprint -or $_.Subject -eq $taskExpectedSubject})
    if ($taskOwnRootAfter.Count -ne 1 -or [Convert]::ToBase64String($taskOwnRootAfter[0].RawData) -ne $taskOwnDer) {throw 'Own root DER postcondition not proven; inspect only own VM manually'}
    $taskChain = New-Object System.Security.Cryptography.X509Certificates.X509Chain
    try {
        $taskChain.ChainPolicy.RevocationMode = [System.Security.Cryptography.X509Certificates.X509RevocationMode]::NoCheck
        $taskChain.ChainPolicy.VerificationFlags = [System.Security.Cryptography.X509Certificates.X509VerificationFlags]::NoFlag
        $taskChain.ChainPolicy.UrlRetrievalTimeout = [TimeSpan]::Zero
        if (-not $taskChain.Build($taskPublic)) {throw 'Only local own trust chain failed; no success promised'}
        if ($taskChain.ChainElements.Count -ne 1 -or [Convert]::ToBase64String($taskChain.ChainElements[0].Certificate.RawData) -ne $taskOwnDer) {throw 'Expected only our one self-signed local chain element'}
    } finally {$taskChain.Dispose()}
    Write-Output ('Own UUID root trust added: ' + $taskUUID)
    Write-Output ('Own public thumbprint: ' + $taskPublic.Thumbprint)
    Write-Output 'Only own DER imported to CurrentUser Root. No revocation/TSA/OCSP/qualified-signature claim; no automatic cleanup.'
} finally {
    $taskRoot.Close()
    $taskRoot.Dispose()
    $taskMy.Close()
    $taskMy.Dispose()
    $taskPublic.Dispose()
}

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

  1. Подготовьте свою новую Windows VM, точный own UUID/CN/publicDER/SHA1hex/private-CSP-key и отдельно own Root. Полный модуль формы проверяет пустую базу/версию/все источники расширений; внешняя сеть VM отключена.

  2. Команда создает реальный собственный PDF из ТабличныйДокумент. Парсинг первой страницы доказывает реальные размеры/ориентацию/0вложений; input не приходит от неизвестного файла или процесса.

  3. Первый ЧтениеPDF читает исходные bytes и получает массив описаний0. Само отсутствие подписей не выдается за successful VerifyAll: verification здесь еще не выполняется.

  4. Закройте source-reader перед созданием отдельного writable ПотокВПамяти из собственного PDF-буфера. ЗаписьPDF принимает этот поток и actual ОписаниеПодписиPDF с собственным сертификатом/уникальным именем/координатами.

  5. Ждать ЗаписатьПодписьАсинх возвращает Неопределено; проверяем это. Promise не является подписанными bytes или Bool.

  6. Закрываем writer ДО его потока; поток возвращает новые actual bytes, отличные от source. Новый reader на новом read-stream получает фактический массив описаний1, точное own имя и включенный own DER.

  7. Ждать ПроверитьПодписьАсинх(name,,manager) возвращает сертификат; Ждать ПроверитьПодписиАсинх(manager) — массив. Старые optional выходные параметры не выдаются за результаты promises.

  8. Отрицательное имя = реально прочитанное own имя + _missing. Оно отсутствует в подтвержденном единственном описании. Допускаем native исключение либо отсутствие сертификата; какой-либо возвращенный сертификат означает провал. Не обещаем исключение для undocumented missing-name поведения.

  9. Ресурсы закрываются в порядке reader/writer → соответствующие streams также при ошибке. Успех запрещен при ошибке закрытия; crypto-пароль сбрасывается. Ключ/root/cert из VM автоматически не удаляются.

  10. В callback-варианте UUID запуска и точная стадия проверяются до каждого перехода; только Missing может считаться ожидаемой native error-стадией, остальные ошибки дают неподтвержденный результат.

  11. Итог различает реальную присоединенную PDF-подпись и внешний вид учебного штампа. Не подтверждаются отзыв/TSA/OCSP, юридическая квалификация или внешняя CA-цепочка.

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

  1. До подписи0 описаний, после нового чтения1 own описание; name/DER происходят из фактического подписанного PDF.

  2. Одиночная и полная проверки подтверждают exact own DER; неизмененные исходные bytes за signed output не принимаются.

  3. Отсутствующее name не дает подтвержденного сертификата. Native throw/Undefined не смешивается с positive verify result.

  4. Правильные арности/позиции sync, Ждать и callback, в частности writer callback1argument и ошибка3arguments.

  5. Все свои reader/writer/streams закрыты; отсутствие actual запуска четко обозначено. Trust/ключи только manual новая VM.

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

Ожидаемый будущий ручной результат: unsigned0→signed1, actual own имя/DER, verify one/all, missing без сертификата, закрытые ресурсы. Native/source тесты не являются runtime-проверкой; actualsigning0.

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

  1. ЧтениеPDF и ДокументPDF — разные API. У обоих ПроверитьПодпись, но выходной сертификат и менеджер имеют различный позиционный порядок; этот урок проверяет именно sequential reader.

  2. После ЗаписьPDF.ЗаписатьПодпись content/attachments нельзя модифицировать. Здесь после signer выполняется только закрытие и новое независимое чтение.

  3. Reader verification не является обещанием TSA/qualified-signature. Свой explicit Root только локальное учебное доверие, отзыв не подготовлен.

  4. Внешний вид видимого штампа не подтвержден статикой: размер и позиция основаны на реальной странице, но просмотр требует отдельного пользовательского ручного контроля.

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

Callback: ЗаписьPDF подписывает свой поток, ЧтениеPDF проверяет новые байты

Полный собственный потоковый PDF lifecycle: настоящая таблица/UUID → unsigned0descriptions → ЗаписьPDF/закрытие → новый signed reader1description → verify одной и всех с exact own DER. Самостоятельная manual CSP/Root VM-подготовка и отрицательное неизвестное имя; не getter-wrapper.

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

Полный модуль собственной управляемой формы толстого Windows-клиента8.5.1.1343/новой VM/пустой файловой ИБ, только memory PDF; futuremanual, runtime0.

Подготовка

  1. Создайте новую отдельную изолированную Windows VM, отдельного только учебного Windows-пользователя и новую пустую файловую ИБ платформы ровно8.5.1.1343. Имя корня конфигурации УчебнаяПоследовательнаяПодписьPDF85, русский встроенный язык, Такси, совместимость Не использовать. Рабочие пользователи, базы и реальные ключи запрещены.

  2. В этой конфигурации не должно быть предметных объектов, регистров, заданий, внешних источников или подписок; не устанавливайте расширения. Полный server guard каждого модуля проверяет metadata/три источника расширений/версию/транзакцию.

  3. Создайте собственную внешнюю обработку УчебнаяПоследовательнаяПодписьPDF85 с основной управляемой формой. Три урока самостоятельны: выбирайте целый sync, Ждать или callback модуль, не склеивайте одноименные процедуры.

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

  5. Для ВСЕХ трех PDF-уроков создайте ПодтверждаюСвоеRootДоверие85 Булево исходноЛожь. Own Root trust нужен как явно подготовленное локальное условие; математическая подпись PDF, цепочка, срок и отзыв — разные проверки.

  6. Запускайте обработку только толстым управляемым Windows-клиентом8.5.1.1343 под тем же новым учебным пользователем. Включите режим синхронных вызовов: даже Ждать/callback варианты имеют синхронную подготовку DER/провайдера/собственных хранилищ. Нельзя переносить ключи/менеджер на сервер.

  7. В модуль своей формы вставьте весь основной код выбранного урока. Он проверяет собственный локальный DER и legacy CSP, реальные имена SHA256/RSA/AES256 из возможностей; не выбирает случайный персональный сертификат.

  8. В своей новой VM сохраните полный вспомогательный PowerShell как own_crypto85_fixture.ps1. После открытия формы скопируйте точный клиентский КаталогДляКомплекта85 и вручную в Windows PowerShell5.1 выполните .\own_crypto85_fixture.ps1 -BaseDirectory "скопированный каталог". Только этот ВАШ ручной этап создает один новый selfsigned RSA2048 legacy CSP key/type24/KeyExchange в CurrentUser My и новый публичный DER OwnCrypto85-UUID.

  9. Закрытый ключ NonExportable, KeyProtectionNone — только учебный. Скрипт не устанавливает Root-доверие и не экспортирует private key. Запишите UUID/публичный thumbprint; UUID введите в форму без изменения регистра, а40hexсимволов thumbprint — в ОжидаемыйОтпечаток85. Native Отпечаток имеет тип ДвоичныеДанные/SHA1, поэтому в модуле он переводится в hex перед сравнением с PowerShell. CN субъекта/издателя DER обязательно равен OwnCrypto85-UUID. Не создавайте новые комплекты без проверки результатов прежней подготовки.

  10. Для проверки сертификата сохраните ОТДЕЛЬНЫЙ полный trust-PowerShell как trust_only_own_uuid_der.ps1 и вручную выполните .\trust_only_own_uuid_der.ps1 -BaseDirectory "скопированный каталог" -OwnUUID "точный свой UUID" -ConfirmIsolatedVMTrust, только на этой VM. Он требует0 совпадающих именно СВОИХ Root-cert по UUID subject/thumbprint, не0 всех встроенных Root; сверяет единственный свой My/private key и точный DER; добавляет только свой публичный DER; проверяет1точный own Root и локальную одноэлементную цепочку. Повторный импорт/замена запрещены.

  11. Отключите внешнюю сеть только у своей VM через ее настройки до учебных запусков. Собственный selfsigned не имеет CDP/AIA/TSA адресов, проверки отзыва явно пропускаются; это отдельная ограниченная локальная модель доверия, не production политика безопасности.

  12. После отдельного ручного own-DER/UUID/thumbprint Root-этапа включите дополнительное подтверждение формы. Не используйте AllowTest/IgnoreTime/IgnoreSignature ради результата. Собственный ROOT не превращает сертификат в сертификат квалифицированногоУЦ; никаких TSA/OCSP/CRL гарантий.

  13. Не закрывайте форму/клиент во время ожидания или callback-цепочки. Повторная команда отказывает по Занято85. Успех выставляется только после всех стадий/отрицательных тестов; закрытие процесса не гарантирует выполнение финального callback.

  14. Ручная подготовка оставляет свой сертификат/ключ, а trust-этап еще и свой Root; автоматического удаления/отката нет. Никакие ключи, подписи, хранилища, сети или 1С нами здесь не запускались и не менялись. Все результаты до вашего ручного опыта — ожидаемые, runtime0.

  15. PDF-исток каждый раз реально создается в памяти из собственного ТабличныйДокумент: OWN PDF85, Книга/3/250/750 и свойUUID. Проверяются1страница,0вложений, ориентация0 и фактические размеры для штампа10/45/70/15мм. Не требуется какой-либо готовый неизвестный PDF.

  16. ЗаписьPDF и ЧтениеPDF работают с собственными потоками в памяти. После signer закрывается именно writer, затем его поток дает новые подписанные байты; для reader открывается новый независимый поток чтения. Не используйте исходный неподписанный reader как доказательство результата.

  17. Штамп только учебный: УЧЕБНАЯ ПОДПИСЬ — OWN PDF85. Нативные успешные проверки подписи/DER не доказывают вид штампа: для visualQA пользователь может отдельно посмотреть свою выгруженную копию, здесь файл/просмотр не создавался.

  18. У ЧтениеPDF порядок ПроверитьПодпись = Имя, выходнойСертификат, Менеджер; у ДокументPDF порядок иной. В НачатьЗаписьПодписи порядок = Менеджер, Оповещение, Описание, Представление; не переносите позиционные параметры из других объектов.

&НаКлиенте
Перем КонтекстPDF85;

&НаКлиенте
Процедура ВыполнитьКрипто85(Команда)
    Если Занято85 Тогда
        ВызватьИсключение "Дождитесь своей PDF callback-цепочки";
    КонецЕсли;
    Занято85 = Истина;
    Статус85 = "Своя PDF callback-подпись еще не подтверждена";
    К = Неопределено;
    Попытка
        К = ПодготовитьСвойPDF85();
        К.Вставить("Запуск", Строка(Новый УникальныйИдентификатор));
        К.Вставить("Стадия", "Исток");
        К.Вставить("Завершено", Ложь);
        КонтекстPDF85 = К;
        ОткрытьСвоеЧтениеPDF85(К, К.Исток);
        К.Чтение.НачатьПолучениеОписанияПодписей(ОповещениеPDF85("СвоиОписанияPDF85", К), К.Менеджер);
    Исключение
        Чисто = ЗакрытьСвоиРесурсыPDF85(К);
        Занято85 = Ложь;
        КонтекстPDF85 = Неопределено;
        Если Чисто Тогда
            Статус85 = "Своя PDF-подготовка/начало не подтверждены; ресурсы закрыты";
        Иначе
            Статус85 = "Своя PDF-подготовка/начало/закрытие не подтверждены";
        КонецЕсли;
    КонецПопытки;
КонецПроцедуры

&НаКлиенте
Функция ОповещениеPDF85(Имя, К)
    Возврат Новый ОписаниеОповещения(Имя, ЭтотОбъект, К, "ОшибкаPDF85", ЭтотОбъект);
КонецФункции

&НаКлиенте
Процедура ПроверитьСвоюСтадиюPDF85(К, Имя)
    ПроверитьСеанс85();
    Если Не Занято85 Или КонтекстPDF85 = Неопределено Или ТипЗнч(К) <> Тип("Структура") Тогда
        ВызватьИсключение "Нет собственного активного PDF контекста";
    КонецЕсли;
    Если К.UUID <> КонтекстPDF85.UUID Или К.Запуск <> КонтекстPDF85.Запуск
        Или К.Стадия <> Имя Или К.Завершено Тогда
        ВызватьИсключение "Подменены собственный PDF UUID/запуск/стадия";
    КонецЕсли;
КонецПроцедуры

&НаКлиенте
Процедура ЗавершитьPDF85(К, Успех)
    Если К.Завершено Тогда
        Возврат;
    КонецЕсли;
    К.Завершено = Истина;
    Чисто = ЗакрытьСвоиРесурсыPDF85(К);
    Если Успех И Чисто Тогда
        Статус85 = "UUID " + К.UUID + ": real PDF0→1signature; writer completion1arg; descriptions/one/all callbacks exact own DER; missing не подтвердил сертификат; resources закрыты. TSA/отзыв/qualified не доказаны";
    Иначе
        Статус85 = "Свой PDF callback-опыт/закрытие не подтверждены. Секреты и диагностика не выводятся";
    КонецЕсли;
    Занято85 = Ложь;
    КонтекстPDF85 = Неопределено;
КонецПроцедуры

&НаКлиенте
Процедура СвоиОписанияPDF85(Описания, К) Экспорт
    Попытка
        Если К.Стадия = "Исток" Тогда
            ПроверитьСвоюСтадиюPDF85(К, "Исток");
            ПроверитьСвоиОписанияPDF85(Описания, К, Ложь);
            ЗакрытьСвоеЧтениеPDF85(К);
            ОткрытьСвоюЗаписьPDF85(К);
            К.Стадия = "Запись";
            К.Запись.НачатьЗаписьПодписи(К.Менеджер, ОповещениеPDF85("СвояPDFПодписьЗаписана85", К), К.Описание, К.Штамп);
        Иначе
            ПроверитьСвоюСтадиюPDF85(К, "Описания");
            ПроверитьСвоиОписанияPDF85(Описания, К, Истина);
            К.Стадия = "Одна";
            К.Чтение.НачатьПроверкуПодписи(ОповещениеPDF85("СвояОднаPDF85", К), К.Описание.Имя, К.Менеджер);
        КонецЕсли;
    Исключение
        ЗавершитьPDF85(К, Ложь);
    КонецПопытки;
КонецПроцедуры

&НаКлиенте
Процедура СвояPDFПодписьЗаписана85(К) Экспорт
    Попытка
        ПроверитьСвоюСтадиюPDF85(К, "Запись");
        ЗафиксироватьСвоюПодписьPDF85(К);
        ОткрытьСвоеЧтениеPDF85(К, К.Подписанные);
        К.Стадия = "Описания";
        К.Чтение.НачатьПолучениеОписанияПодписей(ОповещениеPDF85("СвоиОписанияPDF85", К), К.Менеджер);
    Исключение
        ЗавершитьPDF85(К, Ложь);
    КонецПопытки;
КонецПроцедуры

&НаКлиенте
Процедура СвояОднаPDF85(Сертификат, К) Экспорт
    Попытка
        ПроверитьСвоюСтадиюPDF85(К, "Одна");
        ПроверитьСвойСертификат85(Сертификат, К.Сертификат);
        К.Стадия = "Все";
        К.Чтение.НачатьПроверкуПодписей(ОповещениеPDF85("СвоиВсеPDF85", К), К.Менеджер);
    Исключение
        ЗавершитьPDF85(К, Ложь);
    КонецПопытки;
КонецПроцедуры

&НаКлиенте
Процедура СвоиВсеPDF85(Сертификаты, К) Экспорт
    Попытка
        ПроверитьСвоюСтадиюPDF85(К, "Все");
        ПроверитьСвоиСертификатыPDF85(Сертификаты, К);
        К.Стадия = "Missing";
        К.Чтение.НачатьПроверкуПодписи(ОповещениеPDF85("ОтсутствующееИмяPDF85", К), К.Описание.Имя + "_missing", К.Менеджер);
    Исключение
        ЗавершитьPDF85(К, Ложь);
    КонецПопытки;
КонецПроцедуры

&НаКлиенте
Процедура ОтсутствующееИмяPDF85(Сертификат, К) Экспорт
    Попытка
        ПроверитьСвоюСтадиюPDF85(К, "Missing");
        Если Сертификат <> Неопределено Тогда
            ЗавершитьPDF85(К, Ложь);
        Иначе
            ЗавершитьPDF85(К, Истина);
        КонецЕсли;
    Исключение
        ЗавершитьPDF85(К, Ложь);
    КонецПопытки;
КонецПроцедуры

&НаКлиенте
Процедура ОшибкаPDF85(ИнформацияОбОшибке, СтандартнаяОбработка, К) Экспорт
    СтандартнаяОбработка = Ложь;
    Попытка
        Если ТипЗнч(ИнформацияОбОшибке) <> Тип("ИнформацияОбОшибке") Тогда
            ВызватьИсключение "Native error должен передать diagnostic object";
        КонецЕсли;
        Если К.Стадия = "Missing" Тогда
            ПроверитьСвоюСтадиюPDF85(К, "Missing");
            ЗавершитьPDF85(К, Истина);
        Иначе
            ЗавершитьPDF85(К, Ложь);
        КонецЕсли;
    Исключение
        ЗавершитьPDF85(К, Ложь);
    КонецПопытки;
КонецПроцедуры

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

&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
    ПроверитьСвойПустойСтенд();
    UUIDКомплекта85 = "";
    ОжидаемыйОтпечаток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));
        Субъект = Публичный.Субъект;
        Издатель = Публичный.Издатель;
        Если ТипЗнч(Субъект) <> Тип("ФиксированнаяСтруктура") Или ТипЗнч(Издатель) <> Тип("ФиксированнаяСтруктура") Тогда
            ВызватьИсключение "Native subject/issuer должны быть readonly структурами собственного DER";
        КонецЕсли;
        Если Не Субъект.Свойство("CN", ИмяСубъекта) Или Не Издатель.Свойство("CN", ИмяИздателя) Тогда
            ВызватьИсключение "Нужен реальный CN собственного selfsigned DER";
        КонецЕсли;
        Если ИмяСубъекта <> "OwnCrypto85-" + ТекстUUID Или ИмяИздателя <> ИмяСубъекта Тогда
            ВызватьИсключение "Публичный DER не принадлежит нашему собственному UUID selfsigned комплекту";
        КонецЕсли;
        Если СтрДлина(ОжидаемыйОтпечаток85) <> 40
            Или ВРег(ОжидаемыйОтпечаток85) <> ВРег(ПолучитьHexСтрокуИзДвоичныхДанных(Публичный.Отпечаток)) Тогда
            ВызватьИсключение "Нужен точный публичный SHA1 hex отпечаток из собственной ручной подготовки";
        КонецЕсли;
        Менеджер = Новый МенеджерКриптографии("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()
    ПроверитьСвойПустойСтенд();
КонецПроцедуры

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

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

&НаКлиенте
Функция СоздатьСвойPDF85(UUIDОпыта)
    Таблица = Новый ТабличныйДокумент;
    Таблица.КодЯзыка = "ru";
    Таблица.Область(1, 1).Текст = "OWN PDF85";
    Таблица.Область(2, 1).Текст = "Книга";
    Таблица.Область(2, 2).Текст = "3";
    Таблица.Область(2, 3).Текст = "250";
    Таблица.Область(2, 4).Текст = "750";
    Таблица.Область(3, 1).Текст = UUIDОпыта;
    Таблица.Область(, 1, , 1).ШиринаКолонки = 45;
    Поток = Новый ПотокВПамяти;
    Попытка
        Таблица.Записать(Поток, ТипФайлаТабличногоДокумента.PDF);
        Возврат Поток.ЗакрытьИПолучитьДвоичныеДанные();
    Исключение
        Ошибка = ОписаниеОшибки();
        Попытка
            Поток.Закрыть();
        Исключение
            ВызватьИсключение "Экспорт своего PDF и закрытие не подтверждены";
        КонецПопытки;
        ВызватьИсключение Ошибка;
    КонецПопытки;
КонецФункции

&НаКлиенте
Функция ПодготовитьСвойPDF85()
    К = ПодготовитьСвойОпыт85();
    ПроверитьСвоеКорневоеДоверие85(К);
    К.Менеджер.АдресаСерверовМетокВремени = Новый Массив;
    Если К.Менеджер.АдресаСерверовМетокВремени.Количество() <> 0 Тогда
        ВызватьИсключение "В учебном PDF нет TSA-адресов";
    КонецЕсли;
    Исток = СоздатьСвойPDF85(К.UUID);
    Если ТипЗнч(Исток) <> Тип("ДвоичныеДанные") Или Исток.Размер() <= 0 Тогда
        ВызватьИсключение "Нужен фактический собственный PDF-экспорт";
    КонецЕсли;
    ПотокИстока = Исток.ОткрытьПотокДляЧтения();
    Попытка
        Документ = Новый ДокументPDF;
        Документ.Прочитать(ПотокИстока);
    Исключение
        Ошибка = ОписаниеОшибки();
        Попытка
            ПотокИстока.Закрыть();
        Исключение
            ВызватьИсключение "Чтение своего PDF и закрытие не подтверждены";
        КонецПопытки;
        ВызватьИсключение Ошибка;
    КонецПопытки;
    ПотокИстока.Закрыть();
    Если Документ.КоличествоСтраниц() <> 1 Или Документ.Вложения.Количество() <> 0 Тогда
        ВызватьИсключение "В собственном опыте одна реальная страница и нет вложений";
    КонецЕсли;
    Страница = Документ.Страницы.Получить(0);
    Если Страница.Ориентация <> 0 Или Страница.Ширина - Страница.ПолеСлева - Страница.ПолеСправа < 80
        Или Страница.Высота - Страница.ПолеСверху - Страница.ПолеСнизу < 60 Тогда
        ВызватьИсключение "Реальная собственная страница не вмещает учебный штамп10/45/70/15мм";
    КонецЕсли;
    Описание = Новый ОписаниеПодписиPDF;
    Описание.Имя = "OwnPdf85_" + К.UUID;
    Описание.Сертификат = К.Сертификат;
    Описание.ТипПодписи = ТипПодписиPDF.Утверждающая;
    Описание.БлокировкаДокумента = Ложь;
    Описание.НомерСтраницы = 1;
    Описание.Лево = 10;
    Описание.Верх = 45;
    Описание.Ширина = 70;
    Описание.Высота = 15;
    Штамп = Новый ТабличныйДокумент;
    Штамп.КодЯзыка = "ru";
    Штамп.Область(1, 1).Текст = "УЧЕБНАЯ ПОДПИСЬ — OWN PDF85";
    К.Вставить("Исток", Исток);
    К.Вставить("Описание", Описание);
    К.Вставить("Штамп", Штамп);
    К.Вставить("Чтение", Неопределено);
    К.Вставить("Запись", Неопределено);
    К.Вставить("ПотокЧтения", Неопределено);
    К.Вставить("ПотокЗаписи", Неопределено);
    К.Вставить("Подписанные", Неопределено);
    Возврат К;
КонецФункции

&НаКлиенте
Процедура ОткрытьСвоеЧтениеPDF85(К, Байты)
    Если К.Чтение <> Неопределено Или К.ПотокЧтения <> Неопределено Тогда
        ВызватьИсключение "Сначала закройте свой предыдущий reader и его поток";
    КонецЕсли;
    К.ПотокЧтения = Байты.ОткрытьПотокДляЧтения();
    К.Чтение = Новый ЧтениеPDF(К.ПотокЧтения);
КонецПроцедуры

&НаКлиенте
Процедура ЗакрытьСвоеЧтениеPDF85(К)
    К.Чтение.Закрыть();
    К.Чтение = Неопределено;
    К.ПотокЧтения.Закрыть();
    К.ПотокЧтения = Неопределено;
КонецПроцедуры

&НаКлиенте
Процедура ОткрытьСвоюЗаписьPDF85(К)
    Если К.Запись <> Неопределено Или К.ПотокЗаписи <> Неопределено Тогда
        ВызватьИсключение "Свой writer нельзя повторно открыть поверх незавершенного опыта";
    КонецЕсли;
    К.ПотокЗаписи = Новый ПотокВПамяти(ПолучитьБуферДвоичныхДанныхИзДвоичныхДанных(К.Исток));
    К.ПотокЗаписи.Перейти(0, ПозицияВПотоке.Начало);
    К.Запись = Новый ЗаписьPDF(К.ПотокЗаписи);
КонецПроцедуры

&НаКлиенте
Процедура ЗафиксироватьСвоюПодписьPDF85(К)
    К.Запись.Закрыть();
    К.Запись = Неопределено;
    К.Подписанные = К.ПотокЗаписи.ЗакрытьИПолучитьДвоичныеДанные();
    К.ПотокЗаписи = Неопределено;
    Если ТипЗнч(К.Подписанные) <> Тип("ДвоичныеДанные") Или К.Подписанные.Размер() <= 0
        Или Base64Строка(К.Подписанные) = Base64Строка(К.Исток) Тогда
        ВызватьИсключение "Подпись должна изменить собственный PDF, а не вернуть исходные байты";
    КонецЕсли;
КонецПроцедуры

&НаКлиенте
Процедура ПроверитьСвоиОписанияPDF85(Описания, К, ДолжнаБытьПодпись)
    Если ТипЗнч(Описания) <> Тип("Массив") Тогда
        ВызватьИсключение "Native PDF descriptions должны быть массивом";
    КонецЕсли;
    Если Не ДолжнаБытьПодпись Тогда
        Если Описания.Количество() <> 0 Тогда
            ВызватьИсключение "До собственной записи подписи PDF обязан быть неподписанным";
        КонецЕсли;
        Возврат;
    КонецЕсли;
    Если Описания.Количество() <> 1 Тогда
        ВызватьИсключение "В own signed PDF должна быть ровно одна реальная подпись";
    КонецЕсли;
    Описание = Описания[0];
    Если ТипЗнч(Описание) <> Тип("ОписаниеПодписиPDF") Или Описание.Имя <> К.Описание.Имя Тогда
        ВызватьИсключение "Имя подписи должно происходить из фактически прочитанного собственного PDF";
    КонецЕсли;
    ПроверитьСвойСертификат85(Описание.Сертификат, К.Сертификат);
КонецПроцедуры

&НаКлиенте
Процедура ПроверитьСвоиСертификатыPDF85(Сертификаты, К)
    Если ТипЗнч(Сертификаты) <> Тип("Массив") Или Сертификаты.Количество() <> 1 Тогда
        ВызватьИсключение "Verify all должен подтвердить единственный собственный сертификат";
    КонецЕсли;
    ПроверитьСвойСертификат85(Сертификаты[0], К.Сертификат);
КонецПроцедуры

&НаКлиенте
Функция ЗакрытьСвоиРесурсыPDF85(К)
    Чисто = Истина;
    Если К = Неопределено Тогда
        Возврат Чисто;
    КонецЕсли;
    Для Каждого Имя Из СтрРазделить("Чтение,Запись,ПотокЧтения,ПотокЗаписи", ",") Цикл
        Если К[Имя] <> Неопределено Тогда
            Попытка
                К[Имя].Закрыть();
                К[Имя] = Неопределено;
            Исключение
                Чисто = Ложь;
            КонецПопытки;
        КонецЕсли;
    КонецЦикла;
    К.Менеджер.ПарольДоступаКЗакрытомуКлючу = "";
    Возврат Чисто;
КонецФункции
PowerShell: own_crypto85_fixture.ps1

Отдельная новая собственная Windows VM, не модуль1С.

Ручной selfsigned CSP/private-key/My/publicDER этап; здесь не запускался.

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()
}
PowerShell: trust_only_own_uuid_der.ps1

Только отдельная собственная новая Windows VM.

Отдельный manual exact own DER/UUID/thumbprint root этап,0ownдо/1exactпосле; фактических store/trust операций здесь0.

param(
    [Parameter(Mandatory=$true)][string]$BaseDirectory,
    [Parameter(Mandatory=$true)][string]$OwnUUID,
    [Parameter(Mandatory=$true)][switch]$ConfirmIsolatedVMTrust
)
$ErrorActionPreference = 'Stop'
if (-not $ConfirmIsolatedVMTrust -or [Environment]::OSVersion.Platform -ne [PlatformID]::Win32NT) {throw 'Only an explicitly confirmed own isolated Windows VM'}
$taskUUID = ([guid]::Parse($OwnUUID)).ToString()
if ($taskUUID -ne $OwnUUID -or $taskUUID -eq [guid]::Empty.ToString()) {throw 'Exact nonzero own UUID required'}
$taskBase = (Resolve-Path -LiteralPath $BaseDirectory).Path
$taskFolder = Join-Path $taskBase ('OwnCrypto85-' + $taskUUID)
$taskPublicFile = Join-Path $taskFolder 'own-public.cer'
$taskResolvedFolder = (Resolve-Path -LiteralPath $taskFolder).Path
if ($taskResolvedFolder -ne [System.IO.Path]::GetFullPath($taskFolder)) {throw 'Own UUID path was redirected'}
if ((Get-Item -LiteralPath $taskResolvedFolder).Attributes -band [System.IO.FileAttributes]::ReparsePoint) {throw 'No reparse-point own directory'}
$taskFileInfo = Get-Item -LiteralPath $taskPublicFile
if ($taskFileInfo.PSIsContainer -or $taskFileInfo.Length -le 0 -or $taskFileInfo.Length -gt 65536 -or ($taskFileInfo.Attributes -band [System.IO.FileAttributes]::ReparsePoint)) {throw 'Only own bounded public DER file'}
$taskPublic = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 -ArgumentList $taskPublicFile
$taskExpectedSubject = 'CN=OwnCrypto85-' + $taskUUID
$taskOwnDer = [Convert]::ToBase64String($taskPublic.RawData)
$taskMy = New-Object System.Security.Cryptography.X509Certificates.X509Store -ArgumentList 'My','CurrentUser'
$taskRoot = New-Object System.Security.Cryptography.X509Certificates.X509Store -ArgumentList 'Root','CurrentUser'
try {
    if ($taskPublic.Subject -ne $taskExpectedSubject -or $taskPublic.Issuer -ne $taskExpectedSubject) {throw 'Only exact own self-signed UUID certificate'}
    if ([DateTime]::UtcNow -lt $taskPublic.NotBefore.ToUniversalTime() -or [DateTime]::UtcNow -ge $taskPublic.NotAfter.ToUniversalTime()) {throw 'Own certificate is not currently valid'}
    $taskMy.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadOnly)
    $taskOwnMy = @($taskMy.Certificates | Where-Object {$_.Thumbprint -eq $taskPublic.Thumbprint -or $_.Subject -eq $taskExpectedSubject})
    if ($taskOwnMy.Count -ne 1 -or -not $taskOwnMy[0].HasPrivateKey -or [Convert]::ToBase64String($taskOwnMy[0].RawData) -ne $taskOwnDer) {throw 'Exact sole own My certificate/private-key binding not proven'}
    $taskRoot.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadWrite)
    $taskOwnRootBefore = @($taskRoot.Certificates | Where-Object {$_.Thumbprint -eq $taskPublic.Thumbprint -or $_.Subject -eq $taskExpectedSubject})
    if ($taskOwnRootBefore.Count -ne 0) {throw 'Own UUID root already exists; do not automatically repeat or replace trust'}
    $taskRoot.Add($taskPublic)
    $taskOwnRootAfter = @($taskRoot.Certificates | Where-Object {$_.Thumbprint -eq $taskPublic.Thumbprint -or $_.Subject -eq $taskExpectedSubject})
    if ($taskOwnRootAfter.Count -ne 1 -or [Convert]::ToBase64String($taskOwnRootAfter[0].RawData) -ne $taskOwnDer) {throw 'Own root DER postcondition not proven; inspect only own VM manually'}
    $taskChain = New-Object System.Security.Cryptography.X509Certificates.X509Chain
    try {
        $taskChain.ChainPolicy.RevocationMode = [System.Security.Cryptography.X509Certificates.X509RevocationMode]::NoCheck
        $taskChain.ChainPolicy.VerificationFlags = [System.Security.Cryptography.X509Certificates.X509VerificationFlags]::NoFlag
        $taskChain.ChainPolicy.UrlRetrievalTimeout = [TimeSpan]::Zero
        if (-not $taskChain.Build($taskPublic)) {throw 'Only local own trust chain failed; no success promised'}
        if ($taskChain.ChainElements.Count -ne 1 -or [Convert]::ToBase64String($taskChain.ChainElements[0].Certificate.RawData) -ne $taskOwnDer) {throw 'Expected only our one self-signed local chain element'}
    } finally {$taskChain.Dispose()}
    Write-Output ('Own UUID root trust added: ' + $taskUUID)
    Write-Output ('Own public thumbprint: ' + $taskPublic.Thumbprint)
    Write-Output 'Only own DER imported to CurrentUser Root. No revocation/TSA/OCSP/qualified-signature claim; no automatic cleanup.'
} finally {
    $taskRoot.Close()
    $taskRoot.Dispose()
    $taskMy.Close()
    $taskMy.Dispose()
    $taskPublic.Dispose()
}

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

  1. Подготовьте свою новую Windows VM, точный own UUID/CN/publicDER/SHA1hex/private-CSP-key и отдельно own Root. Полный модуль формы проверяет пустую базу/версию/все источники расширений; внешняя сеть VM отключена.

  2. Команда создает реальный собственный PDF из ТабличныйДокумент. Парсинг первой страницы доказывает реальные размеры/ориентацию/0вложений; input не приходит от неизвестного файла или процесса.

  3. Первый ЧтениеPDF читает исходные bytes и получает массив описаний0. Само отсутствие подписей не выдается за successful VerifyAll: verification здесь еще не выполняется.

  4. Закройте source-reader перед созданием отдельного writable ПотокВПамяти из собственного PDF-буфера. ЗаписьPDF принимает этот поток и actual ОписаниеПодписиPDF с собственным сертификатом/уникальным именем/координатами.

  5. НачатьЗаписьПодписи получает Менеджер первым, Оповещение вторым, Описание третьим и настоящий ТабличныйДокумент штампа четвертым. Success callback имеет только К, не Результат/К.

  6. Закрываем writer ДО его потока; поток возвращает новые actual bytes, отличные от source. Новый reader на новом read-stream получает фактический массив описаний1, точное own имя и включенный own DER.

  7. НачатьПроверкуПодписи(callback,name,manager) передает Сертификат/К; НачатьПроверкуПодписей(callback,manager) — Массив/К. Обе completion-процедуры экспортируемы, результаты сверяются с actual own DER.

  8. Отрицательное имя = реально прочитанное own имя + _missing. Оно отсутствует в подтвержденном единственном описании. Допускаем native исключение либо отсутствие сертификата; какой-либо возвращенный сертификат означает провал. Не обещаем исключение для undocumented missing-name поведения.

  9. Ресурсы закрываются в порядке reader/writer → соответствующие streams также при ошибке. Успех запрещен при ошибке закрытия; crypto-пароль сбрасывается. Ключ/root/cert из VM автоматически не удаляются.

  10. В callback-варианте UUID запуска и точная стадия проверяются до каждого перехода; только Missing может считаться ожидаемой native error-стадией, остальные ошибки дают неподтвержденный результат.

  11. Итог различает реальную присоединенную PDF-подпись и внешний вид учебного штампа. Не подтверждаются отзыв/TSA/OCSP, юридическая квалификация или внешняя CA-цепочка.

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

  1. До подписи0 описаний, после нового чтения1 own описание; name/DER происходят из фактического подписанного PDF.

  2. Одиночная и полная проверки подтверждают exact own DER; неизмененные исходные bytes за signed output не принимаются.

  3. Отсутствующее name не дает подтвержденного сертификата. Native throw/Undefined не смешивается с positive verify result.

  4. Правильные арности/позиции sync, Ждать и callback, в частности writer callback1argument и ошибка3arguments.

  5. Все свои reader/writer/streams закрыты; отсутствие actual запуска четко обозначено. Trust/ключи только manual новая VM.

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

Ожидаемый будущий ручной результат: unsigned0→signed1, actual own имя/DER, verify one/all, missing без сертификата, закрытые ресурсы. Native/source тесты не являются runtime-проверкой; actualsigning0.

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

  1. ЧтениеPDF и ДокументPDF — разные API. У обоих ПроверитьПодпись, но выходной сертификат и менеджер имеют различный позиционный порядок; этот урок проверяет именно sequential reader.

  2. После ЗаписьPDF.ЗаписатьПодпись content/attachments нельзя модифицировать. Здесь после signer выполняется только закрытие и новое независимое чтение.

  3. Reader verification не является обещанием TSA/qualified-signature. Свой explicit Root только локальное учебное доверие, отзыв не подготовлен.

  4. Внешний вид видимого штампа не подтвержден статикой: размер и позиция основаны на реальной странице, но просмотр требует отдельного пользовательского ручного контроля.

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

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

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

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

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