ДокументPDF.ЗаписатьАсинх

Методы · WriteAsync

Асинхронная функция. Записывает изменения в PDF-документ.

Синтаксис

ЗаписатьАсинх(<ИмяФайла>, <Пароль>, <ТипФайла>)
ЗаписатьАсинх(<Поток>, <Пароль>, <ТипФайла>)
Полное описание, параметры и ограничения

ДокументPDF.ЗаписатьАсинх (PDFDocument.WriteAsync)

ДокументPDF (PDFDocument)

ЗаписатьАсинх (WriteAsync)

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

Вариант синтаксиса: В файл

Синтаксис:
ЗаписатьАсинх(<ИмяФайла>, <Пароль>, <ТипФайла>)
Параметры:

<ИмяФайла> (обязательный)
Тип: Строка.
Имя файла для записи PDF-документа.

<Пароль> (необязательный)
Тип: Строка.
Пароль для доступа к PDF-документу.

<ТипФайла> (необязательный)
Тип: ТипФайлаДокументаPDF, Неопределено.
Тип файла документа PDF. Если содержит Неопределено, то в качестве значения параметра используется свойство ТипФайла объекта ДокументPDF.
Описание варианта метода:
Асинхронная функция. Записывает документ PDF.
Если документ имеет электронную цифровую подпись и выполняется запись с паролем, то генерируется исключение.

Доступность:
Тонкий клиент, толстый клиент, веб-клиент.
Вариант синтаксиса: В поток

Синтаксис:
ЗаписатьАсинх(<Поток>, <Пароль>, <ТипФайла>)
Параметры:

<Поток> (обязательный)
Тип: Поток, ПотокВПамяти, ФайловыйПоток.
Поток для записи PDF-документа.

<Пароль> (необязательный)
Тип: Строка.
Пароль для доступа к PDF-документу.

<ТипФайла> (необязательный)
Тип: ТипФайлаДокументаPDF, Неопределено.
Тип файла документа PDF. Если содержит Неопределено, то в качестве значения параметра используется свойство ТипФайла объекта ДокументPDF.
Описание варианта метода:
Асинхронная функция. Записывает изменения в документ PDF.
Если документ имеет электронную цифровую подпись и выполняется запись с паролем, то генерируется исключение.

Доступность:
Тонкий клиент, толстый клиент, веб-клиент.
Возвращаемое значение:
Тип: Обещание.

Описание:

Асинхронная функция. Записывает изменения в PDF-документ.

Доступность:

Тонкий клиент, веб-клиент, сервер, толстый клиент.

Примечание:
Для работы метода в веб-клиенте требуется наличие расширения работы с файлами.
См. также:
ДокументPDF, метод Записать

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

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

Описание изменено в версии 8.3.24.

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

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

Асинхронно прочитать, поставить штамп и записать собственный PDF

Три операции с Ждать идут строго последовательно. В конце показывается документ, действительно прочитанный из новых байтов, а не только исходный объект.

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

Полный модуль собственной управляемой формы внешней обработки, толстый клиент Windows 8.5.1.1343.

Подготовка

  1. Создайте отдельную собственную конфигурацию УчебнаяКонфигурацияСинтаксиса и внешнюю обработку с управляемой формой. Команда Выполнить связана с процедурой Выполнить, кнопка вызывает эту команду.

  2. Вставьте полный модуль одного урока на уровне модуля формы, не внутрь существующего обработчика. Серверная функция создаёт собственную таблицу Книга / 3 / 250 / 750 и возвращает настоящие PDF-байты.

  3. Используйте толстый клиент Windows платформы 8.5.1.1343. Серверу необходима возможность экспортировать табличный документ в PDF. Веб-клиент и его расширения этим уроком не проверяются.

  4. Окна имеют префикс guide-owned; закрывайте вручную только эти окна. Показать передаётся пустое имя файла, поэтому рабочий файл для автосохранения не назначается.

  5. Во время асинхронной операции не закрывайте форму и не запускайте второй экземпляр операции. Ждать означает ожидание завершения обещания, а не получение готового документа без ожидания.

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

  7. При подготовке пособия эти окна и печать в 1С не запускались: проверки структуры не являются доказательством внешнего вида или физического вывода.

Перем СобственныйPDF;
Перем Занято;

