Создает описание подписи.
Синтаксис
Новый ОписаниеПодписи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.
Подготовка
Создайте новую отдельную изолированную Windows VM, отдельного только учебного Windows-пользователя и новую пустую файловую ИБ платформы ровно8.5.1.1343. Имя корня конфигурации УчебнаяПоследовательнаяПодписьPDF85, русский встроенный язык, Такси, совместимость Не использовать. Рабочие пользователи, базы и реальные ключи запрещены.
В этой конфигурации не должно быть предметных объектов, регистров, заданий, внешних источников или подписок; не устанавливайте расширения. Полный server guard каждого модуля проверяет metadata/три источника расширений/версию/транзакцию.
Создайте собственную внешнюю обработку УчебнаяПоследовательнаяПодписьPDF85 с основной управляемой формой. Три урока самостоятельны: выбирайте целый sync, Ждать или callback модуль, не склеивайте одноименные процедуры.
Реквизиты формы: UUIDКомплекта85 Строка36; ОжидаемыйОтпечаток85 Строка40; КаталогДляКомплекта85 Строка0, ТолькоПросмотр; Статус85 Строка0, многострочный ТолькоПросмотр; ПодтверждаюУчебнуюVM85 Булево исходноЛожь. Назначьте ПриСозданииНаСервере и ПриОткрытии из полного кода. Добавьте команду ВыполнитьКрипто85 с одноименным обработчиком/кнопкой, ИзменяетСохраняемыеДанныеЛожь.
Для ВСЕХ трех PDF-уроков создайте ПодтверждаюСвоеRootДоверие85 Булево исходноЛожь. Own Root trust нужен как явно подготовленное локальное условие; математическая подпись PDF, цепочка, срок и отзыв — разные проверки.
Запускайте обработку только толстым управляемым Windows-клиентом8.5.1.1343 под тем же новым учебным пользователем. Включите режим синхронных вызовов: даже Ждать/callback варианты имеют синхронную подготовку DER/провайдера/собственных хранилищ. Нельзя переносить ключи/менеджер на сервер.
В модуль своей формы вставьте весь основной код выбранного урока. Он проверяет собственный локальный DER и legacy CSP, реальные имена SHA256/RSA/AES256 из возможностей; не выбирает случайный персональный сертификат.
В своей новой 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.
Закрытый ключ NonExportable, KeyProtectionNone — только учебный. Скрипт не устанавливает Root-доверие и не экспортирует private key. Запишите UUID/публичный thumbprint; UUID введите в форму без изменения регистра, а40hexсимволов thumbprint — в ОжидаемыйОтпечаток85. Native Отпечаток имеет тип ДвоичныеДанные/SHA1, поэтому в модуле он переводится в hex перед сравнением с PowerShell. CN субъекта/издателя DER обязательно равен OwnCrypto85-UUID. Не создавайте новые комплекты без проверки результатов прежней подготовки.
Для проверки сертификата сохраните ОТДЕЛЬНЫЙ полный 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 и локальную одноэлементную цепочку. Повторный импорт/замена запрещены.
Отключите внешнюю сеть только у своей VM через ее настройки до учебных запусков. Собственный selfsigned не имеет CDP/AIA/TSA адресов, проверки отзыва явно пропускаются; это отдельная ограниченная локальная модель доверия, не production политика безопасности.
После отдельного ручного own-DER/UUID/thumbprint Root-этапа включите дополнительное подтверждение формы. Не используйте AllowTest/IgnoreTime/IgnoreSignature ради результата. Собственный ROOT не превращает сертификат в сертификат квалифицированногоУЦ; никаких TSA/OCSP/CRL гарантий.
Не закрывайте форму/клиент во время ожидания или callback-цепочки. Повторная команда отказывает по Занято85. Успех выставляется только после всех стадий/отрицательных тестов; закрытие процесса не гарантирует выполнение финального callback.
Ручная подготовка оставляет свой сертификат/ключ, а trust-этап еще и свой Root; автоматического удаления/отката нет. Никакие ключи, подписи, хранилища, сети или 1С нами здесь не запускались и не менялись. Все результаты до вашего ручного опыта — ожидаемые, runtime0.
PDF-исток каждый раз реально создается в памяти из собственного ТабличныйДокумент: OWN PDF85, Книга/3/250/750 и свойUUID. Проверяются1страница,0вложений, ориентация0 и фактические размеры для штампа10/45/70/15мм. Не требуется какой-либо готовый неизвестный PDF.
ЗаписьPDF и ЧтениеPDF работают с собственными потоками в памяти. После signer закрывается именно writer, затем его поток дает новые подписанные байты; для reader открывается новый независимый поток чтения. Не используйте исходный неподписанный reader как доказательство результата.
Штамп только учебный: УЧЕБНАЯ ПОДПИСЬ — OWN PDF85. Нативные успешные проверки подписи/DER не доказывают вид штампа: для visualQA пользователь может отдельно посмотреть свою выгруженную копию, здесь файл/просмотр не создавался.
У Чтение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()
}
Разбор по шагам
Подготовьте свою новую Windows VM, точный own UUID/CN/publicDER/SHA1hex/private-CSP-key и отдельно own Root. Полный модуль формы проверяет пустую базу/версию/все источники расширений; внешняя сеть VM отключена.
Команда создает реальный собственный PDF из ТабличныйДокумент. Парсинг первой страницы доказывает реальные размеры/ориентацию/0вложений; input не приходит от неизвестного файла или процесса.
Первый ЧтениеPDF читает исходные bytes и получает массив описаний0. Само отсутствие подписей не выдается за successful VerifyAll: verification здесь еще не выполняется.
Закройте source-reader перед созданием отдельного writable ПотокВПамяти из собственного PDF-буфера. ЗаписьPDF принимает этот поток и actual ОписаниеПодписиPDF с собственным сертификатом/уникальным именем/координатами.
Ждать ЗаписатьПодписьАсинх возвращает Неопределено; проверяем это. Promise не является подписанными bytes или Bool.
Закрываем writer ДО его потока; поток возвращает новые actual bytes, отличные от source. Новый reader на новом read-stream получает фактический массив описаний1, точное own имя и включенный own DER.
Ждать ПроверитьПодписьАсинх(name,,manager) возвращает сертификат; Ждать ПроверитьПодписиАсинх(manager) — массив. Старые optional выходные параметры не выдаются за результаты promises.
Отрицательное имя = реально прочитанное own имя + _missing. Оно отсутствует в подтвержденном единственном описании. Допускаем native исключение либо отсутствие сертификата; какой-либо возвращенный сертификат означает провал. Не обещаем исключение для undocumented missing-name поведения.
Ресурсы закрываются в порядке reader/writer → соответствующие streams также при ошибке. Успех запрещен при ошибке закрытия; crypto-пароль сбрасывается. Ключ/root/cert из VM автоматически не удаляются.
В callback-варианте UUID запуска и точная стадия проверяются до каждого перехода; только Missing может считаться ожидаемой native error-стадией, остальные ошибки дают неподтвержденный результат.
Итог различает реальную присоединенную PDF-подпись и внешний вид учебного штампа. Не подтверждаются отзыв/TSA/OCSP, юридическая квалификация или внешняя CA-цепочка.
Как проверить результат
До подписи0 описаний, после нового чтения1 own описание; name/DER происходят из фактического подписанного PDF.
Одиночная и полная проверки подтверждают exact own DER; неизмененные исходные bytes за signed output не принимаются.
Отсутствующее name не дает подтвержденного сертификата. Native throw/Undefined не смешивается с positive verify result.
Правильные арности/позиции sync, Ждать и callback, в частности writer callback1argument и ошибка3arguments.
Все свои reader/writer/streams закрыты; отсутствие actual запуска четко обозначено. Trust/ключи только manual новая VM.
Ожидаемый результат
Ожидаемый будущий ручной результат: unsigned0→signed1, actual own имя/DER, verify one/all, missing без сертификата, закрытые ресурсы. Native/source тесты не являются runtime-проверкой; actualsigning0.
На что обратить внимание
ЧтениеPDF и ДокументPDF — разные API. У обоих ПроверитьПодпись, но выходной сертификат и менеджер имеют различный позиционный порядок; этот урок проверяет именно sequential reader.
После ЗаписьPDF.ЗаписатьПодпись content/attachments нельзя модифицировать. Здесь после signer выполняется только закрытие и новое независимое чтение.
Reader verification не является обещанием TSA/qualified-signature. Свой explicit Root только локальное учебное доверие, отзыв не подготовлен.
Внешний вид видимого штампа не подтвержден статикой: размер и позиция основаны на реальной странице, но просмотр требует отдельного пользовательского ручного контроля.
Пример не запускался в вашей базе. Перед применением проверьте его в указанном учебном контексте.
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.
Подготовка
Создайте новую отдельную изолированную Windows VM, отдельного только учебного Windows-пользователя и новую пустую файловую ИБ платформы ровно8.5.1.1343. Имя корня конфигурации УчебнаяПоследовательнаяПодписьPDF85, русский встроенный язык, Такси, совместимость Не использовать. Рабочие пользователи, базы и реальные ключи запрещены.
В этой конфигурации не должно быть предметных объектов, регистров, заданий, внешних источников или подписок; не устанавливайте расширения. Полный server guard каждого модуля проверяет metadata/три источника расширений/версию/транзакцию.
Создайте собственную внешнюю обработку УчебнаяПоследовательнаяПодписьPDF85 с основной управляемой формой. Три урока самостоятельны: выбирайте целый sync, Ждать или callback модуль, не склеивайте одноименные процедуры.
Реквизиты формы: UUIDКомплекта85 Строка36; ОжидаемыйОтпечаток85 Строка40; КаталогДляКомплекта85 Строка0, ТолькоПросмотр; Статус85 Строка0, многострочный ТолькоПросмотр; ПодтверждаюУчебнуюVM85 Булево исходноЛожь. Назначьте ПриСозданииНаСервере и ПриОткрытии из полного кода. Добавьте команду ВыполнитьКрипто85 с одноименным обработчиком/кнопкой, ИзменяетСохраняемыеДанныеЛожь.
Для ВСЕХ трех PDF-уроков создайте ПодтверждаюСвоеRootДоверие85 Булево исходноЛожь. Own Root trust нужен как явно подготовленное локальное условие; математическая подпись PDF, цепочка, срок и отзыв — разные проверки.
Запускайте обработку только толстым управляемым Windows-клиентом8.5.1.1343 под тем же новым учебным пользователем. Включите режим синхронных вызовов: даже Ждать/callback варианты имеют синхронную подготовку DER/провайдера/собственных хранилищ. Нельзя переносить ключи/менеджер на сервер.
В модуль своей формы вставьте весь основной код выбранного урока. Он проверяет собственный локальный DER и legacy CSP, реальные имена SHA256/RSA/AES256 из возможностей; не выбирает случайный персональный сертификат.
В своей новой 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.
Закрытый ключ NonExportable, KeyProtectionNone — только учебный. Скрипт не устанавливает Root-доверие и не экспортирует private key. Запишите UUID/публичный thumbprint; UUID введите в форму без изменения регистра, а40hexсимволов thumbprint — в ОжидаемыйОтпечаток85. Native Отпечаток имеет тип ДвоичныеДанные/SHA1, поэтому в модуле он переводится в hex перед сравнением с PowerShell. CN субъекта/издателя DER обязательно равен OwnCrypto85-UUID. Не создавайте новые комплекты без проверки результатов прежней подготовки.
Для проверки сертификата сохраните ОТДЕЛЬНЫЙ полный 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 и локальную одноэлементную цепочку. Повторный импорт/замена запрещены.
Отключите внешнюю сеть только у своей VM через ее настройки до учебных запусков. Собственный selfsigned не имеет CDP/AIA/TSA адресов, проверки отзыва явно пропускаются; это отдельная ограниченная локальная модель доверия, не production политика безопасности.
После отдельного ручного own-DER/UUID/thumbprint Root-этапа включите дополнительное подтверждение формы. Не используйте AllowTest/IgnoreTime/IgnoreSignature ради результата. Собственный ROOT не превращает сертификат в сертификат квалифицированногоУЦ; никаких TSA/OCSP/CRL гарантий.
Не закрывайте форму/клиент во время ожидания или callback-цепочки. Повторная команда отказывает по Занято85. Успех выставляется только после всех стадий/отрицательных тестов; закрытие процесса не гарантирует выполнение финального callback.
Ручная подготовка оставляет свой сертификат/ключ, а trust-этап еще и свой Root; автоматического удаления/отката нет. Никакие ключи, подписи, хранилища, сети или 1С нами здесь не запускались и не менялись. Все результаты до вашего ручного опыта — ожидаемые, runtime0.
PDF-исток каждый раз реально создается в памяти из собственного ТабличныйДокумент: OWN PDF85, Книга/3/250/750 и свойUUID. Проверяются1страница,0вложений, ориентация0 и фактические размеры для штампа10/45/70/15мм. Не требуется какой-либо готовый неизвестный PDF.
ЗаписьPDF и ЧтениеPDF работают с собственными потоками в памяти. После signer закрывается именно writer, затем его поток дает новые подписанные байты; для reader открывается новый независимый поток чтения. Не используйте исходный неподписанный reader как доказательство результата.
Штамп только учебный: УЧЕБНАЯ ПОДПИСЬ — OWN PDF85. Нативные успешные проверки подписи/DER не доказывают вид штампа: для visualQA пользователь может отдельно посмотреть свою выгруженную копию, здесь файл/просмотр не создавался.
У Чтение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()
}
Разбор по шагам
Подготовьте свою новую Windows VM, точный own UUID/CN/publicDER/SHA1hex/private-CSP-key и отдельно own Root. Полный модуль формы проверяет пустую базу/версию/все источники расширений; внешняя сеть VM отключена.
Команда создает реальный собственный PDF из ТабличныйДокумент. Парсинг первой страницы доказывает реальные размеры/ориентацию/0вложений; input не приходит от неизвестного файла или процесса.
Первый ЧтениеPDF читает исходные bytes и получает массив описаний0. Само отсутствие подписей не выдается за successful VerifyAll: verification здесь еще не выполняется.
Закройте source-reader перед созданием отдельного writable ПотокВПамяти из собственного PDF-буфера. ЗаписьPDF принимает этот поток и actual ОписаниеПодписиPDF с собственным сертификатом/уникальным именем/координатами.
НачатьЗаписьПодписи получает Менеджер первым, Оповещение вторым, Описание третьим и настоящий ТабличныйДокумент штампа четвертым. Success callback имеет только К, не Результат/К.
Закрываем writer ДО его потока; поток возвращает новые actual bytes, отличные от source. Новый reader на новом read-stream получает фактический массив описаний1, точное own имя и включенный own DER.
НачатьПроверкуПодписи(callback,name,manager) передает Сертификат/К; НачатьПроверкуПодписей(callback,manager) — Массив/К. Обе completion-процедуры экспортируемы, результаты сверяются с actual own DER.
Отрицательное имя = реально прочитанное own имя + _missing. Оно отсутствует в подтвержденном единственном описании. Допускаем native исключение либо отсутствие сертификата; какой-либо возвращенный сертификат означает провал. Не обещаем исключение для undocumented missing-name поведения.
Ресурсы закрываются в порядке reader/writer → соответствующие streams также при ошибке. Успех запрещен при ошибке закрытия; crypto-пароль сбрасывается. Ключ/root/cert из VM автоматически не удаляются.
В callback-варианте UUID запуска и точная стадия проверяются до каждого перехода; только Missing может считаться ожидаемой native error-стадией, остальные ошибки дают неподтвержденный результат.
Итог различает реальную присоединенную PDF-подпись и внешний вид учебного штампа. Не подтверждаются отзыв/TSA/OCSP, юридическая квалификация или внешняя CA-цепочка.
Как проверить результат
До подписи0 описаний, после нового чтения1 own описание; name/DER происходят из фактического подписанного PDF.
Одиночная и полная проверки подтверждают exact own DER; неизмененные исходные bytes за signed output не принимаются.
Отсутствующее name не дает подтвержденного сертификата. Native throw/Undefined не смешивается с positive verify result.
Правильные арности/позиции sync, Ждать и callback, в частности writer callback1argument и ошибка3arguments.
Все свои reader/writer/streams закрыты; отсутствие actual запуска четко обозначено. Trust/ключи только manual новая VM.
Ожидаемый результат
Ожидаемый будущий ручной результат: unsigned0→signed1, actual own имя/DER, verify one/all, missing без сертификата, закрытые ресурсы. Native/source тесты не являются runtime-проверкой; actualsigning0.
На что обратить внимание
ЧтениеPDF и ДокументPDF — разные API. У обоих ПроверитьПодпись, но выходной сертификат и менеджер имеют различный позиционный порядок; этот урок проверяет именно sequential reader.
После ЗаписьPDF.ЗаписатьПодпись content/attachments нельзя модифицировать. Здесь после signer выполняется только закрытие и новое независимое чтение.
Reader verification не является обещанием TSA/qualified-signature. Свой explicit Root только локальное учебное доверие, отзыв не подготовлен.
Внешний вид видимого штампа не подтвержден статикой: размер и позиция основаны на реальной странице, но просмотр требует отдельного пользовательского ручного контроля.
Пример не запускался в вашей базе. Перед применением проверьте его в указанном учебном контексте.