Задания инкрементального подписания PFX
Операция sign поддерживает проверенный инкрементальный профиль, который сохраняет точную исходную ревизию PDF и каждую существующую криптографическую подпись, отдавая отдельно подписанный артефакт
Выбор профиля
{"schemaVersion":1,"type":"sign","profile":"pfx-incremental-document",
"pfxFile":"C:/keys/signer.pfx","pfxPassword":"password",
"fieldName":"ApprovalTwo","existingField":true,
"budget":{"memoryBytes":268435456,"outputBytes":134217728,
"resultBytes":16777216,"timeMilliseconds":60000,
"objectCount":1000000,"pageCount":1024}}
Профиль pfx-unsigned-document по умолчанию сохраняет прежнее поведение и принимает источники без полей подписи; он создаёт новое поле по page, x1, y1, x2 и y2 и восстанавливает граф источника перед публикацией
pfx-incremental-document выбирает новый профиль; incremental: true — эквивалентный селектор, если profile опущен, и конфликтует с явно заданным другим профилем
existingField: true заполняет именованное пустое поле подписи, не добавляя ещё одно поле; иначе операция создаёт новое поле, а page и прямоугольник идентифицируют его виджет
По умолчанию fieldName — Signature1 и принимает от 1 до 1024 байт UTF-8; existingField требует инкрементальный профиль, совпадающее поле подписи и отсутствующее или null-значение V
Read-only бит 1 в Ff и некорректные или вне диапазона целочисленные флаги отвергаются до подготовки; унаследованные словари SV и Lock разрешаются по уникальным семантическим именам PDF с ограниченным обходом родителей
Seed-значения и блокировки полей
digestMethod выбирает реальный дайджест CMS SHA256, SHA384 или SHA512; по умолчанию SHA256, а cades: true выбирает ETSI.CAdES.detached и включает атрибут signing-certificate-v2, привязывающий реальный сертификат подписанта
reason, location и contactName — строки UTF-8 в JSON, записываемые как текстовые строки Unicode PDF; обязательные seed-причины сравниваются по их значениям в Unicode, включая специальное значение из одной точки, которое требует опущенного reason
Обязательные ограничения SV проверяют реальный обработчик, SubFilter CMS, поддерживаемую версию подписания, дайджест, причину, разрешение сертификации, юридическую аттестацию и режим LockDocument; обязательный timestamp использует транспорт приложения и workflow явного доверия из таймстампов подписания Windows, обязательные подписанные свидетельства отзыва — аутентифицированное подписание CRL и OCSP, а обязательные именованные appearances — реально генерируемые пресеты appearance подписи
Обязательные свойства SVCert до подписания проверяют реальный листовой сертификат PFX и включённую цепочку издателей, разрешённые байты сертификата, OID политик сертификата, биты key usage и атрибуты subject DN в Unicode
credentialSourceURL передаёт заявленный приложением источник credential для обязательного URL в SVCert; библиотека сравнивает объявленный URL и поддерживаемый URLType, не открывая эту конечную точку и не аутентифицируя транспортную идентичность
Блокировки полей All, Include и Exclude записываются как косвенный словарь SigFieldLock и согласованная ссылка FieldMDP, привязанная к Catalog документа; имена полей — текстовые строки Unicode PDF, и в зашифрованных документах они остаются зашифрованными
Подписанное значение P в Lock ограничивает последующие ревизии по исходной подписанной политике поля, независимо от каких-либо разрешений DocMDP; разрешённые вторые подписи сохраняют точную предыдущую ревизию и CMS, а заблокированное поле или изменение P1 отвергается до публикации
Поиск политики декодирует эскейпы имён PDF ровно один раз, включая унаследованные имена Parent; словари с дублирующимися семантическими именами, невалидные или циклические родители отвергаются до публикации, а валидные эскейпированные имена сохраняют исходные байты
contentsBytes по умолчанию 16384 и принимает от 1024 до 1048576; reason, location и contactName заполняют новый словарь подписи
Сертификация и юридические аттестации
certificationPermission по умолчанию 0 для подписания утверждения (approval); значения 1, 2 или 3 создают сертифицирующую подпись с запрошенным разрешением DocMDP, привязывают Catalog Perms DocMDP к реальному косвенному словарю подписи и сохраняют любой независимый трансформ FieldMDP
Сертификация должна быть первой подписью, и в документе допустима только одна сертифицирующая подпись; некорректные разрешения Catalog, неразрешённые ссылки и значения DocMDP без привязки к исходному подписанному полю отвергаются
legalAttestation — опциональная строка Unicode для сертифицирующего подписания, записывается в Catalog Legal Attestation; обязательный список LegalAttestation в SV должен содержать выбранный текст, а пустой обязательный список требует его опустить
Существующие сертифицирующие подписи P2 и P3 допускают последующее подписание утверждения, если это позволяют блокировки полей и политика ревизий; подписи P1 и заблокированные поля отвергают изменения до публикации бинарных данных
Контракт источника
Инкрементальный профиль задания принимает сохранённый исходный источник, включая поддерживаемые зашифрованные PDF, и немодифицированный загруженный граф объектов; для зашифрованного источника укажите password в операции sign — отдельно от pfxPassword
Пароль источника аутентифицирует независимый сырой снимок, инициализирует исходный контекст шифрования и заново открывает подписываемого кандидата; полученная инкрементальная ревизия сохраняет исходный префикс шифротекста, словарь безопасности и постоянный ID документа
Расшифровка может пометить объекты dirty или убрать словарь безопасности из расшифрованного графа; операция сверяет эти состояния и ограниченные сериализации объектов со свежей аутентифицированной базой источника, отвергая реальные изменения графа и все сброшенные состояния объектов
Для user-пароля существующее поле подписи требует разрешения form-fill или аннотаций, а новый виджет подписи — разрешения аннотаций; аутентифицированный owner-пароль разрешает любой из вариантов с учётом существующих политик подписей
SaveLoadedDocumentToStream может изменить состояние сериализации; подписывая сохранённый или отредактированный граф, перезагрузите сохранённые байты в новый хэндл и подписывайте эту ревизию
Держите хранилище исходного источника стабильным всё время жизни хэндла; файловые задания запрещают совместную запись, пока загружают и готовят свой снимок
Исходный хэндл сохраняет количество полей, идентичность существующих полей, значения форм, байты источника и прежние настройки запуска и декодирования; любой успешный возврат подписания даёт documentUpdated: false, а неудачи оставляют хэндл пригодным для запросов, сохранения или последующего корректного запроса
Файловые задания отвергают выходной путь, совпадающий с входным файлом или алиасящий его, включая жёсткие ссылки; прямые stream API отвергают распознаваемые алиасы исходного потока и исходного файла, а вызывающий код обязан держать хранилище вывода callback'ов отдельно от входного хранилища
Подготовка и валидация
Операция копирует сырые байты источника в монопольно созданный временный снимок, открывает независимый инкрементальный клон, дописывает placeholder через ограниченный SaveIncrementalUpdate и заполняет detached CMS нативным стримовым подписантом PFX
PFX остаётся открытым с запретом совместной записи от предварительной проверки размера до подписания; временные файлы создаются с CREATE_NEW и удаляются, только если операция сама их создала
Подписанный кандидат должен содержать точный байтовый префикс источника и ожидаемые количества полей формы и подписи; новая подпись должна проверяться в выбранном поле, каждая ранее заполненная подпись должна по-прежнему проверяться, а анализ ревизий каждой старой подписи должен принять ограничения DocMDP, FieldMDP, usage-rights и идентичности
Незаполненные старые поля сохраняются и не учитываются в preservedSignatures; некорректные, невалидные или нарушающие политику существующие подписи приводят к отказу до публикации бинарных данных
Проверка существующих подписей сверяет целостность CMS с подписанными байтами и нативной политикой ревизий; если для новой подписи запрошен timestamp, его ответ отдельно проверяет явное доверие к TSA и настроенные свидетельства отзыва до публикации
Бюджеты и публикация
Фактический потолок вывода — min(outputBytes, memoryBytes / 8), включая весь префикс источника, дописанные объекты, xref и резерв под подпись; effectiveOutputLimit сообщает применённый потолок
Неподписанный кандидат и подписанный вывод ограничиваются этим потолком по отдельности, байты PFX — потолком memoryBytes / 8, а бюджеты декодирования базы источника, клона и кандидата отдельно ограничены memoryBytes / 8 с порогами сброса в файл не выше 1 МиБ
Это бюджеты операции и буферов, а не жёсткая гарантия RSS процесса; криптография, метаданные объектов, анализ ревизий и уже загруженный вызывающим источник могут потребовать дополнительной резидентной памяти
Проверки отмены и затраченного времени покрывают копирование источника, сериализацию и копирование дельты, чтения кандидата, чтения при проверке подписей, чтения при анализе ревизий и публикацию через callback; «липкие» сбои нативных потоков сохраняют свою категорию бюджета или отмены на границе задания
Бинарный вывод подготавливается и проверяется до первого output callback'а, а размер JSON-результата проверяется до публикации бинарных данных; сам вывод callback'ов не атомарен, поэтому неудавшийся callback бинарных данных или результата может оставить байты, которые вызывающий обязан отбросить
Файловые задания пишут в собственный кандидат в том же каталоге, сбрасывают его на диск, закрывают хэндлы источника и вывода и атомарно заменяют отдельный целевой файл после успешной валидации; при сбоях существующее назначение сохраняется
Успех возвращает mediaType: application/pdf, fieldName, profile, incremental, existingField, certificationPermission, revocationInfo, appearanceProfile, preservedSignatures, effectiveOutputLimit, outputBytes и documentUpdated: false
Приёмочный replay
$env:HOTPDF_SUPPRESS_AUTO_LAUNCH = '1'
./Tests/CABI/Run-IncrementalSigningAcceptance.ps1 -BuildDLL -CheckFPC
Раннер находит RAD Studio через её реестровый RootDir, принимает переопределения RADStudioRoot и PythonExecutable и использует настроенный тулчейн FPC при выбранном CheckFPC; FPCOnly повторяет только нативные цели, а OpenSSLWin32Library и OpenSSLWin64Library выбирают соответствующие рантайм-библиотеки для каждой архитектуры
Не-GUI раннер проверяет нативные API Win32 и Win64 и реальные C ABI DLL: частичные callback'и, существующие пустые поля, прежние подписи, обязательные seed-значения, реальные CAdES SHA384 и SHA512, метаданные в Unicode, разрешённые и заблокированные вторые подписи, реальные свойства сертификата PFX, трансформы блокировки полей AES-128 и AES-256, ограничения разрешений и owner-креденшалы, неверные пароли, dirty-графы после расшифровки, точные снимки зашифрованных источников, восстановление шифрованной записи на месте, реальные CMS-фикстуры DocMDP P1 и P2, бюджеты, отмену, сбои callback'ов и повторное использование источника после сбоев
Независимые проверки pypdf, MuPDF и OpenSSL доказывают точность прежних префиксов и диапазонов байтов, неизменность шифрования и постоянных ID, поисковость исходного текста, валидность старых и новых значений CMS, исключение шифрования для Contents подписи и отвержение изменённого подписанного содержимого; independent-proof.json записывает диапазоны байтов и результаты доказательств
Смежные темы
Операции документа, CopyLoadedSourceToStream, SaveIncrementalUpdate, EHPDFIncrementalOutputBudget