Przejdź do głównej zawartości
getnextpdf.com

Enterprise edycja

Powiązanie zaufania ASiC

Kontener ASiC łączy podpisane pliki z podpisami, które je chronią. Trudne pytanie nie brzmi „czy podpis się przelicza?”, lecz „kto stoi za podpisującym?”. NextPDF\Enterprise\Security\Asic\AsicTrustBinder odpowiada dokładnie na to pytanie. Przekazujesz mu certyfikat podpisujący z podpisu kontenera, zaufaną listę oraz czas walidacji. Odpowiada obiektem AsicTrustBindingResult: werdyktem zaufany/niezaufany, wersją pakietu kotwic, względem której podjął decyzję, oraz maszynowo czytelnymi powodami. Każde odrzucenie nazywa swoją przyczynę, więc dowód audytowy zapisuje się sam.

Jedna granica jest celowa i warto zaznaczyć ją na wstępie. To API nie parsuje kontenerów ASiC. Twoje narzędzia otwierają kontener i wyodrębniają certyfikat podpisujący; NextPDF jest właścicielem decyzji o zaufaniu.

Ta funkcja dostarczana jest w NextPDF Enterprise (nextpdf/enterprise) i aktywuje się kopertą licencyjną poziomu Enterprise. Wdrożenie bez tego uprawnienia nie ładuje klas tej funkcji. Porównaj edycje i uzyskaj licencję.

Okno terminala
composer require nextpdf/enterprise

Aktywacja wymaga koperty licencyjnej Enterprise. Zobacz Instalacja i uwierzytelnianie. Klasy z tej strony znajdują się w przestrzeniach NextPDF\Enterprise\Security\Asic oraz NextPDF\Enterprise\Security\Tsl.

ASiC (Associated Signature Containers, ETSI EN 319 162-1) pakuje pliki danych i podpisy w jednym archiwum. Bazowy kontener ASiC osadza wyłącznie bazowe podpisy CAdES lub XAdES. Bazowy podpis CAdES niesie swój certyfikat podpisujący wewnątrz SignedData.certificates, więc oczekuje się, że weryfikator wyodrębni go, gdy podpis jest poprawnie sformowany i obsługiwany przez narzędzia kontenera, z podpisu kontenera. Ten wyodrębniony certyfikat jest wejściem tego API.

Źródłem zaufania jest zaufana lista (TSL) ETSI TS 119 612: podpisany dokument XML, który wylicza dostawców usług zaufania oraz ich certyfikaty usługowe. NextPDF\Enterprise\Security\Tsl\TslTrustAnchorProvider przekształca sparsowany TslDocument w pakiet kotwic. Tylko usługi, które są jednocześnie w statusie granted i typu usługi CA/QC, zasilają zbiór kotwic. Pakiet niesie ciąg wersji wyprowadzony z numeru sekwencji TSL i terytorium, plus skrót integralności SHA-256.

Przed jakimkolwiek porównaniem kotwic uruchamiane są dwie bramki fail-closed:

  1. Świeżość TSL. Zaufana lista, której moment NextUpdate już minął, musi zostać odrzucona jako wygasła. AsicTrustBinder::verify() sprawdza świeżość w podanym czasie walidacji, zanim wyprowadzi choćby jedną kotwicę. Nieaktualna lista lub wartość NextUpdate bez jawnego oznaczenia UTC rzuca TslParseException.
  2. Okres ważności podpisującego. Walidacja ścieżki RFC 5280 wymaga, aby okres ważności certyfikatu obejmował czas walidacji. Kryptograficznie nienaruszony podpis, którego certyfikat był w tym czasie wygasły lub jeszcze nieważny, jest odrzucany z precyzyjnym kodem powodu.

Dopiero wtedy binder testuje certyfikat podpisujący względem każdej kotwicy. Dopasowanie daje trusted: true z powodem anchor_signature_match. Brak dopasowania daje trusted: false z powodem no_anchor_chain.