&НаСервереБезКонтекста
Функция СоздатьСобственныеБайтыPDF()
    Таблица = Новый ТабличныйДокумент;
    Таблица.КодЯзыка = "ru";
    Таблица.Область(1, 1).Текст = "СОБСТВЕННЫЙ УЧЕБНЫЙ ОБРАЗЕЦ";
    Таблица.Область(2, 1).Текст = "Книга";
    Таблица.Область(2, 2).Текст = "3";
    Таблица.Область(2, 3).Текст = "250";
    Таблица.Область(2, 4).Текст = "750";
    Таблица.Область(1, 1, 2, 4).ЦветФона = Новый Цвет(255, 255, 210);
    Таблица.Область(, 1, , 1).ШиринаКолонки = 45;
    Поток = Новый ПотокВПамяти;
    Попытка
        Таблица.Записать(Поток, ТипФайлаТабличногоДокумента.PDF);
        Данные = Поток.ЗакрытьИПолучитьДвоичныеДанные();
    Исключение
        Поток.Закрыть();
        ВызватьИсключение;
    КонецПопытки;
    Возврат Данные;
КонецФункции

&НаКлиенте
Функция ПрочитатьСобственныйДокумент(Данные)
    Поток = Данные.ОткрытьПотокДляЧтения();
    Попытка
        Документ = Новый ДокументPDF;
        Документ.Прочитать(Поток);
        Поток.Закрыть();
    Исключение
        Поток.Закрыть();
        ВызватьИсключение;
    КонецПопытки;
    Возврат Документ;
КонецФункции

&НаКлиенте
Функция СоздатьСобственныйШтамп()
    ТаблицаШтампа = Новый ТабличныйДокумент;
    ТаблицаШтампа.КодЯзыка = "ru";
    ТаблицаШтампа.Область(1, 1).Текст = "ОБРАЗЕЦ — НЕ ДОКУМЕНТ";
    ТаблицаШтампа.Область(1, 1).ЦветФона = Новый Цвет(210, 240, 255);
    Описание = Новый ОписаниеОтображаемогоОбъектаPDF;
    Описание.Имя = "guide-owned-stamp";
    Описание.Объект = ТаблицаШтампа;
    Описание.НомерСтраницы = 1;
    Описание.Лево = 10;
    Описание.Верх = 45;
    Описание.Ширина = 60;
    Описание.Высота = 10;
    Возврат Описание;
КонецФункции

&НаКлиенте
Асинх Процедура Выполнить(Команда)
    Если Занято = Истина Тогда
        ВызватьИсключение "Дождитесь завершения собственной операции";
    КонецЕсли;
    Занято = Истина;
    ВходнойПоток = Неопределено;
    ВыходнойПоток = Неопределено;
    Попытка
        Данные = СоздатьСобственныеБайтыPDF();
        ВходнойПоток = Данные.ОткрытьПотокДляЧтения();
        СобственныйPDF = Новый ДокументPDF;
        Ждать СобственныйPDF.ПрочитатьАсинх(ВходнойПоток, "");
        ВходнойПоток.Закрыть();
        ВходнойПоток = Неопределено;
        Если СобственныйPDF.КоличествоСтраниц() = 0 Тогда
            ВызватьИсключение "Нет собственной страницы для штампа";
        КонецЕсли;
        Первая = СобственныйPDF.Страницы.Получить(0);
        Если Первая.Ширина < 80 Или Первая.Высота < 65 Тогда
            ВызватьИсключение "Собственная страница меньше учебного прямоугольника";
        КонецЕсли;
        Описание = СоздатьСобственныйШтамп();
        Ждать СобственныйPDF.ДобавитьОтображаемыйОбъектАсинх(Описание, Истина);
        ВыходнойПоток = Новый ПотокВПамяти;
        Ждать СобственныйPDF.ЗаписатьАсинх(ВыходнойПоток, "", ТипФайлаДокументаPDF.PDF);
        НовыеБайты = ВыходнойПоток.ЗакрытьИПолучитьДвоичныеДанные();
        ВыходнойПоток = Неопределено;
        Копия = ПрочитатьСобственныйДокумент(НовыеБайты);
        Сообщить(НовыеБайты.Размер() > 0);
        Сообщить(Копия.КоличествоСтраниц() = СобственныйPDF.КоличествоСтраниц());
        СобственныйPDF = Копия;
        СобственныйPDF.Показать("guide-owned: штамп после асинхронной записи и чтения", "");
        Занято = Ложь;
    Исключение
        ОшибкаОперации = ОписаниеОшибки();
        Если ВходнойПоток <> Неопределено Тогда
            ВходнойПоток.Закрыть();
        КонецЕсли;
        Если ВыходнойПоток <> Неопределено Тогда
            ВыходнойПоток.Закрыть();
        КонецЕсли;
        Занято = Ложь;
        ВызватьИсключение ОшибкаОперации;
    КонецПопытки;
