PDFiumPas splitst PAdES-ondertekening in twee aanroepen zodat de privésleutel nooit in uw proces hoeft te staan. PreparePadesRemoteSignature schrijft een incrementele update met een lege placeholder van vaste breedte voor /Contents en geeft een verzoekrecord terug dat de SHA-256-documentdigest, het exacte ByteRange en een vingerafdruk van het voorbereide bestand draagt. CompletePadesRemoteSignature neemt de losstaande CMS die uw ondertekeningsservice teruggeeft en plaatst die in die gereserveerde plek
Tussen die twee aanroepen kunnen minuten of uren verstrijken, kan het proces herstarten, en kan het werk naar een andere machine verhuizen. Die kloof is de volledige reden waarom de API zo vormgegeven is
Waarom kan een externe sleutel niet de gewone ondertekeningsaanroep gebruiken?
Omdat SignPadesBytes ervan uitgaat dat de ondertekeningsbewerking binnen de aanroep plaatsvindt. Ze bouwt de incrementele update, berekent de digest over het ByteRange, ondertekent die, en schrijft het resultaat, allemaal vóór ze terugkeert. Dat is precies goed wanneer de sleutel in de Windows-certificaatopslag of in een PKCS#12-bestand staat dat u geladen hebt
Het is onmogelijk wanneer de sleutel zich in een netwerk-HSM bevindt, in een gekwalificeerd handtekeningcreatie-apparaat beheerd door een vertrouwensdienstverlener, of in een cloud-ondertekeningsservice die de gebruiker om bevestiging op een telefoon vraagt. In die gevallen is de reeks geen functieaanroep, het is een gesprek: u stuurt een digest, iets anders authenticeert een mens, en er komt later een CMS terug. Een synchrone API kan "later" niet uitdrukken zonder een thread te blokkeren op een bewerking die mogelijk een tweede factor nodig heeft
Het tweefasenprotocol
Fase één bereidt het document voor. PDFiumPas voegt het handtekeningveld en de waarde-dictionary toe, reserveert ContentsSize bytes hex-gecodeerde ruimte in /Contents, berekent het ByteRange rond die reservering, en produceert een TPadesRemoteSigningRequest met FormatVersion, PreparedFingerprint, DocumentDigest, het viervoudige ByteRange, ContentsHexOffset en ContentsSize
De enige waarde die uw ondertekeningsservice nodig heeft, is DocumentDigest: de SHA-256 die de teruggegeven CAdES SignedData als message digest moet dragen. Al het overige in het record bestaat zodat fase twee kan bewijzen dat het bestand dat het voltooit, het bestand is waaruit die digest berekend werd
uses
FPdfPades;
var
Options: TPadesRemoteSignOptions;
Request: TPadesRemoteSigningRequest;
Source, Prepared, Session: TFileStream;
begin
Options := TPadesRemoteSignOptions.Default;
Options.Reason := 'Approved by finance';
Options.Location := 'Lisbon';
Options.Name := 'A. Moreira';
Options.SigningTimeUtc := NowUtc;
Options.ContentsSize := 16384; // hex-bytes gereserveerd voor de CMS
Source := TFileStream.Create('contract.pdf', fmOpenRead or fmShareDenyWrite);
Prepared := TFileStream.Create('contract.prepared.pdf', fmCreate);
try
PreparePadesRemoteSignature(Source, Prepared, Options, Request);
finally
Prepared.Free;
Source.Free;
end;
// Bewaar de sessie zodat een latere run - of een andere machine - hem kan afronden
Session := TFileStream.Create('contract.signreq', fmCreate);
try
SavePadesRemoteSigningRequest(Session, Request);
finally
Session.Free;
end;
SendDigestToSigningService(Request.DocumentDigest);
end;
Wat weigert Complete, en waarom bestaat elke controle?
Voltooiing is waar een ontwerp voor externe ondertekening meestal misgaat, dus de validatie is met opzet onverbiddelijk. CompletePadesRemoteSignature weigert een voorbereide PDF waarvan de vingerafdruk niet langer overeenkomt met het verzoek, een ByteRange dat niet overeenkomt met de vastgelegde placeholder-coördinaten, gewijzigde /Contents-scheidingstekens, een placeholder die niet langer leeg is, een CMS groter dan de reservering, een CMS die niet exact één DER-waarde is, een niet-ondersteunde SignedData-vorm, een ontbrekend signing-certificate-v2-attribuut, en een CMS waarvan de message digest niet gelijk is aan de digest van het voorbereide document
Elk daarvan komt overeen met een echte storing. De vingerafdruk- en ByteRange-controles vangen het geval waarin iemand het voorbereide bestand tussen de fasen opnieuw genereerde, wat een handtekening zou opleveren die verifieert tegen bytes die niemand heeft. De controle op de lege placeholder vangt dubbele voltooiing, waarbij een tweede CMS geschreven wordt over een handtekening die al bestaat. De message-digest-controle vangt het gevaarlijkste geval van allemaal: een correct gevormde CMS ondertekend over een ander document, wat u krijgt wanneer een wachtrij twee gelijktijdige ondertekeningssessies door elkaar haalt. Zonder die controle zou u een bestand produceren dat ondertekend lijkt en overal faalt bij validatie, of erger, dat de goedkeuring van iemand anders draagt
De eis voor signing-certificate-v2 is een kwestie van PAdES-conformiteit, geen integriteitskwestie. ETSI EN 319 142 vereist dat het ondertekeningscertificaat vastgebonden wordt in de ondertekende attributen, en een CMS zonder dat attribuut is geen PAdES-handtekening, zelfs als hij cryptografisch verifieert. Dit bij voltooiing weigeren betekent dat u het hier ontdekt, niet in een validatorrapport van een klant, een onderwerp dat verder verkend wordt in waarom validators PAdES-handtekeningen afwijzen
var
Request: TPadesRemoteSigningRequest;
Session, Prepared, Dest: TFileStream;
CmsDer: TBytes;
begin
Session := TFileStream.Create('contract.signreq', fmOpenRead);
try
Request := LoadPadesRemoteSigningRequest(Session);
finally
Session.Free;
end;
CmsDer := FetchDetachedCmsFromService; // teruggegeven door de HSM of TSP
Prepared := TFileStream.Create('contract.prepared.pdf', fmOpenRead);
Dest := TFileStream.Create('contract.signed.pdf', fmCreate);
try
try
CompletePadesRemoteSignature(Prepared, Dest, Request, CmsDer);
except
on E: EPadesCrypto do
// Elke weigering draagt een specifieke reden; log die letterlijk
FailSession(E.Message);
end;
finally
Dest.Free;
Prepared.Free;
end;
end;
Proces- en machinegrenzen oversteken
SavePadesRemoteSigningRequest en LoadPadesRemoteSigningRequest serialiseren de sessie via een stabiel geversioneerd binair formaat, wat het ontwerp praktisch maakt in plaats van slechts correct. Een webapplicatie kan een document voorbereiden in het ene verzoek, het voorbereide PDF-bestand en de sessieblob opslaan, een digest teruggeven aan de browser voor een smartcard-handtekening, en het bestand voltooien in een compleet andere verzoekafhandelaar
Het veld FormatVersion is wat dat veilig houdt over upgrades heen. Een sessie geschreven door een oudere build en geladen door een nieuwere wordt expliciet herkend of geweigerd, in plaats van verkeerd gelezen te worden als een anders gevormd record. Als uw wachtrij sessies dagenlang kan vasthouden, behandel de formatversie dan als een operationeel feit dat het waard is om te loggen, geen implementatiedetail
De placeholder dimensioneren
ContentsSize is de ene parameter waarover u moet nadenken, want die staat vast voordat de CMS bestaat. Ze telt de hex-gecodeerde reservering, dus een DER-CMS van 6 KB heeft minstens 12 KB ruimte nodig, en de implementatie plafonneert de reservering op 64 MiB
Reserveer te weinig en voltooiing faalt met een te-grote-CMS-fout nadat uw ondertekeningsservice haar werk al gedaan heeft, wat bij een gemeten gekwalificeerde-handtekeningservice een verspilde bewerking betekent. Reserveer te veel en elk ondertekend document draagt de opvulling voor altijd mee. De verstandige aanpak is meten: onderteken één document met uw echte certificaatketen, bekijk de DER-lengte, verdubbel die voor hex, en tel dan royale marge op voor het tijdstempeltoken als u van plan bent te upgraden naar een T-niveau-handtekening. Ketens met meerdere tussencertificaten en een lange OCSP-respons groeien sneller dan mensen verwachten
Wat er na de handtekening komt
Een voltooide externe handtekening is PAdES B-B. Langetermijnvalidatie heeft een tijdstempel en het validatiemateriaal nodig, wat een aparte incrementele update is die een DSS en de bijbehorende VRI-dictionaries per handtekening toevoegt, beschreven in langetermijnhandtekeningen met RFC 3161-tijdstempels en DSS. Die stap is lokaal: ze voegt certificaten, OCSP-responsen en CRL's toe, waarvan er geen de privésleutel nodig heeft
Verifieer vóór het uitleveren wat u geproduceerd hebt met hetzelfde codepad dat een vertrouwende partij zou gebruiken, behandeld in digitale handtekeningen en PAdES-niveaus inspecteren. Ondertekenen en verifiëren zijn verschillende code, en een pijplijn voor externe ondertekening is precies de plek waar de twee uit elkaar kunnen groeien zonder dat iemand het merkt totdat een externe validator het zegt
PDFiumPas is een Delphi- en Lazarus-component rond de PDFium-engine met een native Pascal PAdES-stack, dus ondertekenen, tijdstempelen en valideren werken zonder externe commandoregeltools. Volledige API-documentatie en een proefversie staan op de PDFium Delphi-componentpagina