Nośną decyzją projektową jest ścisłe oddzielenie mechaniki kontenera od decyzji o zaufaniu, przy wymuszeniu, aby decyzja o zaufaniu była jawna co do czasu. Formaty kontenerów różnią się (ASiC-S, ASiC-E, ładunki CAdES lub XAdES), ale pytanie o zaufanie jest jednym niezmiennym jądrem: czy ten certyfikat łączy się z kotwicą ze świeżej zaufanej listy w podanym momencie? Utrzymanie tego jądra wolnym od parsowania ZIP i XML sprawia, że jest ono na tyle małe, by testować je wyczerpująco i by zamykać się bezpiecznie przy każdej bramce. To samo rozumowanie zakazuje cichej wartości domyślnej now: czas walidacji zmienia werdykt, więc musi być własnością wywołującego. Świeżość sprawdzana jest wewnątrz samej ścieżki wyprowadzania kotwic, a nie w opcjonalnym współpracowniku, więc żadna ścieżka producenta nie może jej pominąć.

Tło projektowe: Jak podpis cyfrowy dowodzi, kto podpisał.

Konstrukcja przyjmuje dostawcę kotwic, który zamienia zaufane listy w pakiety kotwic.

public function __construct(
private readonly TslTrustAnchorProvider $anchorProvider,
) {}

Główny punkt wejścia weryfikuje certyfikat podpisującego względem zaufanej listy:

public function verify(
string $signerCertPem,
TslDocument $tsl,
DateTimeInterface $validationTime,
): AsicTrustBindingResult
  • $signerCertPem — niepusty ciąg PEM: certyfikat podpisujący z podpisu ASiC.
  • $tsl — sparsowana, uwierzytelniona zaufana lista.
  • $validationTime — moment, który musi obejmować okres ważności certyfikatu podpisującego. Nie ma wartości domyślnej.

Rzuca lub kończy się niepowodzeniem z: NextPDF\Enterprise\Security\Tsl\TslParseException, gdy TSL jest nieaktualny (NextUpdate minął), gdy NextUpdate nie jest kanoniczną wartością UTC lub gdy lista nie zawiera aktywnych usług CA/QC. Niezaufani podpisujący nie rzucają wyjątku; zwracają wynik z trusted: false i kodem powodu.

Dla obciążeń wsadowych weryfikuj względem wcześniej zbudowanego pakietu:

public function verifyAgainstBundle(
string $signerCertPem,
EnterpriseCaTrustAnchorBundle $bundle,
DateTimeInterface $validationTime,
): AsicTrustBindingResult

Rzuca lub kończy się niepowodzeniem z: brak własnych wyjątków; każdy wynik jest obiektem AsicTrustBindingResult. Pakiet uzyskaj z TslTrustAnchorProvider::buildBundle() — nie konstruuj go ręcznie.

public function buildBundle(TslDocument $tsl, DateTimeImmutable $now): EnterpriseCaTrustAnchorBundle

Rzuca lub kończy się niepowodzeniem z: TslParseException, jeśli TSL jest nieaktualny, jego NextUpdate nie jest kanoniczną wartością UTC lub nie ma aktywnych usług CA/QC.

public function __construct(
public bool $trusted,
public string $anchorBundleVersion,
public array $reasons,
) {}

$reasons to list<non-empty-string> maszynowo czytelnych kodów. $anchorBundleVersion zapisuje użyty zbiór kotwic w postaci tsl-<territory>-seq<N> (na przykład tsl-eu-seq42).

Kod powoduZnaczenie
anchor_signature_matchCertyfikat podpisujący weryfikuje się względem kotwicy wyprowadzonej z TSL. Zaufany.
no_anchor_chainŻadna kotwica w pakiecie nie weryfikuje certyfikatu podpisującego. Niezaufany.
signer_cert_expiredCzas walidacji przypada po notAfter certyfikatu. Niezaufany.
signer_cert_not_yet_validCzas walidacji przypada przed notBefore certyfikatu. Niezaufany.
cannot_parse_signer_certDostarczony PEM nie parsuje się jako certyfikat X.509. Niezaufany.