КонецПроцедуры

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

  1. Создать свой PDF и открыть входной поток. Дождаться ПрочитатьАсинх, только затем закрыть поток.

  2. Создать настоящий собственный штамп и дождаться его добавления с явным учётом ориентации.

  3. Записать в новый поток с пустым паролем и форматом PDF. Дождаться записи до получения и закрытия байтов.

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

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

  1. Две структурные проверки дают Истина: новые байты непусты и число страниц сохраняется.

  2. В окне с собственным заголовком после асинхронной записи виден учебный штамп. Эта проверка выполняется пользователем в 1С, не тестами HTML.

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

Полный последовательный асинхронный PDF-цикл с реальным чтением результата и управлением временем жизни потоков.

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

  1. Нельзя закрывать поток сразу после получения Обещание без Ждать: операция ещё может использовать данные.

  2. ПрочитатьАсинх и ЗаписатьАсинх не возвращают готовый новый ДокументPDF. Пользуемся объектом после завершения ожидания.

  3. Здесь только собственные байты в памяти; не подставляйте путь рабочего документа для перезаписи.

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

Асинхронно подписать свой PDF и получить реальные результаты проверки в тонком клиенте

Полный модуль собственной формы получает свои PDF-байты с сервера, использует явно подготовленный CurrentUser-ключ, ждёт подпись/запись/чтение и проверяет реальные Array/Certificate-результаты Promise. Ни Promise, ни выходные параметры синхронных overloads не подменяют готовый результат.

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

Полный модуль управляемой формы собственного тонкого Windows-клиента: PDF-операции с Ждать, отдельный полный supporting-серверный модуль для генерации своих байтов. В1С не запускался.

