Een PDF-handtekening is grotendeels byte-administratie, en byte-administratie is waar het misgaat. De cryptografie draait op code die twee decennia is ge-audit, en dat deel faalt vrijwel nooit. Wat in productie faalt is nederiger: een placeholder die te klein werd gereserveerd voor de echte handtekening, een hash over het verkeerde stuk van het bestand, of een "save" na het ondertekenen die stilletjes bytes herschreef die de handtekening al had bevroren. Leg de bytes goed neer en het groene vinkje regelt zichzelf
HotPDF dekt ondertekenen voor Delphi en C++Builder op drie niveaus, en u kiest tussen ze door één vraag te beantwoorden: waar leeft de private sleutel? Een PFX-bestand op schijf heeft één functieaanroep nodig. Een sleutel die in een HSM of een externe ondertekenings-service vergrendeld zit, heeft de reserve-hash-insert-sequentie nodig, want geen bibliotheek kan in een token reiken en de sleutel eruit trekken. Een handtekening die aan Europese regelgeving moet voldoen, heeft daarbovenop de PAdES-baseline-structuren nodig. De secties hieronder volgen die progressie
Hoe /ByteRange de ondertekende bytes vastpint
Een handtekening moet leven in het bestand dat hij ondertekent, en hij kan zichzelf niet ondertekenen. PDF omzeilt het paradoxon door een gat achter te laten. Vóór het ondertekenen reserveert de writer een /Contents-entry van vaste grootte vol nullen en registreert een /ByteRange-array voor de twee spannen aan weerszijden ervan: alles vóór het gat, alles erna. De ondertekenaar hasht die twee spannen en schrijft de resulterende CMS-blob als hexadecimaal in het gat. De val zit in het woord vast. U commit op de grootte van dat gat voordat u weet hoe groot de afgewerkte handtekening zal zijn, dus de reservering moet een zelfverzekerde overschatting zijn. Acht kilobyte houdt comfortabel een detached CMS-handtekening met een korte certificaatketen
HotPDF splitst de twee gevallen in twee aanroepen, en ze door elkaar halen is een veelvoorkomende vroege fout. AddSignatureField dropt een leeg, zichtbaar veld voor een persoon om later in een viewer te ondertekenen. AddSignedSignatureField creëert het veld en reserveert het /Contents-gat, en dat is degene die u wilt wanneer code, in plaats van een mens, de handtekening zal voltooien. Overhandig een externe ondertekenaar een leeg veld en hij heeft niets om te vullen
Het één-aanroep-pad: ondertekenen vanuit een PFX
Wanneer het certificaat en zijn private sleutel in een PFX/PKCS#12-bestand zitten dat uw proces kan lezen, reduceert de hele pipeline tot één class-functie:
if THotPDF.SignPDFWithPFX('invoice-unsigned.pdf', 'invoice-signed.pdf',
'company-cert.pfx', 'pfx-password') then
Writeln('Signed: invoice-signed.pdf')
else
raise Exception.Create('PFX signing failed');
Wanneer dit faalt, is de PDF zelden het probleem. De PFX wel. HotPDF leest containers die met PBES2 zijn beveiligd, wat PBKDF2-key-derivation over AES-256-CBC betekent. Een PFX die door een oudere Windows-certificaat-wizard, of door OpenSSL vóór 3.0, werd geëxporteerd, is meestal in legacy RC2 of 3DES gewikkeld, en hij parseert eenvoudig niet. De reparatie is de container eenmaal opnieuw te exporteren met moderne bescherming; hedendaagse OpenSSL doet dit standaard, en het is geen codewijziging. Dus wanneer ondertekenen onmiddellijk sterft op een certificaat dat "overal anders werkt," kijk dan hoe de PFX werd gecreëerd voordat u uw eigen code verdenkt
Het reserve-hash-insert-pad voor HSM's en tokens
Het één-aanroep-pad neemt aan dat uw proces de sleutel als bestand kan lezen. Steeds vaker kan het dat niet. De sleutel zit in een HSM, op een USB-token, of achter de API van een ondertekenings-service, en er is geen manier voor een bibliotheek om er direct bij te komen. HotPDF handelt dat af door ondertekening in byte-niveau stappen te breken: schrijf een placeholder-document, vraag de bibliotheek om de hash-ranges, geef de hash-input door aan wat de sleutel ook vasthoudt, en splicee de teruggekeerde CMS dan terug in het gat
var
Doc: THotPDF;
Fs: TFileStream;
PdfBytes, HashInput, SigHex: AnsiString;
R1Start, R1Len, R2Start, R2Len, CStart, CLen: Integer;
begin
// 1. Schrijf het document met een gereserveerd /Contents-gat
Doc := THotPDF.Create(nil);
try
Doc.FileName := 'placeholder.pdf';
Doc.BeginDoc;
Doc.CurrentPage.AddSignedSignatureField('Sig1',
Rect(50, 100, 350, 150), 8192, 'adbe.pkcs7.detached',
'Contract approval', 'Boston, MA', 'legal@example.com');
Doc.EndDoc;
finally
Doc.Free;
end;
// 2. Laad de opgeslagen bytes; de geretourneerde offsets zijn 0-based
Fs := TFileStream.Create('placeholder.pdf', fmOpenRead);
try
SetLength(PdfBytes, Fs.Size);
Fs.ReadBuffer(PdfBytes[1], Fs.Size);
finally
Fs.Free;
end;
THotPDF.PreparePDFForSigning(PdfBytes, R1Start, R1Len, R2Start, R2Len,
CStart, CLen);
// 3. Hash beide bereiken en onderteken extern (HSM, token, service)
HashInput := Copy(PdfBytes, R1Start + 1, R1Len) +
Copy(PdfBytes, R2Start + 1, R2Len);
SigHex := SignWithHsm(HashInput); // uw integratie: retourneert CMS als hex
// 4. Voeg de handtekening in het gereserveerde gat
THotPDF.InsertSignatureHex(PdfBytes, SigHex);
Fs := TFileStream.Create('signed.pdf', fmCreate);
try
Fs.WriteBuffer(PdfBytes[1], Length(PdfBytes));
finally
Fs.Free;
end;
end;
Twee details in deze sequentie veroorzaken de meeste van de intermitterende fouten. De eerste is dat PreparePDFForSigning werkt op de bytes van een afgewerkt bestand. De placeholder moet volledig zijn geschreven en opgeslagen voordat de offsets iets betekenen; bereken ze tegen een stream die nog wordt samengesteld en ze zullen niet uitlijnen met de bytes die u uiteindelijk hasht. De tweede is de reserveringsgrootte, opnieuw. De 8192 bytes waar u om vroeg moet de finale CMS bevatten, en een handtekening die intermediate-certificaten draagt, of een die een service met signed attributes versiert, kan eroverheen lopen. InsertSignatureHex zal het gat niet laten groeien om plaats te maken. Het herkenningsteken is een pipeline die met het ene certificaat goed ondertekent en met het volgende faalt; de remedie is de placeholder te regenereren met een reservering gemeten aan een echte handtekening die door de werkelijke ondertekenaar werd geproduceerd, niet geraden
PAdES-baselines, en de tijdstempels die een handtekening in leven houden
Als u onder Europese regels ondertekent, is de standaard in het spel ETSI EN 319 142-1, die vier PAdES-baseline-niveaus stapelt. B-B is de gewone handtekening. B-T voegt een vertrouwde tijdstemper toe die bewijst wanneer hij werd gemaakt. B-LT embedt het validatiemateriaal, de certificaten en revocation-data, in het document zodat het jaren later nog kan worden gecontroleerd. B-LTA laagt periodieke document-tijdstempels erbovenop, zodat het bewijs de algoritmen overleeft waarop het werd gebouwd. HotPDF emit de document-zijde structuren voor elk niveau:
// PAdES-baseline handtekeningveld (ETSI EN 319 142-1)
Pdf.CurrentPage.AddPAdESSignatureField(
'ApprovalSig', Rect(50, 100, 350, 150), 'B-B',
'Contract approval', 'Boston, MA', 'legal@example.com');
// Documenttijdstempel: grotere reservering voor het TSA-token en de keten
Pdf.CurrentPage.AddDocumentTimestampSignature('ArchiveTS', 16384);
De 16384-byte-reservering op de tijdstemper is opzettelijk. Een timestamp-authority retourneert een token die zijn eigen certificaatketen meesleept, dus hij heeft routinematig meer ruimte nodig dan de 8 KB waarmee een gewone handtekening gelukkig is. Die document-tijdstempels zijn ook de machinerie achter B-LTA: een gearchiveerde handtekening elke paar jaar opnieuw tijdstemperen, met algoritmen die nog actueel zijn, is wat een document dat u in 2026 ondertekende in 2040 verifieerbaar houdt
Een woord over de reason-, location- en contact-strings die beide veld-aanroepen accepteren: het zijn gemaksmetadata en niets meer. HotPDF bewaart ze als gewone dictionary-entries en verft ze in het zichtbare handtekenings-uiterlijk, maar geen validator controleert ze ergens tegen. Vul ze consistent vanuit uw workflow-data, want auditors lezen ze wel, en verwissel ze dan nooit voor bewijs. De werkelijke cryptografische claim leeft volledig in de CMS en zijn certificaatketen, en een verificateur negeert de zichtbare tekst volledig
Na ondertekening mag het bestand alleen maar groeien
Vanaf het moment dat een handtekening bestaat, zijn de bytes binnen zijn ranges bevroren. De enige legitieme manier om het bestand achteraf te wijzigen is een ISO 32000-1 §7.5.6 incremental update, die nieuwe en gewijzigde objecten na de originele bytes appendt en een verse cross-reference-sectie eraan ketent. Op die manier gedaan blijft de handtekening geldig voor zijn revisie en rapporteert een viewer de eerlijke staat: de ondertekende revisie is intact, het document werd achteraf uitgebreid. Reserialiseer in plaats daarvan het hele bestand en u herschrijft de ondertekende spannen, wat de handtekening vernietigt ook al veranderde er niets zichtbaars. Hetzelfde revisiemechanisme is ook hoe één document meerdere handtekeningen draagt: elke nieuwe handtekening landt in zijn eigen incremental update, en zijn ranges dekken alles ervóór, inclusief de eerdere handtekeningen. De append-only-machinerie, en wanneer het veilig is om ze te compacteren, worden behandeld in het artikel over object streams en incremental updates
Twee grenzen zijn het waard om tijdens het ontwerpen in gedachten te houden. HotPDF's PDF/A-output-modus wijst handtekeningvelden ronduit af, dus archiefconformiteit en een ingebedde handtekening moeten als afzonderlijke bestanden worden verscheept. En ondertekening zegt niets over geheimhouding: het bewijst wie een document produceerde en dat het sindsdien niet is veranderd, maar iedereen kan het nog steeds lezen. De inhoud verbergen is een afzonderlijke taak, behandeld door AES-256-versleuteling en toestellingsbeleid
Wat u ook bouwt, test het met iets anders dan de code die het bestand schreef. Open de output in Acrobats handtekeningspaneel en bevestig drie dingen: de handtekening is geldig, de identiteit ketent naar de root die u verwachtte, en het paneel rapporteert geen wijzigingen sinds ondertekening. Flip dan één byte binnen het ondertekende bereik van een weggooikopie en bevestig dat het paneel het document nu gewijzigd noemt. Een ondertekenings-pipeline die u nooit een gemanipuleerd bestand hebt zien verwerpen, is er een waarvan de verificatie niet werkelijk is getest
Alle drie de ondertekenings-tiers worden geleverd met de HotPDF Delphi Component voor Delphi en C++Builder; de productpagina linkt de volledige handtekening-API-referentie