Twoje narzędzia kontenera już wyodrębniły certyfikat podpisujący. Powiąż go z zaufaną listą państwa członkowskiego, którą pobrałeś i uwierzytelniłeś (zobacz Trusted lists).

asic-trust-binding-quickstart.php
<?php
declare(strict_types=1);
require __DIR__ . '/vendor/autoload.php';
use NextPDF\Enterprise\Security\Asic\AsicTrustBinder;
use NextPDF\Enterprise\Security\Tsl\TslParseException;
use NextPDF\Enterprise\Security\Tsl\TslTrustAnchorProvider;
use NextPDF\Enterprise\Security\Tsl\TslXmlParser;
// Extracted by YOUR tooling from META-INF/signature.p7s or signatures.xml.
$signerCertPem = (string) file_get_contents(__DIR__ . '/asic-signer.pem');
// A trusted list you have already fetched and authenticated.
$tslXml = (string) file_get_contents(__DIR__ . '/member-state-tsl.xml');
$binder = new AsicTrustBinder(new TslTrustAnchorProvider());
try {
$tsl = (new TslXmlParser())->parse($tslXml);
$result = $binder->verify(
signerCertPem: $signerCertPem,
tsl: $tsl,
validationTime: new DateTimeImmutable('2026-07-03T12:00:00Z'),
);
} catch (TslParseException $e) {
// Fail closed: stale TSL, malformed NextUpdate, or no active CA/QC services.
fwrite(STDERR, 'Trusted list rejected: ' . $e->getMessage() . PHP_EOL);
exit(1);
}
echo $result->trusted ? "TRUSTED\n" : "NOT TRUSTED\n";
echo 'Anchors: ' . $result->anchorBundleVersion . "\n";
echo 'Reasons: ' . implode(', ', $result->reasons) . "\n";

Oczekiwany wynik dla podpisującego wystawionego przez wymienioną usługę CA/QC:

TRUSTED
Anchors: tsl-eu-seq42
Reasons: anchor_signature_match

Wyprowadź pakiet kotwic raz na zaufaną listę, a następnie weryfikuj wiele podpisujących z kontenerów względem niego. Jeden nieaktualny lub nieużyteczny TSL zamyka bezpiecznie całą partię; indywidualne problemy podpisujących ujawniają się per kontener.

asic-trust-binding-batch.php
<?php
declare(strict_types=1);
require __DIR__ . '/vendor/autoload.php';
use NextPDF\Enterprise\Security\Asic\AsicTrustBinder;
use NextPDF\Enterprise\Security\Asic\AsicTrustBindingResult;
use NextPDF\Enterprise\Security\Tsl\TslDocument;
use NextPDF\Enterprise\Security\Tsl\TslParseException;
use NextPDF\Enterprise\Security\Tsl\TslTrustAnchorProvider;
use NextPDF\Enterprise\Security\Tsl\TslXmlParser;
/**
* @param array<string, non-empty-string> $signerPemsByContainer PEM per container path.
* @return array<string, AsicTrustBindingResult>
* @throws TslParseException When no anchor set can be derived from the TSL.
*/
function bindBatch(
TslDocument $tsl,
array $signerPemsByContainer,
DateTimeImmutable $validationTime,
): array {
$provider = new TslTrustAnchorProvider();
// Derive the anchor set ONCE; a throw here means the trusted list itself
// is unusable at this validation time.
$bundle = $provider->buildBundle($tsl, $validationTime);
$binder = new AsicTrustBinder($provider);
$results = [];
foreach ($signerPemsByContainer as $container => $signerPem) {
$results[$container] = $binder->verifyAgainstBundle(
signerCertPem: $signerPem,
bundle: $bundle,
validationTime: $validationTime,
);
}
return $results;
}
$tsl = (new TslXmlParser())->parse(
(string) file_get_contents(__DIR__ . '/member-state-tsl.xml'),
);
$signerPems = [
'invoice-2026-06.asice' => (string) file_get_contents(__DIR__ . '/signer-a.pem'),
'tender-2019.asice' => (string) file_get_contents(__DIR__ . '/signer-b.pem'),
];
try {
$results = bindBatch(
tsl: $tsl,
signerPemsByContainer: $signerPems,
validationTime: new DateTimeImmutable('now', new DateTimeZone('UTC')),
);
} catch (TslParseException $e) {
// Fail closed for the WHOLE batch: no trustworthy anchor set exists.
fwrite(STDERR, 'Anchor derivation failed: ' . $e->getMessage() . PHP_EOL);
exit(1);
}
foreach ($results as $container => $result) {
printf(
"%s => %s (%s; anchors %s)\n",
$container,
$result->trusted ? 'trusted' : 'rejected',
implode(',', $result->reasons),
$result->anchorBundleVersion,
);
}

