PDFium Component Delphille tarkistaa jokaisen ECDSA- ja EdDSA-PDF-allekirjoituksen ISO/TS 32002:n algoritmiprofiilia vasten ja raportoi tuomion arvossa TPadesSignatureValidation.AlgorithmPolicyStatus. Vain P-256, P-384, P-521, kolme Brainpool r1 -käyrää, Ed25519 ja Ed448 voivat läpäistä, kullakin täsmäävällä tiivisteellä, ja mismatsi nostaa ppeiSignatureAlgorithmMismatchin. Kyseisellä politiikalla on enemmän painoarvoa kuin kuulostaa. Allekirjoitus brainpoolP160r1:llä, tai P-256-avain joka allekirjoittaa SHA-512-tiivisteen, voi varifoida täydellisesti matematiikan tasolla, joten Windows CryptoAPI raportoi allekirjoitusarvon kelvolliseksi kun taas tiukka PDF 2.0 -validaattori hylkää tiedoston. Politiikkatarkistus sulkee kyseisen kuilun, ja se on tarkoituksella erillinen kysymyksestä ovatko allekirjoitustavut kryptografisesti oikein
Mitä ISO/TS 32002 oikeasti sallii elliptisen käyrän allekirjoituksille?
ISO/TS 32002 sallii täsmälleen kuusi ECDSA-käyrää ja kaksi EdDSA-kaavaa PDF-allekirjoituksissa, ja se sitoo jokaisen käyrän tiivistekokoihin joita se saa kantaa. PDFium Component enkoodaa kyseisen taulukon funktioon PadesCurveDigestAllowed, avaimena allekirjoittajan sertifiikaatin käyrä-OID. NIST-käyrät ovat tiukkoja: tiivisteen on oltava samaa bittileveyttä kuin käyrä, joko SHA-2 tai SHA-3. Brainpool-käyrät ovat löysempiä ja hyväksyvät oman leveytensä tai mitä tahansa leveämpää:
- P-256 (
1.2.840.10045.3.1.7): vain SHA-256 tai SHA3-256 - P-384 (
1.3.132.0.34): vain SHA-384 tai SHA3-384 - P-521 (
1.3.132.0.35): vain SHA-512 tai SHA3-512 - brainpoolP256r1 (
1.3.36.3.3.2.8.1.1.7): mikä tahansa SHA-2- tai SHA-3-tiiviste 256:sta 512 bittiin - brainpoolP384r1 (
1.3.36.3.3.2.8.1.1.11): 384- tai 512-bittinen SHA-2 / SHA-3 - brainpoolP512r1 (
1.3.36.3.3.2.8.1.1.13): vain SHA-512 tai SHA3-512
EdDSA:lla ei ole käyrävalintaa eikä tiivistevalintaa, mikä on täsmälleen syy siihen että sen säännöt koskevat enkoodausta eikä vahvuutta. RFC 8419:n mukaan Ed25519-SignerInfoin on declareerattava SHA-512 sen digestAlgorithmiksi ilman parametreja, ja Ed448-SignerInfoin, signed-attributes-polulla jota PAdES aina käyttää, on declareerattava id-shake256-len (2.16.840.1.101.3.4.2.18) INTEGER-parametrilla täsmälleen 512. Molemmissa kaavoissa allekirjoituksen AlgorithmIdentifierin ja sertifikaatin julkisen avaimen AlgorithmIdentifierin ei saa kantaa yhtään parametria. Tuottaja joka kirjoittaa sinne NULLin, tapa jonka RSA-enkooderit ovat opettaneet moniin ASN.1-kirjastoihin, tuottaa sääntöjen vastaisen allekirjoituksen vaikka avain ja allekirjoitusarvo olisivatkin kunnossa
Miten PDFium Component poimii algoritmitrion CMS:stä
PDFium itse ei vastaa tähän kysymykseen, koska sen julkinen allekirjoitus-API lukee allekirjoitussanakirjan mutta ei varifoi CMS:ää eikä paljasta allekirjoittajan sertifiikaatin käyrää. Siksi PDFiumin päälle rakennettu PAdES-tarkastuskerros jäsentää CMS SignedDatan (RFC 5652) itse. InspectPadesSignatureAlgorithm lukee ensimmäisen SignerInfoin digestAlgorithmin ja signatureAlgorithmin, löytää sitten allekirjoittajan sertifiikaatin ja lukee sen SubjectPublicKeyInfon saadakseen avainalgoritmin ja käyrän. Sertifiikaatin haku on tarkoituksella rajattu: CMS:n certificates-joukon enintään 64 sertifiikaattia tutkitaan, täsmäys on täsmällinen tavuvertailu myöntäjästä ja sarjanumerosta issuerAndSerialNumberista, ja koodi palautuu varaan "ainoa siellä oleva sertifiikaatti" vain kun joukko kantaa täsmälleen yhden jäsennettävän sertifiikaatin. Ensimmäisen EC-sertifiikaatin poimiminen järjestämättömästä joukosta olisi helppoa, ja se antaisi CA-sertifiikaatin päättää minkä käyrän allekirjoittajan oletettavasti käytti
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 soveltaa politiikkaa osana sääntöjenmukaisuuskäyntiään alkaen sertifiikaatista jonka se paikantaa CMS:n sisältä, ja TPdf.ValidatePadesTrust ajaa sen uudelleen sitä allekirjoittajan sertifiikaattia vasten jota Windows CryptoAPI oikeasti käytti varifiointiin, joten CryptoAPI:n raportoimalla sertifiikaatilla on viimeinen sana. Jokainen raakasyöte laskeutuu TPadesSignatureAlgorithmInfoon, mukaan lukien DigestParametersPresent, DigestParameterBits, SignatureParametersPresent ja PublicKeyParametersAreNamedCurve, joten hylkäys on aina selitettävissä tietueesta eikä lokirivistä
Miksi P-256-allekirjoitus SHA3-256:lla kaatuu politiikkaan?
P-256-allekirjoitus kaatuu PDFium Componentin politiikkaan aina kun CMS:n digestAlgorithm ja ECDSA:n signatureAlgorithmin implikoima tiiviste eroavat, vaikka molemmat olisivatkin yksinään hyväksyttäviä käyrälle. EvaluatePadesSignatureAlgorithm mapaa ensin ecdsa-with-SHA256in, ecdsa-with-SHA3-256in ja niiden sisarukset tiivisteeksi, vertaa sitä declareerattuun digestAlgorithmiin ja palauttaa pcsInvalidin missä tahansa erossa ennen kuin käyrätaulukkoon katsotaan. Tapaus on todellinen: allekirjoitustyökalu vaihtaa hashinsa SHA3-256:een mutta pitää kovakoodatun ecdsa-with-SHA256-tunnisteen, ja tuloksena on tiedosto jota mikään sääntöjen mukainen varifoija ei voi tulkita yhdenmukaisesti. Funktio on julkinen, joten matriisin voi lukita yksikkötestiin rakentamatta 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); // tiiviste ei täsmää
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, nyt kongruentti
Assert(EvaluatePadesSignatureAlgorithm(Info) = pcsUnsupported); // käyrä ei ole profiilissa
end;
Käyrän enkoodaus saa saman tiukkuuden. RFC 5480 §2.1.1 sallii ECParametersin olla nimetty käyrä-OID, implisiittinen käyrä (NULL) tai täysi eksplisiittinen parametriryhmä, ja PKIX-profiilit vaativat nimetyn muodon. PDFium Component palauttaa pcsInvalidin kun id-ecPublicKey-sertifiikaatti kantaa ei parametreja, implisiittisiä parametreja tai eksplisiittisiä parametreja, koska eksplisiittiset parametrit antavat hyökkääjän kuvata käyrän joka vain muistuttaa standardia. Oikein nimetty käyrä joka vain puuttuu ISO/TS 32002:n luettelosta, kuten yllä oleva brainpoolP160r1 tai secp256k1, saa pcsUnsupportedin sen sijaan
Invalid, unsupported vai indeterminate: statuksen rehellinen lukeminen
Kolme ei-validia statusta AlgorithmPolicyStatusissa tarkoittavat eri asioita, ja niiden litistäminen yhdeksi "epäonnistui"-koriin heittää pois informaation jota auditoijat tarvitsevat. pcsInvalid tarkoittaa että tunnistettu algoritmiyhdistelmä on muodoltaan rikki tai ei täsmää; se lisää ppeiSignatureAlgorithmMismatchin TPadesValidationResult.Issueseen ja ajaa agregoidun IntegrityStatusin arvoon pcsInvalid, joten IsCryptographicallyValid palauttaa False:n vaikka CMS-allekirjoitusarvo täsmäisi. pcsUnsupported tarkoittaa että käyrä tai tiiviste on profiilin nimeämien ulkopuolella, mikä on kykytulos, ei todiste peukaloinnista. pcsIndeterminate tarkoittaa että allekirjoittajan sertifiikaattia ei saatu kiinnitettyä, yleensä CertificateSet jolla on useita kandidaatteja eikä täsmällistä issuerAndSerialNumber-täsmäystä, joten koodi kieltäytyy arvaamasta käyrää; versiosta v3.124.0 alkaen se merkitsee myös RSA-allekirjoituksen SHA-1:n yli tai 112-bittisen tiivisteen kuten SHA-224, jolle ei enää ole yhteisymmärrystä nykyisessä varifioinnissa. Sama jako pätee EdDSA:han koneella jonka CryptoAPI ei voi varifoida Ed25519:a tai Ed448:a: CmsSignatureStatus pysyy pcsUnsupportedina kun taas AlgorithmPolicyStatus voi yhä olla pcsValid, koska enkoodaus oli oikein ja vain varifoija puuttui. Jos jaat Adobe:n tai DSS-pohjaisen validaattorin hylkäystä, artikkeli miksi validaattorit hylkäävät PAdES-allekirjoitukset kattaa muut yleiset syyt
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, ei peruutustarkistusta
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;
Mitä pcsValid ei takaa?
AlgorithmPolicyStatus = pcsValid sertifioi vain sen että ECDSA- tai EdDSA-allekirjoitus käyttää hyväksyttyä käyrää kongruentilla, oikein enkoodatulla tiivisteellä, ja että RSA-allekirjoitus käyttää tiivistettä nykyisestä suitesta; se ei sano mitään siitä onko allekirjoitusarvo oikein. Ennen v3.124.0:aa EvaluatePadesSignatureAlgorithmin RSA-haara oli tarkoituksella leveä: mikä tahansa signatureAlgorithm PKCS #1 -haaran 1.2.840.113549.1.1.* alla palautti pcsValidin, perinteinen sha1WithRSAEncryption mukaan lukien. PDFiumPas v3.124.0:sta alkaen RSA-haara soveltaa ETSI TS 119 312:n allekirjoitussuiteja. MD2-, MD4- ja MD5-tiivistet ovat pcsInvalidia. SHA-1 ja 112-bittiset tiivistet kuten SHA-224 ovat pcsIndeterminatea, joten SHA-1-allekirjoitus säilyttää kokonaisintegriteettituloksensa ja merkitään katselmointiin hylkäämisen sijaan. digestAlgorithm joka eroaa allekirjoitusalgoritmin kiinnittämästä tiivisteestä, kuten sha256WithRSAEncryption SHA-1-tiivisteen yli, tai RSA-allekirjoitusalgoritmi ei-RSA-allekirjoittajan avaimella, on pcsInvalidia, raportoidaan ppeiSignatureAlgorithmMismatchina ja kaatuu integriteettiin. Allekirjoittajan sertifiikaatti jota ei löydy antaa pcsIndeterminaten, niin kuin se jo antoi ECDSA:lle, ja tunnistamattomat tiivistet tai ei-allekirjoitus-RSA-OID:t antavat pcsUnsupportedin. Moduluksen pituutta ei yhä tarkisteta, PSS-parametreja ei validoida tässä (artikkeli RFC 4055:n RSASSA-PSS-parametreista kattaa miten ne enkoodataan allekirjoituspuolella), ja SHA-1- tai MD5-tiivistet nostavat lisäksi erillisen ppeiBadDigestAlgorithm-kysymyksen. Samoin allekirjoitusmatematiikka, sertifiikaattiketju ja peruutus jäävät CmsSignatureStatusin, CertificateTrustStatusin ja RevocationStatusin tehtäviksi, jotka tulevat Windows CryptoAPI:sta. Kohtele pcsValidia nimellä "algoritmiprofiili pitää", ei koskaan nimellä "tämä avain on tarpeeksi vahva"
Delphi-sovellukselle joka vastaanottaa allekirjoitettuja PDF-laskuja, sopimuksia tai arkistopaketteja käytännön asennus on lyhyt: aja ValidatePadesTrust, hylkää ppeiSignatureAlgorithmMismatchista, reititä pcsUnsupported ja pcsIndeterminate ihmiselle, ja panna täytäntöön oma RSA-avainkokosi alaraja koska politiikka ei pann sitä. PDFium Component for Delphi and Lazarus toimittaa PAdES-validaattorin, todisteraportin rakentajan ja allekirjoitusputken, joten sama kirjasto voi tuottaa kyseiset allekirjoitukset ja tarkistaa ne päästä päähän