Enterprise редакция
Доверенные списки — глубокий справочник
Коротко
Заголовок раздела «Коротко»Эта страница — глубокий справочник по поверхности доверенных списков в NextPDF Enterprise. Поверхность — это двенадцать публичных классов пространства имён NextPDF\Enterprise\Security\Tsl. NextPDF\Enterprise\Security\Tsl\TslPolicyEnforcer — оркестрированная точка входа: он возвращает TslDocument только тогда, когда проходят все этапы — HTTP-загрузка, проверка XMLDSig, структурный разбор и шлюз устаревания nextUpdate. Затем TslTrustAnchorProvider::buildBundle() выводит набор якорей доверия из активных служб CA/QC, повторно подтверждая актуальность на переданный вызывающей стороной момент времени до извлечения любого якоря. Каждый сбой поднимает типизированное исключение; ни один этап не деградирует молча. Конвейер поддерживает проверку доверенных списков государств-членов ЕС и якорей доверия из LOTL (List of Trusted Lists), когда они предоставлены вызывающей стороной; автоматическое обнаружение LOTL, опрос и обработка pivot вне области действия.
Доступность и лицензирование
Заголовок раздела «Доступность и лицензирование»Эта возможность поставляется в NextPDF Enterprise (nextpdf/enterprise) и активируется лицензионным конвертом уровня Enterprise. Развёртывание без этого права не загружает классы возможности. Сравните редакции и получите лицензию.
Публичная поверхность API
Заголовок раздела «Публичная поверхность API»| Символ | Параметры | Поведение по умолчанию | Возвращает | Бросает или завершается с | Примечания |
|---|---|---|---|---|---|
TslPolicyEnforcer | TslFetcher $fetcher, TslSignatureVerifier $verifier, TslXmlParser $parser | Объединяет загрузку, проверку подписи, разбор и шлюз устаревания в одну точку входа | — | Пробрасывает исключения конвейера ниже | final; по построению отказывает в закрытое состояние |
TslPolicyEnforcer::fetchAndVerify | string $url | Загружает один TSL, затем выполняет verifyXml() над байтами | TslDocument | TslFetchException, NetworkPolicyViolation, TslSignatureException, TslParseException | Возвращает только тогда, когда проходят все четыре этапа |
TslPolicyEnforcer::verifyXml | string $xml | Проверяет подпись, разбирает и отклоняет устаревший список | TslDocument | TslSignatureException, TslParseException | Устаревание оценивается относительно текущего системного времени |
TslFetcher | ClientInterface $httpClient, RequestFactoryInterface $requestFactory, ?CacheInterface $cache = null, int $defaultTtlSeconds = 3600, int $maxBytes = 16_777_216, NetworkPolicy $networkPolicy = NetworkPolicy::ONLINE | Загрузка TSL/LOTL только по HTTPS с кэшированием по ETag | — | — | final; защита от SSRF блокирует частные, петлевые, link-local и метаданные-хосты с защитой от DNS-rebinding |
TslFetcher::fetch | string $url | GET с ревалидацией If-None-Match; кэширует тело плюс ETag с настроенным TTL | string (сырые байты XML) | TslFetchException, NetworkPolicyViolation | Читает не более $maxBytes байт; при STRICT_OFFLINE отдаётся только кэшированное тело |
TslSignatureVerifier | array $trustAnchorsPem, int $clockTolerance = 0 | Верификатор XMLDSig, закреплённый на настроенных якорях доверия | — | InvalidArgumentException, когда список якорей пуст | final; списки разрешённого в ALLOWED_SIG_ALG и ALLOWED_DIGEST_ALG |
TslSignatureVerifier::verify | string $xml | Проверяет вложенную подпись XMLDSig с отказом в закрытое состояние | string (PEM сертификата подписанта) | TslSignatureException с машиночитаемым кодом причины | Сертификаты из KeyInfo сами по себе никогда не доверяются; подписант должен выстраивать цепочку до настроенного якоря |
TslXmlParser::parse | string $xml | Структурный разбор в TslDocument; не зависит от подписи | TslDocument | TslParseException | Отклоняет любой DOCTYPE в закрытое состояние до разбора; загружает с LIBXML_NONET; вызывающие стороны должны проверить перед тем, как доверять результату |
TslTrustAnchorProvider::buildBundle | TslDocument $tsl, DateTimeImmutable $now | Сначала подтверждает актуальность, затем собирает сертификаты активных служб CA/QC | EnterpriseCaTrustAnchorBundle | TslParseException | Шлюз актуальности предшествует любому извлечению якоря; пустой результат бросает исключение |
TslDocument | Восемь продвинутых readonly-свойств (см. ограждение конструктора) | Неизменяемый объект-значение разобранного TSL | — | — | final readonly; в исходнике помечен @api |
TslDocument::isStale | DateTimeImmutable $now | Сравнивает nextUpdate с $now после разбора UTC с отказом в закрытое состояние | bool | TslParseException | Требует явного обозначения Z или числового смещения |
TslDocument::assertFresh | DateTimeImmutable $now | Бросает, когда список устарел или nextUpdate неразбираем | void | TslParseException | Шлюз актуальности на границе потребителя |
TslDocument::servicesOfType | string $serviceTypeIdentifier | Фильтрует службы по URI типа службы ETSI | list<TspService> | Не бросает | — |
TslDocument::activeServices | — | Возвращает только службы в статусе granted | list<TspService> | Не бросает | Granted означает TspService::STATUS_GRANTED |
TspService | Восемь продвинутых readonly-свойств | Одна запись службы доверия внутри TSL | — | — | final readonly; константы для статуса и URI типов служб |
TspService::isGranted | — | Равенство статуса с URI granted | bool | Не бросает | — |
TspService::isQualifiedCa | — | Равенство типа с URI CA/QC | bool | Не бросает | — |
TspServiceQualifier | string $qualifierUri, string $criteriaListAssert = 'all', array $policyOidConditions = [], array $keyUsageConditions = [] | Один квалификатор службы ETSI с необязательными критериями | — | — | final readonly; константы FOR_ESIG, FOR_ESEAL, FOR_WSA, QSCD_STATEMENT, NO_QSCD |
EnterpriseCaTrustAnchorBundle | array $anchorsPem, string $bundleVersion, string $bundleSha256 | Набор закреплённых якорей; проверяет переданный дайджест против переданных якорей при построении | — | InvalidArgumentException | Получайте из buildBundle(); не конструируйте вручную; реализует TrustAnchorStoreInterface |
EnterpriseCaTrustAnchorBundle::containsFingerprint | string $anchorDerSha256Hex | Принадлежность якоря по hex SHA-256 над телом DER | bool | Не бросает | — |
EnterpriseCaTrustAnchorBundle::computeBundleSha256 | array $anchorsPem | Каноническая SHA-256 над конкатенацией PEM с нормализованными переводами строк | string | Не бросает | static |
TslFetchException | — | Сигнализирует о неудавшейся загрузке TSL | — | — | final; расширяет RuntimeException |
TslParseException | — | Сигнализирует о структурном сбое или сбое актуальности | — | — | final; расширяет RuntimeException |
TslSignatureException | string $reason, string $message | Сигнализирует о сбое проверки XMLDSig с кодом причины | — | — | final; публичное readonly $reason (см. коды причин ниже) |
TslPolicyEnforcer
public function fetchAndVerify(string $url): TslDocumentpublic function verifyXml(string $xml): TslDocumentTslFetcher
public function __construct( private readonly ClientInterface $httpClient, private readonly RequestFactoryInterface $requestFactory, private readonly ?CacheInterface $cache = null, private readonly int $defaultTtlSeconds = 3600, private readonly int $maxBytes = 16_777_216, private readonly NetworkPolicy $networkPolicy = NetworkPolicy::ONLINE,) {}
public function fetch(string $url): stringTslSignatureVerifier
public function __construct(private readonly array $trustAnchorsPem, private readonly int $clockTolerance = 0)
public function verify(string $xml): stringTslXmlParser
public function parse(string $xml): TslDocumentTslTrustAnchorProvider
public function buildBundle(TslDocument $tsl, DateTimeImmutable $now): EnterpriseCaTrustAnchorBundleTslDocument
public function __construct( public string $schemeTerritory, public string $schemeOperatorName, public string $tslType, public int $sequenceNumber, public string $issueDateTime, public string $nextUpdate, public array $tspServices, public string $rawXmlSha256,) {}
public function isStale(DateTimeImmutable $now): boolpublic function assertFresh(DateTimeImmutable $now): voidpublic function servicesOfType(string $serviceTypeIdentifier): arraypublic function activeServices(): arrayTspService
public function __construct(public string $tspName, public string $serviceName, public string $serviceTypeIdentifier, public string $serviceStatus, public string $statusStartingTime, public string $serviceCertificatePem, public array $qualifiers, public array $additionalServiceInformation) {}
public function isGranted(): boolpublic function isQualifiedCa(): boolTspServiceQualifier
public function __construct(public string $qualifierUri, public string $criteriaListAssert = 'all', public array $policyOidConditions = [], public array $keyUsageConditions = []) {}EnterpriseCaTrustAnchorBundle
public function __construct(public array $anchorsPem, public string $bundleVersion, public string $bundleSha256)
public function containsFingerprint(string $anchorDerSha256Hex): boolpublic static function computeBundleSha256(array $anchorsPem): stringTslSignatureException
public function __construct(public readonly string $reason, string $message)Коды причин TslSignatureException: missing_signature, untrusted_signer, invalid_signature, digest_mismatch, unsupported_algorithm, unsupported_transform, expired_anchor.
Контракт поведения
Заголовок раздела «Контракт поведения»- Порядок конвейера фиксирован: загрузка, проверка XMLDSig, структурный разбор, шлюз устаревания.
TslPolicyEnforcerвозвращаетTslDocumentтолько тогда, когда все четыре этапа успешны. Доверенный список подписывается своим оператором схемы, чтобы полагающиеся стороны могли проверить подлинность и целостность — ETSI TS 119 612 §5.7.1. TslXmlParserпо замыслу не зависит от подписи. Вызывающие стороны должны проверить подпись, прежде чем доверять любому разобранному полю.TslPolicyEnforcer::verifyXml()обеспечивает этот порядок.- Инвариант актуальности обеспечивается на каждой границе потребителя. Список, у которого
nextUpdateпрошёл, считается просроченным и отклоняется — ETSI TS 119 612 §5.3.15.verifyXml()контролирует относительно текущего системного времени;TslDocument::assertFresh()иbuildBundle()контролируют относительно переданного вызывающей стороной момента времени. - Разбор актуальности отказывает в закрытое состояние. Поля даты-времени — это значения ISO 8601 в UTC с явным обозначением — ETSI TS 119 612 §5.1.3.
nextUpdateбез явногоZили числового смещения поднимаетTslParseException; значение никогда не переинтерпретируется в местном часовом поясе сервера. buildBundle()вызываетassertFresh($now)до извлечения любого якоря, затем допускает только службы, которые одновременно granted и CA/QC. Granted и withdrawn — это URI статусов квалифицированной службы — ETSI TS 119 612 §5.5.4. CA/QC — это URI типа службы квалифицированного УЦ — ETSI TS 119 612 §5.5.1.1.- Версия набора выводится из территории схемы и порядкового номера TSL. Порядковый номер монотонен по релизам — ETSI TS 119 612 §5.3.2. Дайджест набора — это каноническая SHA-256 над PEM якорей, а
containsFingerprint()отвечает на вопрос принадлежности по DER SHA-256. - Верификатор доверяет только настроенным якорям. Сертификаты, найденные в
KeyInfo, служат листом подписанта и кандидатами-промежуточными; цепочка должна достичь настроенного якоря в пределах глубины 8, каждое звено должно быть темпорально действительным, а выпускающий сертификат должен нестиbasicConstraintscA=TRUE(плюсkeyCertSign, когда присутствуетkeyUsage). - Профиль проверки — это список разрешённого: RSA или ECDSA с SHA-256, SHA-384 или SHA-512; методы дайджеста SHA-256, SHA-384 или SHA-512; только исключительная канонизация; и в точности пара трансформаций вложенной подписи плюс исключительная C14N на покрывающем список
ds:Reference. Всё остальное завершается сunsupported_algorithmилиunsupported_transform. TslFetcherотклоняет URL не-HTTPS и применяет защиту от SSRF до любого исходящего трафика. ПриNetworkPolicy::STRICT_OFFLINEон отдаёт ранее кэшированное тело или поднимаетNetworkPolicyViolation; ни один исходящий запрос никогда не отправляется.
Граничные случаи и режимы сбоя
Заголовок раздела «Граничные случаи и режимы сбоя»- Устаревший список.
TslParseExceptionизverifyXml(),assertFresh()илиbuildBundle()означает, что источник доверия непригоден. Трактуйте это как операционный сбой обновления, а не как вердикт по подписи. - Неканонический
nextUpdate. Значение без явногоZили числового смещения бросает вместо снисходительного разбора. ETSI TS 119 612 §5.1.3 предписывает UTC-формуZ; шлюз также принимает явное числовое смещение и отклоняет всё остальное. - Дрейф времени использования.
verifyXml()контролирует во время проверки; документ, удерживаемый в памяти послеnextUpdate, всё равно не проходит более поздний шлюзbuildBundle($tsl, $now). - Пустая конфигурация якорей.
TslSignatureVerifierотказывается от построения с пустым списком якорей (InvalidArgumentException). - Нет пригодных служб. Свежий список без granted-служб CA/QC поднимает
TslParseExceptionизbuildBundle(); пустой набор никогда не производится. - Оффлайн-режим.
STRICT_OFFLINEбез кэшированного тела поднимаетNetworkPolicyViolation. Поиск в кэше предшествует проверке политики, поэтому кэшированный список сохраняет работу валидации в изолированной сети. - Слишком большой или пустой ответ.
fetch()читает не более$maxBytesбайт (по умолчанию 16 MiB); усечённый список затем не проходит проверку дайджеста далее по конвейеру. Пустое тело поднимаетTslFetchException. - DOCTYPE в XML. Любой DOCTYPE отклоняется до того, как libxml построит таблицу сущностей, и снова после загрузки. Это закрывает входные классы XXE и раскрытия сущностей (billion-laughs).
- Множественные подписи. До вычисления дайджеста удаляется только проверенная вложенная
ds:Signature; соседние подписи и контрподписи сохраняются. Дополнительные ссылки XAdES разрешены, но ровно однаds:Referenceдолжна покрывать корень документа. - Просроченный материал цепочки. Просроченный или ещё не действительный подписант, промежуточный или якорь завершается с причиной
expired_anchor.clockToleranceсимметрично расширяет окно приёма и по умолчанию равен0.
Поведение в режиме FIPS
Заголовок раздела «Поведение в режиме FIPS»Список разрешённого верификатора зафиксирован на RSA и ECDSA с семейством SHA-2; SHA-1 и MD5 структурно исключены. Подписная арифметика выполняется во встроенной программной криптографии (phpseclib). NextPDF не заявляет о валидации по FIPS 140-3 для этой арифметики. Профиль криптополитики Enterprise FIPS 140-3 задокументирован вместе с модулем безопасности; он ограничивает выбор алгоритмов и не меняет ни структуры доверенных списков, ни поведение этого модуля с отказом в закрытое состояние.
Соответствие
Заголовок раздела «Соответствие»| Утверждение | Стандарт | Пункт |
|---|---|---|
| Доверенный список, у которого Next update прошёл, отбрасывается как просроченный. | ETSI TS 119 612 | §5.3.15 |
Поля даты-времени — это строки ISO 8601 в UTC с обозначением Z. | ETSI TS 119 612 | §5.1.3 |
| Оператор схемы подписывает доверенный список ради подлинности и целостности. | ETSI TS 119 612 | §5.7.1 |
| Статус квалифицированной службы — это URI статуса granted или withdrawn. | ETSI TS 119 612 | §5.5.4 |
Квалифицированный УЦ идентифицируется по URI типа службы Svctype/CA/QC. | ETSI TS 119 612 | §5.5.1.1 |
| Порядковый номер TSL начинается с 1 и увеличивается при каждом релизе. | ETSI TS 119 612 | §5.3.2 |
Все пункты пересказаны; NextPDF не воспроизводит нормативный текст. NextPDF не заявляет о соответствии ETSI TS 119 612 и не заявляет о сертификации eIDAS. Потребление доверенного списка не делает подпись, сертификат или вывод NextPDF «квалифицированным»; квалификация принадлежит поставщику услуг доверия под надзором государства-члена, а юридическая сила лежит вне этого модуля. Ограничения модели обработки XMLDSig (трансформация вложенной подписи, исключительная канонизация, ссылка, покрывающая корень) задокументированы из профиля проверки продукта; спецификация W3C XML Signature находится вне цитируемого набора доказательств. Этот модуль решает только то, приемлем ли список как входные данные доверия; проверка пути сертификата относительно полученных якорей принадлежит слою валидации сертификатов.
Заметки по разработке
Заголовок раздела «Заметки по разработке»- Зависимости — интерфейсы PSR: клиент PSR-18, фабрика запросов PSR-17 и необязательный кэш PSR-16. Внедряйте в тестах in-memory-двойники; ни один этап не требует живого сетевого доступа, кроме холодного
fetch(). - Закрепляйте верхний якорь внеполосно. Для списков государств-членов якорь LOTL авторизует подписантов списков; верификатор никогда не начинает доверие из содержимого
KeyInfo. - Фоновый опрос, обработка pivot-LOTL и взаимный TLS или аутентификация прокси находятся вне области действия загрузчика в этой версии. Планируйте обновление внешне и заново загружайте перед каждым
nextUpdate. - Передавайте в
buildBundle()момент валидации, а не момент построения. Пересобирайте набор после каждого обновления; никогда не кэшируйте набор дольшеnextUpdateисходного списка. bundleVersionимеет наблюдаемую формуtsl-<territory>-seq<sequenceNumber>;rawXmlSha256наTslDocumentподдерживает записи доказательств и обнаружение повторов.- Некорректные записи служб разбираются с защитными значениями-заглушками; некорректная цифровая идентичность, дошедшая до построения набора, отказывает в закрытое состояние с
InvalidArgumentException. - Классы несут аннотации исходника пакета
@since 1.10.0(TslFetchException:3.2.0).TslDocument,TspServiceиTspServiceQualifierпомечены в исходнике@api.
Смотрите также
Заголовок раздела «Смотрите также»- Уровни доверия eIDAS — страница возможности, сопоставляющая доказательства доверенных списков с уровнями доверия.
- Контейнеры ASiC — потребитель
TslTrustAnchorProvider::buildBundle()для привязки доверия контейнера. - Проверка подписи — сторона проверки AdES/PAdES, потребляющая якоря доверия.
- Безопасность — глубокий справочник — объединённая поверхность безопасности Enterprise.
- Подпись — глубокий справочник — производитель долгосрочных PAdES B-LT и B-LTA.
Граница публикации
Заголовок раздела «Граница публикации»Эта страница документирует только внешне наблюдаемое поведение и поддерживаемую публичную поверхность API. Внутренние пути пространств имён, вспомогательные классы, таблицы механизмов, имена файлов runbook и префиксы тикетов вне области действия.