Подготовка

  1. НОВАЯ пустая файловая учебная база УчебнаяАсинхроннаяПодписьPDF85 на8.5.1.1343, совместимость Не использовать. Только свои модуль и форма; без рабочих объектов, расширений, подписок/заданий. Запускать под ОТДЕЛЬНЫМ собственным Windows-пользователем или в отдельном учебном Windows-стенде без рабочих сертификатов. Не серверный кластер под служебной учётной записью.

  2. Для будущей ручной подготовки отключите сеть учебного стенда. Все операции подписи локальны, TSA-массив в коде явно пуст. Команды создания ключа ниже НЕ выполнялись при подготовке справочника; они создают собственный сертификат и закрытый ключ в Личном текущего учебного Windows-пользователя.

  3. Откройте Windows PowerShell в своём отдельном учебном профиле, не от администратора рабочего сервера. В одной и той же консоли вручную выполните последовательно только следующие четыре команды; не закрывайте её между командами. Провайдер Microsoft Enhanced RSA and AES Cryptographic Provider должен существовать и иметь Type24. CNG-провайдер по умолчанию не подставлять вместо него.

  4. Ручная команда 1: $pdfTrainingId = [guid]::NewGuid().ToString("D")

  5. Ручная команда 2: $pdfTrainingCert = New-SelfSignedCertificate -Type Custom -Subject ("CN=Syntax Guide PDF Own85 " + $pdfTrainingId) -Provider "Microsoft Enhanced RSA and AES Cryptographic Provider" -KeySpec Signature -KeyAlgorithm RSA -KeyLength 2048 -KeyUsage DigitalSignature -HashAlgorithm SHA256 -KeyExportPolicy NonExportable -CertStoreLocation "Cert:\CurrentUser\My" -NotAfter (Get-Date).AddMonths(1)

  6. Ручная команда 3: if (-not $pdfTrainingCert.HasPrivateKey) { throw "Own training private key was not created" }

  7. Ручная команда 4: [pscustomobject]@{TrainingId=$pdfTrainingId;ThumbprintHex=$pdfTrainingCert.Thumbprint;OwnPrivateKey=$pdfTrainingCert.HasPrivateKey;Subject=$pdfTrainingCert.Subject}

  8. Сохраните показанные TrainingId и ThumbprintHex для этой попытки. OwnPrivateKey должен быть True, Subject должен содержать только Syntax Guide PDF Own85 и ваш новый TrainingId. Ключ не экспортируется в PFX и не печатается. Сертификат самоподписанный и не добавляется в доверенные корневые.

  9. Команды и различие legacy CSP/KeySpec=Signature сверены с официальной документацией Microsoft New-SelfSignedCertificate: https://learn.microsoft.com/en-us/powershell/module/pki/new-selfsignedcertificate?view=windowsserver2025-ps. Это документирование будущего учебного опыта, не выполненное создание ключа.

  10. Учебный сертификат и ключ остаются в собственном Windows-профиле после закрытия1С; урок их автоматически не удаляет. Администратор отдельного стенда отвечает за его очистку после обучения. Не переносите опыт в рабочий профиль и не выдавайте закрытие формы за удаление ключа.

  11. Ни PowerShell-команды, ни CryptoAPI/1С, ни AddSignature, ни GUI/сервер/ключи/подпись здесь не запускались. runtimeVerifiedScenarios=0.

  12. Используйте именно тонкий Windows-клиент собственной файловой базы8.5.1.1343. В конфигурации разрешите синхронные вызовы расширений платформы/внешних компонент: сведения, инициализация CryptoAPI и поиск своего сертификата здесь синхронные. Асинхронны PDF-операции; это не обещание полностью неблокирующего криптографического стека.

  13. Основной код теперь вставляется В МОДУЛЬ ФОРМЫ, не в серверный общий модуль. Своя управляемая форма: реквизиты МеткаСертификата85 Строка100, ОтпечатокHex85 Строка40, ПодтверждаюСвойСтенд Булево начально Ложь, Занято Булево начально Ложь, Отчёт Строка0. Поля с теми же путями; Занято не редактировать, Отчёт многострочный ТолькоПросмотр=Да. Команда/кнопка Выполнить, действие Выполнить. Вставьте весь основной модуль, сохранить/F7, открыть свою форму и скопировать фактические TrainingId/ThumbprintHex.

  14. В общий серверный модуль УчебнаяАсинхроннаяПодписьPDF85 вставьте ТОЛЬКО supporting-модуль ниже. Сервер=Да, Вызов сервера=Да, Клиент=Нет, Глобальный=Нет, Привилегированный=Нет. Он проверяет свою пустую конфигурацию и экспортирует только собственную таблицу в байты. Подписание/ключевой контейнер работают у своего текущего Windows-клиента, не у учётной записи рабочего сервера.

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

&НаКлиенте
Функция ПодготовитьСвойМенеджер()
    Информация = СредстваКриптографии.ПолучитьИнформациюМодуляКриптографии("Microsoft Enhanced RSA and AES Cryptographic Provider", "", 24);
    Если Информация = Неопределено Или ТипЗнч(Информация) <> Тип("ИнформацияМодуляКриптографии") Тогда
        ВызватьИсключение "На своём Windows-стенде не найден требуемый провайдер";
    КонецЕсли;
    Если Информация.ТипМодуляКриптографии <> 24 Тогда
        ВызватьИсключение "Нужен фактический CryptoAPI-провайдер типа24";
    КонецЕсли;
    АлгоритмSHA256 = "";
    Для Каждого Алгоритм Из Информация.АлгоритмыХеширования Цикл
        Если НРег(Алгоритм) = "sha256" Или НРег(Алгоритм) = "sha-256" Или Алгоритм = "2.16.840.1.101.3.4.2.1" Тогда
            АлгоритмSHA256 = Алгоритм;
        КонецЕсли;
    КонецЦикла;
    Если АлгоритмSHA256 = "" Тогда
        ВызватьИсключение "Свой провайдер не подтвердил SHA256; другое имя не подставлять наугад";
    КонецЕсли;
    Менеджер = Новый МенеджерКриптографии("Microsoft Enhanced RSA and AES Cryptographic Provider", "", 24, ИспользованиеИнтерактивногоРежимаКриптографии.НеИспользовать);
    Менеджер.АлгоритмХеширования = АлгоритмSHA256;
    Менеджер.ВключениеСертификатовВПодпись = РежимВключенияСертификатовКриптографии.ВключатьСертификатСубъекта;
    Менеджер.АдресаСерверовМетокВремени = Новый Массив;
    Если Менеджер.АдресаСерверовМетокВремени.Количество() <> 0 Тогда
        ВызватьИсключение "Урок не должен обращаться к внешнему серверу меток времени";
    КонецЕсли;
    Возврат Менеджер;