Oczekiwany wynik, gdy jeden certyfikat podpisujący wygasł:

invoice-2026-06.asice => trusted (anchor_signature_match; anchors tsl-eu-seq42)
tender-2019.asice => rejected (signer_cert_expired; anchors tsl-eu-seq42)
  • Czas walidacji jest obowiązkowy i rozstrzygający. Nie ma cichej wartości domyślnej now. Podpis, który zweryfikował się w 2019, zgłasza signer_cert_expired, gdy walidujesz w momencie z 2026 po notAfter. Dla materiałów historycznych przekaż czas, który wspiera Twój dowód (na przykład czas dowodu istnienia), a nie zegar ścienny.
  • Nieaktualny TSL rzuca wyjątek; to nie jest werdykt „niezaufany”. TslParseException z verify() lub buildBundle() oznacza, że źródło zaufania jest nieużyteczne. Traktuj to jako awarię operacyjną: odśwież listę, nie zapisuj tego jako odrzucenia podpisującego.
  • Kotwice testowane są jako bezpośredni wystawcy. Każda kotwica jest próbowana jako certyfikat, który podpisał certyfikat podpisujący. TSL państw członkowskich UE wymieniają certyfikaty usług CA/QC wystawiających, więc certyfikaty kwalifikowane podmiotu końcowego zwykle dopasowują się bezpośrednio. Podpisujący wystawiony przez pośredni urząd CA, który sam nie jest wymienioną aktywną usługą CA/QC, daje no_anchor_chain.
  • Wyprowadzanie kotwic filtruje ostro. Usługi wycofane lub jakiegokolwiek typu innego niż CA/QC nigdy nie stają się kotwicami. Lista, której aktywny zbiór CA/QC jest pusty, rzuca wyjątek, zamiast wytwarzać pusty pakiet.
  • NextUpdate musi być kanonicznym UTC. Wartość bez jawnego Z lub liczbowego oznaczenia przesunięcia jest odrzucana fail-closed, nigdy nie reinterpretowana w lokalnej strefie czasowej serwera.
  • Zniekształcone wejście degraduje precyzyjnie. PEM, który się nie parsuje, zwraca cannot_parse_signer_cert; certyfikat jeszcze nieważny jest odróżniany od wygasłego.
  • Zapisz anchorBundleVersion. Nazywa dokładny zbiór kotwic (tsl-<territory>-seq<N>) stojący za każdym werdyktem, o co właśnie zapyta audytor.
  • Fail-closed z założenia konstrukcyjnego. Świeżość sprawdzana jest przed wyprowadzeniem jakiejkolwiek kotwicy. Bramka ważności podpisującego uruchamiana jest przed jakimkolwiek porównaniem kotwic. Nieużyteczny materiał zaufania rzuca wyjątek; wątpliwi podpisujący są odrzucani z podaniem powodów. Żadna ścieżka nie degraduje do cichego przejścia.
  • Powiązanie zaufania to jedna warstwa, a nie cała walidacja. To API nie weryfikuje wartości podpisu CAdES nad zawartością kontenera, nie sprawdza unieważnienia (brak zapytań CRL ani OCSP) i nie uwierzytelnia samego dokumentu TSL. Najpierw uwierzytelnij listę przez potok zaufanych list (zobacz Trusted lists), zweryfikuj podpis kryptograficznie swoimi narzędziami do podpisów i dodaj sprawdzanie unieważnienia zgodnie ze swoją polityką.
  • Wybieraj czas walidacji rozważnie. Werdykt jest funkcją czasu, który przekazujesz. Wyprowadź go z wiarygodnego dowodu (kwalifikowanego znacznika czasu, zapisu archiwalnego), a nie z zegara, na który może wpłynąć atakujący.
  • Wyjścia dowodowe są deterministyczne. trusted, anchorBundleVersion i reasons to stabilne, maszynowo czytelne wartości nadające się do podpisanych dzienników audytowych.

