Usoudit, že PDF podpis je kvalifikovaný podle eIDAS, znamená odpovědět na otázku, která s kryptografií nemá nic společného: byl certifikát vydán službou důvěry, kterou členský stát uvedl jako kvalifikovanou, v okamžiku, kdy podpis vznikl. Odpověď žije v seznamu důvěry, XML dokumentu publikovaném na území, a celá hodnota toho dokumentu závisí na jeho autenticitě. Proto komponenta PDFium odmítá nahlédnout dovnitř, dokud se za ni někdo nepřimluví. TPdfEuropeanTrustedList.ParseAuthenticated podá kompletní surové bajty volajícím dodanému IPdfTrustedListAuthenticator dříve, než parsuje jedinou službu, a snímek vytvoří, jen pokud ten autentizátor explicitně projde
To pořadí je návrh. Všechno ostatní v této funkci z něj plyne, včetně částí, které vypadají neobvykle nepohodlně
Naparsováno není důvěryhodné
Seznam důvěry, který se čistě naparsuje, vám řekne, že XML je dobře utvářené. Neřekne vám nic o tom, kdo jej napsal. Protože seznam je to, na čem spočívá vaše celé rozhodnutí o kvalifikovaném stavu, přijmout jeden proto, že se naparsoval, by rozhodnutí učinilo nesmyslným: útočník, který dokáže seznam vyměnit, může prohlásit vlastní certifikační autoritu za kvalifikovanou
Tatáž úvaha platí pro cachování a to je past hodná pojmenování. Snímková cache ukládá původní XML spolu s otiskem SHA-256 a snadno by se dalo brát shodný otisk při načtení jako důkaz, že seznam je pravý. Není. Otisk spočítaný tím samým procesem, který soubor uložil, bez zapojení klíče, ověřuje jen to, že se bajty od zapsání nezměnily; byl-li seznam podvodný v okamžiku cachování, otisk potvrzuje, že jde o tentýž podvodný seznam. Načtení cachovaného snímku proto prochází týmž autentizátorem jako parsování čerstvého. Integrita a autenticita jsou různé vlastnosti a jen jedna z nich potřebuje klíč
uses
FPdfTrustedList;
type
TListAuthenticator = class(TInterfacedObject, IPdfTrustedListAuthenticator)
public
function Authenticate(const XmlData: TBytes;
out AuthenticationDetails: string): Boolean;
end;
function TListAuthenticator.Authenticate(const XmlData: TBytes;
out AuthenticationDetails: string): Boolean;
begin
// Vaše politika žije zde: ověřte obálkový podpis XMLDSIG
// proti certifikátu podepisujícímu seznam, který jste si připnuli
// mimo kanál, a popište, co jste zkontrolovali pro auditní stopu
Result := VerifyEnvelopedXmlSignature(XmlData, FPinnedListSigner);
if Result then
AuthenticationDetails := 'XMLDSIG verified against pinned LOTL signer';
end;
var
List: TPdfEuropeanTrustedList;
Cache: TFileStream;
begin
List := TPdfEuropeanTrustedList.ParseAuthenticated(RawXml,
TListAuthenticator.Create, TPdfTrustedListOptions.Default);
// Snímek existuje jen proto, že autentizátor řekl ano
Cache := TFileStream.Create('tl-de.snapshot', fmCreate);
try
List.SaveCache(Cache);
finally
Cache.Free;
end;
end;
Validátor nevlastní síťovou politiku
PAdES validátor nemá co rozhodovat o tom, jak se dostat k seznamu seznamů důvěry, zda jít přes proxy, jak často opakovat nebo co dělat, když je území nedostupné. To jsou rozhodnutí aplikace a nasazení a v regulovaných prostředích se často auditují. Aktualizace proto přicházejí přes IPdfTrustedListSource, kterému se podá URI a bajtový strop a jenž vrací bajty
To, co komponenta vynucuje, jsou invarianty, které činí z aktualizace aktualizaci, nikoli záměnu. Update vyžaduje, že území se nezměnilo, že číslo sekvence přísně roste a že čas vydání se nepohne zpět. Tyto tři kontroly porazí nejzjevnější downgrade útoky: přehrání staršího seznamu, který stále uvádí mezitím staženou službu, nebo vyměnění seznamu jiného území, jehož službám jste nikdy nechtěli důvěřovat
Limity parseru a žádné DTD vůbec
TPdfTrustedListOptions omezuje velikost XML, počet tokenů, hloubku zanoření, počet služeb, počet certifikátů a velikost jednotlivého certifikátu a třídní funkce Default podává použitelné hodnoty. Seznamy důvěry jsou publikované dokumenty předvídatelné velikosti, takže meze se nastavují levně a neexistuje legitimní seznam, který by je potřeboval překročit
Zvlášť a bezpodmínečně parser odmítá deklarace DTD a entit. To zavírá jak odmítnutí služby expanzí entit, tak cestu prozrazení externí entitou jediným odmítnutím a nic to nestojí, protože seznamy důvěry entity nepoužívají. Jakýkoli XML parser dosažitelný z nedůvěryhodného vstupu by měl být takto nakonfigurován; rozdíl zde je v tom, že odmítnutí není konfigurovatelné, takže jej nelze vypnout dobře míněnou změnou volby
Kvalifikovaný stav se zaznamenává vedle důvěry řetězce, neslučuje se do ní
Vyhodnocovací strana je záměrně oddělená. TPadesTrustValidationOptions.QualifiedTrustEvaluator přebírá IPdfQualifiedTrustEvaluator, který snímek seznamu důvěry implementuje. Během validace obdrží vyhodnocovač listový certifikát, řetězec a čas validace, páruje certifikáty služeb přesnou DER srovnávací metodou proti podepisujícímu a řetězci, spojuje stav služby, identifikátor typu služby a URI kvalifikátorů v tom okamžiku a vrací vyhodnocovací záznam
Výsledek dopadá na dvě místa u každého podpisu: QualifiedTrustStatus jako hrubý stav a QualifiedTrust jako plné vyhodnocení s územím, názvem poskytovatele, názvem služby, identifikátorem typu, stavem a počátečním časem stavu. To, co nedělá, je změna CertificateTrustStatus. Systémová důvěra řetězce a kvalifikovaný stav odpovídají na různé otázky a hlášení, které je slejí dohromady, nedokáže rozlišit „důvěryhodný, ale nekvalifikovaný“ od „kvalifikovaný, ale řetězec nevaliduje“, přičemž obojí je skutečné a obojí potřebuje jinou obsluhu
var
Options: TPadesTrustValidationOptions;
Report: TPadesValidationResult;
I: Integer;
begin
Options := TPadesTrustValidationOptions.Default;
Options.CheckRevocation := True;
Options.QualifiedTrustEvaluator := List; // autentizovaný snímek
Options.QualifiedValidationTime := SigningTime; // nikoli Now
Report := Pdf.ValidatePadesTrust(Options);
for I := 0 to High(Report.Signatures) do
if Report.Signatures[I].QualifiedTrustStatus = pcsValid then
Writeln(Format('signature %d qualified by %s / %s (%s)',
[I, Report.Signatures[I].QualifiedTrust.Territory,
Report.Signatures[I].QualifiedTrust.ProviderName,
Report.Signatures[I].QualifiedTrust.ServiceName]))
else if Report.Signatures[I].QualifiedTrustStatus = pcsIndeterminate then
// Žádná odpovídající služba, nebo snímek nemůže odpovědět za tento čas
Writeln(Format('signature %d: qualified status undetermined', [I]));
end;
Proč čas validace není právě teď
Protože kvalifikace je vlastnost okamžiku. Služba důvěry může být oceněna kvalifikovaným stavem, později jej může ztratit a později ještě být obnovena a každý z těch přechodů nese ve seznamu počáteční čas. Podpis učiněný, dokud byla služba kvalifikovaná, zůstává kvalifikovaný i poté; podpis učiněný před udělením se nekvalifikuje dodatečně. Vyhodnocení proti aktuálnímu času proto dává špatnou odpověď oběma směry
Seznam nese to, co je k tomu potřeba: každý záznam služby má počáteční čas stavu a příznak rozlišující historické položky od aktuálních a vyhodnocovač je kombinuje proti času, který dodáte. V praxi ten čas pochází z důvěryhodného časového razítka na podpisu, nikoli z času podpisu tvrzeného v CMS, což je důvod, proč materiál dlouhodobého ověřování záleží i u otázky, která vypadá jako dohledání politiky; strana razítka a DSS je předmětem článku o dlouhodobých podpisech
Co ještě musíte postavit
Tři věci a žádná z nepatří do PDF knihovny. Autentizátor, tedy skutečné ověření XMLDSIG proti certifikátu podepisujícímu seznam, který jste získali kanálem, kterému věříte. Politika stahování, tedy jak a jak často obnovujete a co dělá vaše aplikace, když obnovení selže. A územní rozsah, tedy které seznamy vůbec nesete, což je byznys rozhodnutí o tom, v kterých členských státech podepisují vaše protistrany
Co od komponenty dostáváte, je ta část, kterou je snadné jemně pokazit: pořadí autentizuj-před-parsováním, ohraničené parsování XML bez entit, monotonní invarianty aktualizace, párování služeb přesným DER, historické vyhodnocování stavu a výsledek, který zůstává oddělený od obyčejné důvěry řetězce. Je-li váš bezprostřední problém základnější, že validátor odmítá podpis, o němž jste přesvědčeni, že je v pořádku, obvyklé příčiny jsou katalogizovány v proč validátory odmítají podpisy PAdES a povrch inspekce podpisů popisuje inspekce podpisů a úrovní PAdES. Schopnosti komponenty jsou uvedeny na stránce produktu PDFium Delphi component