КонецФункции

&НаКлиенте
Функция НайтиСвойСертификат(Менеджер, Метка, ОтпечатокHex)
    Если СтрДлина(ОтпечатокHex) <> 40 Тогда
        ВызватьИсключение "Скопируйте весь SHA1-отпечаток из собственного PowerShell-опыта без пробелов";
    КонецЕсли;
    Отпечаток = ПолучитьДвоичныеДанныеИзHexСтроки(ОтпечатокHex);
    Если Отпечаток.Размер() <> 20 Тогда
        ВызватьИсключение "SHA1-отпечаток должен содержать20байт";
    КонецЕсли;
    Хранилище = Менеджер.ПолучитьХранилищеСертификатов(ТипХранилищаСертификатовКриптографии.ПерсональныеСертификаты,
        РасположениеХранилищаСертификатовКриптографии.ДанныеПользователяОС);
    Сертификат = Хранилище.НайтиПоОтпечатку(Отпечаток);
    Если Сертификат = Неопределено Или ТипЗнч(Сертификат) <> Тип("СертификатКриптографии") Тогда
        ВызватьИсключение "В собственном текущем Windows-профиле не найден именно созданный учебный сертификат";
    КонецЕсли;
    Если Base64Строка(Сертификат.Отпечаток) <> Base64Строка(Отпечаток) Тогда
        ВызватьИсключение "Найден не свой точный отпечаток";
    КонецЕсли;
    CN = Неопределено;
    Если Не Сертификат.Субъект.Свойство("CN", CN) Тогда
        ВызватьИсключение "Нет собственного CN учебного сертификата";
    КонецЕсли;
    Если CN <> "Syntax Guide PDF Own85 " + Метка Тогда
        ВызватьИсключение "CN должен совпасть с собственной уникальной меткой подготовки ключа";
    КонецЕсли;
    Возврат Сертификат;
КонецФункции

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

&НаКлиенте
Асинх Функция ПрочитатьСвоиБайтыАсинх(Данные)
    Поток = Данные.ОткрытьПотокДляЧтения();
    Попытка
        Документ = Новый ДокументPDF;
        Ждать Документ.ПрочитатьАсинх(Поток, "");
    Исключение
        ОшибкаОперации = ОписаниеОшибки();
        Попытка
            Поток.Закрыть();
        Исключение
            ОшибкаОперации = ОшибкаОперации + " Закрытие своего входного потока: " + ОписаниеОшибки();
        КонецПопытки;
        ВызватьИсключение ОшибкаОперации;
    КонецПопытки;
    Поток.Закрыть();
    Возврат Документ;
КонецФункции

&НаКлиенте
Асинх Функция ЗаписатьСвоиБайтыАсинх(Документ)
    Поток = Новый ПотокВПамяти;
    Попытка
        Ждать Документ.ЗаписатьАсинх(Поток, "", ТипФайлаДокументаPDF.PDF);
        Данные = Поток.ЗакрытьИПолучитьДвоичныеДанные();
    Исключение
        ОшибкаОперации = ОписаниеОшибки();
        Попытка
            Поток.Закрыть();
        Исключение
            ОшибкаОперации = ОшибкаОперации + " Закрытие своего выходного потока: " + ОписаниеОшибки();
        КонецПопытки;
        ВызватьИсключение ОшибкаОперации;
    КонецПопытки;
    Возврат Данные;
КонецФункции

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

&НаСервереБезКонтекста
Функция ПолучитьСвоиБайтыНаСервере()
    Возврат УчебнаяАсинхроннаяПодписьPDF85.ПолучитьСвоиБайтыPDF();
КонецФункции

