PDFium Component for Delphi sjekker hver ECDSA- og EdDSA-PDF-signatur mot algoritmeprofilen i ISO/TS 32002 og rapporterer dommen i TPadesSignatureValidation.AlgorithmPolicyStatus. Bare P-256, P-384, P-521, de tre Brainpool r1-kurvene, Ed25519 og Ed448 kan passere, hver med en matchende digest, og en mismatch reiser ppeiSignatureAlgorithmMismatch. Den policyen betyr mer enn den høres ut som. En signatur på brainpoolP160r1, eller en P-256-nøkkel som signerer en SHA-512-digest, kan verifisere helt fint på matematikknivå, så Windows CryptoAPI rapporterer signaturverdien som god mens en streng PDF 2.0-validator avviser filen. Policykontrollen lukker det gapet, og den er bevisst holdt adskilt fra spørsmålet om signaturbytene er kryptografisk korrekte
Hva tillater egentlig ISO/TS 32002 for elliptisk kurve-signaturer?
ISO/TS 32002 tillater nøyaktig seks ECDSA-kurver og to EdDSA-ordninger i PDF-signaturer, og den knytter hver kurve til digest-størrelsene den kan bære. PDFium Component innkoder den tabellen i PadesCurveDigestAllowed, nøklet på kurve-OID-en fra signerens sertifikat. NIST-kurvene er strenge: digesten må ha samme bitbredde som kurven, enten SHA-2 eller SHA-3. Brainpool-kurvene er slankere og godtar egen bredde eller hva som helst bredere:
- P-256 (
1.2.840.10045.3.1.7): bare SHA-256 eller SHA3-256 - P-384 (
1.3.132.0.34): bare SHA-384 eller SHA3-384 - P-521 (
1.3.132.0.35): bare SHA-512 eller SHA3-512 - brainpoolP256r1 (
1.3.36.3.3.2.8.1.1.7): enhver SHA-2- eller SHA-3-digest fra 256 til 512 biter - brainpoolP384r1 (
1.3.36.3.3.2.8.1.1.11): 384- eller 512-bits SHA-2 / SHA-3 - brainpoolP512r1 (
1.3.36.3.3.2.8.1.1.13): bare SHA-512 eller SHA3-512
EdDSA har ikke noe kurvevalg og ikke noe digest-valg, noe som er nøyaktig grunnen til at reglene handler om koding i stedet for styrke. Etter RFC 8419 må en Ed25519 SignerInfo deklarere SHA-512 som sin digestAlgorithm uten parametre, og en Ed448 SignerInfo, på signed-attributes-veien PAdES alltid bruker, må deklarere id-shake256-len (2.16.840.1.101.3.4.2.18) med en INTEGER-parameter på nøyaktig 512. For begge ordninger må signaturens AlgorithmIdentifier og sertifikatets public key AlgorithmIdentifier ikke bære noen parametre i det hele tatt. En produsent som skriver en NULL der, vanen RSA-enkodere har trent inn i mange ASN.1-biblioteker, produserer en ikke-konform signatur selv om nøkkelen og signaturverdien er fine
Hvordan PDFium Component trekker ut algoritmetrikkelen fra CMS
PDFium selv kan ikke svare på dette spørsmålet, for det offentlige signatur-API-et leser signaturordboken men verifiserer verken CMS-en eller eksponerer kurven i signerens sertifikat. PAdES-inspeksjonslaget bygget på PDFium parser derfor CMS SignedData (RFC 5652) selv. InspectPadesSignatureAlgorithm leser digestAlgorithm og signatureAlgorithm i den første SignerInfo-en, finner så signerens sertifikat og leser SubjectPublicKeyInfo for å få nøkkelalgoritmen og kurven. Sertifikatoppslaget er bevisst avgrenset: høyst 64 sertifikater i CMS certificates-settet undersøkes, treffet er en eksakt byte-sammenligning av utsteder og serienummer fra issuerAndSerialNumber, og koden faller tilbake på «det eneste sertifikatet som finnes» bare når settet holder nøyaktig ett parserbart sertifikat. Å plukke det første EC-sertifikatet fra et uordnet sett ville vært enkelt, og det ville la et CA-sertifikat avgjøre hvilken kurve signataren angivelig brukte
uses
PDFium, FPdfPades;
const
StatusNames: array[TPadesCryptoStatus] of string =
('not checked', 'valid', 'invalid', 'unsupported', 'indeterminate');
var
Pdf: TPdf;
R: TPadesValidationResult;
A: TPadesSignatureAlgorithmInfo;
I: Integer;
begin
Pdf := TPdf.Create(nil);
try
Pdf.FileName := 'invoice-ecdsa-signed.pdf';
Pdf.Active := True;
R := Pdf.ValidatePades;
for I := 0 to Length(R.Signatures) - 1 do
begin
A := R.Signatures[I].AlgorithmInfo;
Writeln('Signature ', I);
Writeln(' digestAlgorithm : ', string(A.DigestAlgorithmOid));
Writeln(' signatureAlgorithm : ', string(A.SignatureAlgorithmOid));
Writeln(' public key / curve : ', string(A.PublicKeyAlgorithmOid),
' / ', string(A.CurveOid));
Writeln(' policy : ',
StatusNames[R.Signatures[I].AlgorithmPolicyStatus]);
end;
if ppeiSignatureAlgorithmMismatch in R.Issues then
Writeln('At least one signature violates the ISO/TS 32002 profile');
finally
Pdf.Free;
end;
end;
TPdf.ValidatePades anvender policyen som del av sin compliance-pass, med utgangspunkt i sertifikatet den lokaliserte inne i CMS-en, og TPdf.ValidatePadesTrust kjører den på nytt mot signerersertifikatet Windows CryptoAPI faktisk brukte til verifisering, så sertifikatet CryptoAPI rapporterer har det siste ordet. Hvert råinput lander i TPadesSignatureAlgorithmInfo, inkludert DigestParametersPresent, DigestParameterBits, SignatureParametersPresent og PublicKeyParametersAreNamedCurve, så en avvisning er alltid forklarbar fra recorden i stedet for fra en logglinje
Hvorfor feiler en P-256-signatur med SHA3-256 policyen?
En P-256-signatur feiler PDFium Components policy hver gang CMS digestAlgorithm og digesten antydet av ECDSA signatureAlgorithm er uenige, selv om begge er hver for seg akseptable for kurven. EvaluatePadesSignatureAlgorithm mapper først ecdsa-with-SHA256, ecdsa-with-SHA3-256 og søsknene deres til en digest, sammenligner den med den deklarerte digestAlgorithm og gir pcsInvalid ved enhver forskjell før kurvetabellen konsulteres. Tilfellet er reelt: et signeringsverktøy bytter hashen sin til SHA3-256 men beholder en hardkodet ecdsa-with-SHA256-identifikator, og resultatet er en fil ingen konform verifikator kan tolke konsistent. Funksjonen er offentlig, så matrisen kan festes i en unit-test uten å bygge en PDF:
var
Info: TPadesSignatureAlgorithmInfo;
begin
Info := Default(TPadesSignatureAlgorithmInfo);
Info.Family := psafEcdsa;
Info.PublicKeyAlgorithmOid := '1.2.840.10045.2.1'; // id-ecPublicKey
Info.PublicKeyParametersPresent := True;
Info.PublicKeyParametersAreNamedCurve := True;
Info.CurveOid := '1.2.840.10045.3.1.7'; // P-256
Info.DigestAlgorithmOid := '2.16.840.1.101.3.4.2.8'; // SHA3-256
Info.SignatureAlgorithmOid := '2.16.840.1.101.3.4.3.10'; // ecdsa-with-SHA3-256
Assert(EvaluatePadesSignatureAlgorithm(Info) = pcsValid);
Info.SignatureAlgorithmOid := '1.2.840.10045.4.3.2'; // ecdsa-with-SHA256
Assert(EvaluatePadesSignatureAlgorithm(Info) = pcsInvalid); // digest-mismatch
Info.CurveOid := '1.3.36.3.3.2.8.1.1.1'; // brainpoolP160r1
Info.DigestAlgorithmOid := '2.16.840.1.101.3.4.2.1'; // SHA-256, nå kongruent
Assert(EvaluatePadesSignatureAlgorithm(Info) = pcsUnsupported); // kurven er ikke i profilen
end;
Kurvekodingen får samme strenghet. RFC 5480 §2.1.1 lar ECParameters være en navngitt kurve-OID, en implisitt kurve (NULL) eller et fullstendig eksplisitt parametersett, og PKIX-profiler krever den navngitte formen. PDFium Component gir pcsInvalid når et id-ecPublicKey-sertifikat bærer ingen parametre, implisitte parametre eller eksplisitte parametre, for eksplisitte parametre lar en angriper beskrive en kurve som bare ligner en standard én. En korrekt navngitt kurve som bare mangler i ISO/TS 32002-listen, som brainpoolP160r1 over eller secp256k1, får pcsUnsupported i stedet
Ugyldig, ustøttet eller ubestemt: å lese statusen ærlig
De tre ikke-gyldige statusene til AlgorithmPolicyStatus betyr forskjellige ting, og å kollapse dem i én «feilet»-bøtte kaster bort informasjonen revisorer trenger. pcsInvalid betyr at en gjenkjent algoritmekombinasjon er misdannet eller mismatchet; den legger ppeiSignatureAlgorithmMismatch til TPadesValidationResult.Issues og driver den samlede IntegrityStatus til pcsInvalid, så IsCryptographicallyValid gir False selv når CMS signaturverdi sjekker ut. pcsUnsupported betyr at kurven eller digesten er utenfor det profilen navngir, noe som er et kapasitetsresultat, ikke bevis på manipulering. pcsIndeterminate betyr at signerens sertifikat ikke kunne festes, vanligvis et CertificateSet med flere kandidater og intet eksakt issuerAndSerialNumber-treff, så koden nekter å gjette kurven; siden v3.124.0 markerer den også en RSA-signatur over SHA-1 eller en 112-bits digest som SHA-224, som ikke lenger er avtalt for aktuell validering. Samme deling gjelder EdDSA på en maskin der CryptoAPI ikke kan verifisere Ed25519 eller Ed448: CmsSignatureStatus forblir pcsUnsupported mens AlgorithmPolicyStatus fortsatt kan være pcsValid, for kodingen var korrekt og bare verifikatoren manglet. Jager du en avvisning fra Adobe eller en DSS-basert validator, dekker veiledningen om hvorfor validatorer avviser PAdES-signaturer de andre vanlige årsakene
var
Pdf: TPdf;
Options: TPadesTrustValidationOptions;
R: TPadesValidationResult;
S: TPadesSignatureValidation;
I: Integer;
begin
Pdf := TPdf.Create(nil);
try
Pdf.FileName := 'invoice-ecdsa-signed.pdf';
Pdf.Active := True;
Options := TPadesTrustValidationOptions.Default; // offline, ingen tilbakekalling
R := Pdf.ValidatePadesTrust(Options);
for I := 0 to Length(R.Signatures) - 1 do
begin
S := R.Signatures[I];
if S.IsDocumentTimeStamp then
Continue;
case S.AlgorithmPolicyStatus of
pcsInvalid: Writeln(I, ': reject, algorithm combination is invalid');
pcsUnsupported: Writeln(I, ': manual review, curve or digest outside the profile');
pcsIndeterminate: Writeln(I, ': manual review, signer not identified or digest no longer agreed');
pcsValid:
if S.AlgorithmInfo.Family = psafRsa then
Writeln(I, ': RSA digest suite accepted, check key size yourself')
else
Writeln(I, ': EC/EdDSA profile satisfied');
end;
end;
Writeln('Cryptographically valid: ', R.IsCryptographicallyValid);
finally
Pdf.Free;
end;
end;
Hva garanterer ikke pcsValid?
AlgorithmPolicyStatus = pcsValid sertifiserer bare at en ECDSA- eller EdDSA-signatur bruker en godkjent kurve med en kongruent, korrekt innkodet digest, og at en RSA-signatur bruker en digest fra en aktuell pakke; den sier ingenting om signaturverdien er korrekt. Før v3.124.0 var RSA-grenen av EvaluatePadesSignatureAlgorithm bevisst vid: enhver signatureAlgorithm under PKCS #1-buen 1.2.840.113549.1.1.* ga pcsValid, legacy sha1WithRSAEncryption inkludert. Siden PDFiumPas v3.124.0 anvender RSA-grenen ETSI TS 119 312 signaturpakker. MD2-, MD4- og MD5-digester er pcsInvalid. SHA-1 og 112-bits digester som SHA-224 er pcsIndeterminate, så en SHA-1-signatur beholder sitt samlede integritetsresultat og flagges til gjennomgang i stedet for å avvises. En digestAlgorithm som avviker fra digesten fastlagt av signaturalgoritmen, som sha256WithRSAEncryption over en SHA-1-digest, eller en RSA-signaturalgoritme på en ikke-RSA-signerernøkkel, er pcsInvalid, rapportert som ppeiSignatureAlgorithmMismatch og feiler integriteten. Et signerersertifikat som ikke kan finnes, gir pcsIndeterminate, som det allerede gjorde for ECDSA, og ukjente digester eller ikke-signatur-RSA-OID-er gir pcsUnsupported. Moduluslengde sjekkes fortsatt ikke, PSS-parametre valideres ikke her (artikkelen om RFC 4055 RSASSA-PSS-params dekker hvordan de enkodes på signeringssiden), og SHA-1- eller MD5-digester reiser i tillegg det separate issue-en ppeiBadDigestAlgorithm. Likeså forblir signaturmatematikken, sertifikatkjeden og tilbakekalling jobben til CmsSignatureStatus, CertificateTrustStatus og RevocationStatus, som kommer fra Windows CryptoAPI. Behandle pcsValid som «algoritmeprofilen holder», aldri som «denne nøkkelen er sterk nok»
For en Delphi-applikasjon som godtar signerte PDF-fakturaer, kontrakter eller arkivpakker, er det praktiske oppsettet kort: kjør ValidatePadesTrust, avvis på ppeiSignatureAlgorithmMismatch, rout pcsUnsupported og pcsIndeterminate til et menneske, og håndhev din egen RSA-nøkkelstørrelse-gulv, for policyen gjør det ikke. PDFium Component for Delphi og Lazarus leverer PAdES-validatoren, evidensrapport-byggeren og signeringspipelinen, så samme bibliotek kan produsere disse signaturene og sjekke dem ende til ende