AsicTrustBinder wspiera procesy zgodne z ETSI EN 319 162-1 (bazowe kontenery ASiC), ETSI EN 319 122-1 (bazowe podpisy CAdES) oraz ETSI TS 119 612 (zaufane listy) i stosuje bramkę okresu ważności RFC 5280 w podanym czasie walidacji.

Wsparcie to nie zgodność, a zgodność to nie certyfikacja. NextPDF implementuje kontrole opisane na tej stronie; nie został certyfikowany względem tych norm przez żadną jednostkę, a użycie tego API samo w sobie nie czyni Twojego wyniku „kwalifikowanym” ani prawnie skutecznym w świetle eIDAS lub jakiegokolwiek innego reżimu. NextPDF nie posiada żadnej certyfikacji i żadnej nie udziela. To, czy kompletny proces walidacji spełnia dany wymóg prawny lub przetargowy, jest ustaleniem należącym do Twoich audytorów.

Powiązanie zaufania wykonuje kontrole podpisu certyfikatu X.509 w procesie; nie jest kierowane przez strażnika środowiska uruchomieniowego trybu FIPS Enterprise, a włączenie trybu FIPS nie zmienia jego zachowania. Nie jest to usługa kryptograficzna walidowana pod FIPS i nie jest deklarowana żadna certyfikacja FIPS 140. Wdrożenia z obowiązkami FIPS powinny odpowiednio ograniczyć zakres tego API i zobaczyć Polityka kryptograficzna FIPS 140-2/3.

  • verify() wyprowadza kotwice wyłącznie z TSL, który jest świeży w podanym czasie walidacji; nieaktualna lub zniekształcona lista rzuca TslParseException, zanim istnieje jakakolwiek kotwica.
  • Kotwice wyprowadzane są wyłącznie z usług TSL w statusie granted typu usługi CA/QC; pusty aktywny zbiór rzuca wyjątek.
  • Okres ważności certyfikatu podpisującego musi obejmować czas walidacji; naruszenia zwracają signer_cert_expired lub signer_cert_not_yet_valid.
  • Każdy wynik jest obiektem AsicTrustBindingResult niosącym trusted, anchorBundleVersion i co najmniej jeden kod powodu; nie ma werdyktu bez powodu.
  • Niezaufani podpisujący są zwracani, nigdy rzucani; nieużyteczny materiał zaufania jest rzucany, nigdy zwracany jako werdykt.
  • Parsowanie kontenera nigdy nie zachodzi wewnątrz tego API; wejściami są wyodrębniony PEM, zaufana lista i czas walidacji.

NextPDF Core waliduje podpisy PDF (CMS/PAdES) względem kotwic zaufania, które przypinasz jawnie przez jego kontrakt CaTrustAnchorBundle — zobacz Core security. Core nie ma pobierania zaufanych list (TSL) ani specyficznego dla ASiC powiązania zaufania. Z samym Core możesz utrzymywać własny zbiór kotwic do walidacji podpisów PDF; wyprowadzanie kotwic z zaufanej listy ETSI TS 119 612 i wiązanie z nimi podpisujących z kontenerów ASiC wymaga NextPDF Enterprise.

Ta strona dokumentuje wyłącznie zewnętrznie obserwowalne zachowanie oraz wspieraną publiczną powierzchnię API. Wewnętrzne ścieżki przestrzeni nazw, klasy pomocnicze, tabele mechanizmów, nazwy plików runbooków i prefiksy zgłoszeń są poza zakresem.