&НаКлиенте
Асинх Процедура Выполнить(Команда)
    Если Занято Тогда
        ВызватьИсключение "Дождитесь завершения своей текущей операции";
    КонецЕсли;
    Система = Новый СистемнаяИнформация;
    Если (Система.ТипПлатформы <> ТипПлатформы.Windows_x86 И Система.ТипПлатформы <> ТипПлатформы.Windows_x86_64) Тогда
        ВызватьИсключение "Нужен собственный тонкий Windows-клиент8.5.1.1343";
    КонецЕсли;
    Если Не ПодтверждаюСвойСтенд Тогда
        ВызватьИсключение "Подтвердите только использование своего отдельного ключевого стенда";
    КонецЕсли;
    Метка = МеткаСертификата85;
    ОтпечатокHex = ОтпечатокHex85;
    UUID = Новый УникальныйИдентификатор(Метка);
    Если Строка(UUID) <> Метка Тогда
        ВызватьИсключение "Скопируйте фактическую метку собственного учебного сертификата";
    КонецЕсли;
    Занято = Истина;
    Отчёт = "Собственная операция ещё не завершена";
    Попытка
        ДанныеPDF = ПолучитьСвоиБайтыНаСервере();
        Документ = Ждать ПрочитатьСвоиБайтыАсинх(ДанныеPDF);
        Менеджер = ПодготовитьСвойМенеджер();
        Сертификат = НайтиСвойСертификат(Менеджер, Метка, ОтпечатокHex);
        ДоПодписания = Ждать Документ.ПолучитьОписанияПодписейАсинх(Менеджер);
        Если ТипЗнч(ДоПодписания) <> Тип("Массив") Или ДоПодписания.Количество() <> 0 Тогда
            ВызватьИсключение "Собственный исходный PDF не должен содержать прежних подписей";
        КонецЕсли;
        Описание = ПодготовитьСвоюПодпись(Документ, Сертификат, Метка);
        Штамп = Новый ТабличныйДокумент;
        Штамп.Область(1, 1).Текст = "УЧЕБНЫЙ АСИНХРОННЫЙ САМОПОДПИСАННЫЙ ОБРАЗЕЦ";
        Ждать Документ.ДобавитьПодписьАсинх(Менеджер, Описание, Штамп, Истина);
        ПодписанныеБайты = Ждать ЗаписатьСвоиБайтыАсинх(Документ);
        Если ПодписанныеБайты.Размер() = 0 Тогда
            ВызватьИсключение "Не получены собственные подписанные PDF-байты";
        КонецЕсли;
        Копия = Ждать ПрочитатьСвоиБайтыАсинх(ПодписанныеБайты);
        Описания = Ждать Копия.ПолучитьОписанияПодписейАсинх(Менеджер);
        Если ТипЗнч(Описания) <> Тип("Массив") Или Описания.Количество() <> 1 Тогда
            ВызватьИсключение "Из новых PDF-байтов должно вернуться одно описание собственной подписи";
        КонецЕсли;
        Подпись = Описания[0];
        Если ТипЗнч(Подпись) <> Тип("ОписаниеПодписиPDF") Или Подпись.Имя <> Описание.Имя Тогда
            ВызватьИсключение "Await описаний не вернул именно собственную добавленную подпись";
        КонецЕсли;
        ПроверитьСвойОтветСертификата(Подпись.Сертификат, Сертификат);
        ДатаПодписи = Подпись.Дата;
        Если ТипЗнч(ДатаПодписи) <> Тип("Дата") Или ДатаПодписи = Дата(1, 1, 1) Тогда
            ВызватьИсключение "В своём описании нет заполненной native-даты; подстановка текущей даты запрещена";
        КонецЕсли;
        ПроверенныйСертификат = Ждать Копия.ПроверитьПодписьАсинх(Описание.Имя, Менеджер);
        ПроверитьСвойОтветСертификата(ПроверенныйСертификат, Сертификат);
        ВсеСертификаты = Ждать Копия.ПроверитьПодписиАсинх(Менеджер);
        Если ТипЗнч(ВсеСертификаты) <> Тип("Массив") Или ВсеСертификаты.Количество() <> 1 Тогда
            ВызватьИсключение "Await всех подписей должен вернуть один включённый собственный сертификат";
        КонецЕсли;
        ПроверитьСвойОтветСертификата(ВсеСертификаты[0], Сертификат);
        Отчёт = "Все await завершены: собственная подпись и сертификаты проверены. Native-дата: " + Строка(ДатаПодписи)
            + ". Это не доверенная TSA-дата и не правовая оценка";
    Исключение
        Занято = Ложь;
        Отчёт = "Свой асинхронный опыт не завершён: " + ОписаниеОшибки();
        ВызватьИсключение Отчёт;
    КонецПопытки;
    Занято = Ложь;
КонецПроцедуры
УчебнаяАсинхроннаяПодписьPDF85

Конфигуратор → Общие модули → УчебнаяАсинхроннаяПодписьPDF85 → Модуль

Полный серверный общий модуль: защита своего пустого стенда и создание только своих unsigned PDF-байтов в памяти. Не выполнялся.

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

Функция СоздатьСобственныеБайтыPDF()
    Таблица = Новый ТабличныйДокумент;
    Таблица.КодЯзыка = "ru";
    Таблица.Область(1, 1).Текст = "СОБСТВЕННЫЙ УЧЕБНЫЙ ОБРАЗЕЦ";
    Таблица.Область(2, 1).Текст = "Книга";
    Таблица.Область(2, 2).Текст = "3";
    Таблица.Область(2, 3).Текст = "250";
    Таблица.Область(2, 4).Текст = "750";
    Таблица.Область(1, 1, 2, 4).ЦветФона = Новый Цвет(255, 255, 210);
    Таблица.Область(, 1, , 1).ШиринаКолонки = 45;
    Поток = Новый ПотокВПамяти;
    Попытка
        Таблица.Записать(Поток, ТипФайлаТабличногоДокумента.PDF);
        Данные = Поток.ЗакрытьИПолучитьДвоичныеДанные();
    Исключение
        Поток.Закрыть();
        ВызватьИсключение;
    КонецПопытки;
    Возврат Данные;
КонецФункции

Функция ПолучитьСвоиБайтыPDF() Экспорт
    ПроверитьСвойПустойСтенд();
    Возврат СоздатьСобственныеБайтыPDF();
КонецФункции

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

  1. Клиент проверяет точное имя своей конфигурации, версию/Windows и подтверждение учебного стенда. Supporting-серверный модуль отдельно проверяет отсутствие рабочих объектов/расширений ДО экспорта собственных байтов. Серверная защита не скопирована в контекст тонкого клиента.

  2. Свои байты получают полный реальный источник: собственная таблица Книга/3/250/750 → PDF в ПотокВПамяти на сервере → ДвоичныеДанные в свою форму. Клиент открывает собственный поток чтения и ждёт ПрочитатьАсинх до его закрытия.

  3. Полные клиентские функции проверяют настоящий CryptoAPI Type24/SHA256 и собственный CurrentUser-сертификат по20байтам отпечатка и CN с UUID. ВключатьСертификатСубъекта задан явно, TSA-массив пуст. Файл/ключ с неизвестным источником не принимается.

  4. До подписи Ждать ПолучитьОписанияПодписейАсинх возвращает настоящий Массив: требуется0. Promise без Ждать не имеет готового количества; нельзя сравнить сам Promise с пустым массивом.

  5. Собственное описание проверяет реальную первую страницу и поля, получает свой сертификат/имя и прямоугольник. Ждать ДобавитьПодписьАсинх ждёт завершения создания подписи; никакого нового готового ДокументPDF этот результат не обещает.

  6. Новый выходной ПотокВПамяти передаётся ЗаписатьАсинх с ЯВНЫМ ПУСТЫМ паролем и PDF-форматом. Native-контракт запрещает запись подписанного PDF с непустым паролем. Только после Ждать поток закрывается с получением новых байтов.

  7. Подписанные байты читаются новым независимым ДокументPDF через второй awaited ПрочитатьАсинх. ПолучитьОписанияПодписейАсинх у Копия должен вернуть1 ДО индекса0; сверяются реальный тип, имя, свой отпечаток и readonly native-дата.

  8. Ждать ПроверитьПодписьАсинх(Имя, Менеджер) возвращает СертификатКриптографии. В отличие от синхронного overload, третьего выходного параметра нет. Полученный сертификат проверяется по типу/своему отпечатку, не по непустому представлению.

  9. Ждать ПроверитьПодписиАсинх(Менеджер) возвращает Массив сертификатов. В отличие от синхронного overload, второго выходного параметра нет. Проверяем Array/количество1 ДО индекса0 и тот же фактический отпечаток.

  10. Потоки чтения закрываются после завершившегося await; при исключении ошибка закрытия добавляется к исходной ошибке, не превращает её в успех. Выходной поток закрывается с байтами при успехе и отдельно на ошибке. Флаг Занято восстанавливается независимо от итогового статуса.

  11. Строка успеха появляется только ПОСЛЕ всех awaited операций и сравнений. Ошибка любого этапа формирует отчёт об ошибке и повторно выбрасывается. CA/отзыв/правовая сила и доверенная TSA-дата не обещаются.

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

  1. Будущий результат на полностью подготовленном своём тонком клиенте: исходных подписей0, из новых байтов возвращается1; фактическое имя/сертификат/дата совпадают с собственным источником, оба awaited метода проверки возвращают ожидаемые реальные типы. Занято в конце Ложь.

  2. Будущий отрицательный контроль: у ПроверитьПодписьАсинх временно передайте Описание.Имя + "_отсутствует". Promise должен завершиться ошибкой, отчёт успеха не появиться, Занято вернуться в Ложь. Не заменяйте имя другой подписью автоматически.

  3. Будущий контроль результатов: после Ждать проверки всех подписей временно замените ВсеСертификаты на Новый Массив. Проверка количества должна отказать ДО индекса0, несмотря на ранее завершённые операции.

  4. Будущий контроль источника: перед чтением Копия подайте ДанныеPDF вместо ПодписанныеБайты. Массив описаний будет0 и должен быть отвергнут. Верните новые подписанные байты после опыта.

  5. Будущий контроль повторного старта: вызовите собственную кнопку повторно, пока Занято=Истина. Второй запуск должен отказать до открытия потока/использования ключа. Не закрывайте форму для имитации отмены.

  6. runtimeVerifiedScenarios=0: BSL, тонкий клиент, ключи, подпись, PDF-потоки и GUI здесь не запускались. Успех статических проверок не удостоверяет завершение Promise на конкретном провайдере.

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

Все awaited PDF-операции завершены, из новых своих подписанных байтов получены и проверены реальные описание, дата и сертификаты; флаг занятости очищен, доверие/правовая сила не заявлены

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

  1. Это отдельный урок со своим закрытым ключом, не продолжение опыта с публичным DER Syntax Guide Training Only. Публичный DER из предыдущего урока не может заменить этот ключ.

  2. Создание учебного ключа — осознанное будущее изменение ТОЛЬКО своего изолированного профиля. Оно не выполнялось инструментами этого чата. NonExportable не означает, что ключ исчезнет после опыта или что он доверенный.

  3. Чтение описания/даты — не эквивалент ПроверитьПодпись, проверки CA/отзыва или доверенной TSA-метки. Никакого правового вывода по self-signed образцу не делается.

  4. Текст штампа — визуальное представление, не сама криптографическая подпись. Не обещается, что все PDF-просмотрщики доверяют self-signed сертификату или одинаково показывают штамп.

  5. Ключ принадлежит текущему учебному Windows-клиенту. Сервер создаёт только свои PDF-байты и ключ не использует; ошибка источника не разрешает переключиться на чужой профиль или область ДанныеКомпьютера.

  6. Новый ОписаниеПодписиPDF — настройки до подписи, не источник фактической readonly-даты. Её читаем только из awaited описаний новых подписанных PDF-байтов.

  7. Четыре PDF Async-метода доступны клиенту, не серверу. Синхронные методы подписания/чтения/проверки не вставлены вместо Async в основном модуле формы.

  8. Promise надо дождаться внутри Асинх Процедура/Функция. ПолучитьОписанияПодписейАсинх возвращает Array после ожидания, ПроверитьПодписьАсинх — Certificate, ПроверитьПодписиАсинх — Array; это разные контракты.

  9. Асинхронное ожидание — не фоновая серверная работа и не гарантия, что синхронная инициализация криптографии не блокирует интерфейс. Разрешённый синхронный режим указан явно; браузерные расширения не устанавливаются.

  10. Пустой пароль записи обязателен для этого подписанного образца. Непустой пароль должен вызвать native-ошибку; её нельзя скрыть и вернуть старые байты как новый результат.

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

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

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

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

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