Delphi için PDFium Component, her ECDSA ve EdDSA PDF imzasını ISO/TS 32002 algoritma profiline göre denetler ve sonucu TPadesSignatureValidation.AlgorithmPolicyStatus içinde bildirir. Yalnızca P-256, P-384, P-521, üç Brainpool r1 eğrisi, Ed25519 ve Ed448 geçebilir; her biri eşleşen bir digest ile. Uyuşmazlık ppeiSignatureAlgorithmMismatch yükseltir. Bu politika göründüğünden daha önemlidir. brainpoolP160r1 üzerindeki bir imza ya da SHA-512 digest imzalayan bir P-256 anahtarı, matematik düzeyinde kusursuz doğrulanabilir; Windows CryptoAPI imza değerini sağlam bildirirken katı bir PDF 2.0 doğrulayıcısı dosyayı reddeder. Politika denetimi o boşluğu kapatır ve bilinçli olarak, imza baytlarının kriptografik olarak doğru olup olmadığı sorusundan ayrı tutulur
ISO/TS 32002 eliptik eğri imzaları için aslında neye izin verir?
ISO/TS 32002, PDF imzalarında tam olarak altı ECDSA eğrisine ve iki EdDSA şemasına izin verir ve her eğriyi taşıyabileceği digest boyutlarına bağlar. PDFium Component o tabloyu, imzalayan sertifikasının eğri OID'iyle anahtarlanan PadesCurveDigestAllowed içinde kodlar. NIST eğrileri katıdır: digest, eğriyle aynı bit genişliğinde olmalıdır; SHA-2 ya da SHA-3. Brainpool eğrileri daha esnektir ve kendi genişliklerini ya da herhangi bir daha geniş olanı kabul eder:
- P-256 (
1.2.840.10045.3.1.7): yalnızca SHA-256 ya da SHA3-256 - P-384 (
1.3.132.0.34): yalnızca SHA-384 ya da SHA3-384 - P-521 (
1.3.132.0.35): yalnızca SHA-512 ya da SHA3-512 - brainpoolP256r1 (
1.3.36.3.3.2.8.1.1.7): 256 ile 512 bit arası herhangi bir SHA-2 ya da SHA-3 digest - brainpoolP384r1 (
1.3.36.3.3.2.8.1.1.11): 384 ya da 512 bitlik SHA-2 / SHA-3 - brainpoolP512r1 (
1.3.36.3.3.2.8.1.1.13): yalnızca SHA-512 ya da SHA3-512
EdDSA'nın ne eğri seçimi ne digest seçimi vardır; kurallarının güce değil kodlamaya dönük olmasının nedeni de tam olarak budur. RFC 8419 uyarınca bir Ed25519 SignerInfo, parametresiz olarak digestAlgorithm'ında SHA-512 bildirmek zorundadır ve PAdES'in her zaman izlediği signed-attributes yolundaki bir Ed448 SignerInfo, tam 512 değerinde INTEGER parametreli id-shake256-len'i (2.16.840.1.101.3.4.2.18) bildirmek zorundadır. Her iki şemada da imzanın AlgorithmIdentifier'ı ile sertifika açık anahtarı AlgorithmIdentifier'ı hiçbir parametre taşımamalıdır. RSA kodlayıcılarının pek çok ASN.1 kütüphanesine kazandırdığı alışkanlıkla oraya bir NULL yazan üretici, anahtar da imza değeri de sağlam olsa da standart dışı bir imza üretir
PDFium Component'in algoritma üçlüsünü CMS'ten çıkarışı
PDFium'un kendisi bu soruyu yanıtlayamaz; çünkü genel imza API'si imza sözlüğünü okur ama CMS'i doğrulamaz ne de imzalayan sertifikasının eğrisini açmaz. PDFium üzerine kurulu PAdES inceleme katmanı bu yüzden CMS SignedData'yı (RFC 5652) kendisi ayrıştırır. InspectPadesSignatureAlgorithm ilk SignerInfo'nun digestAlgorithm ile signatureAlgorithm'ını okur, sonra imzalayan sertifikasını bulur ve anahtar algoritması ile eğriyi almak için SubjectPublicKeyInfo'sunu okur. Sertifika araması bilinçli olarak sınırlıdır: CMS certificates kümesindeki en çok 64 sertifika incelenir, eşleşme issuerAndSerialNumber'dan gelen veren ve seri numarasının bayt bayt karşılaştırmasıdır ve kod, küme tam olarak tek ayrıştırılabilir sertifika tuttuğunda "oradaki tek sertifika"ya düşer. Dağınık bir kümeden ilk EC sertifikasını seçmek kolaydır ve bir CA sertifikasının imzalayanın kullandığı varsayılan eğriye karar vermesine izin verirdi
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 politikayı uyumluluk geçişinin parçası olarak uygular ve CMS'in içinde bulduğu sertifikadan başlar; TPdf.ValidatePadesTrust ise Windows CryptoAPI'nin doğrulamada fiilen kullandığı imzalayan sertifikasına karşı yeniden koşturur, böylece CryptoAPI'nin bildirdiği sertifika son sözü söyler. Her ham girdi TPadesSignatureAlgorithmInfo'ya düşer; DigestParametersPresent, DigestParameterBits, SignatureParametersPresent ve PublicKeyParametersAreNamedCurve dahil; böylece bir red, günlük satırından değil kayıttan açıklanabilir
SHA3-256'lı bir P-256 imzası politikayı neden geçemez?
Bir P-256 imzası, CMS digestAlgorithm'ı ile ECDSA signatureAlgorithm'ının ima ettiği digest uyuşmazsa PDFium Component politikasını geçemez; ikisi de eğri için tek tek kabul edilebilir olsa bile. EvaluatePadesSignatureAlgorithm önce ecdsa-with-SHA256, ecdsa-with-SHA3-256 ve kardeşlerini bir digest'e eşler, bunu bildirilen digestAlgorithm ile karşılaştırır ve eğri tablosuna danışmadan her farkta pcsInvalid döndürür. Durum gerçektir: bir imzalama aracı özetini SHA3-256'ya çevirir ama sabit kodlanmış ecdsa-with-SHA256 tanımlayıcısını korur ve sonuç, standartlara uyan hiçbir doğrulayıcının tutarlı yorumlayamayacağı bir dosyadır. Fonksiyon genel erişimlidir; böylece matris, PDF kurmadan bir unit testte sabitlenebilir:
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 uyuşmazlığı
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, artık uyumlu
Assert(EvaluatePadesSignatureAlgorithm(Info) = pcsUnsupported); // eğri profilde değil
end;
Eğri kodlaması aynı sıkılığı alır. RFC 5480 §2.1.1, ECParameters'in adlandırılmış bir eğri OID'i, örtük bir eğri (NULL) ya da tam bir açık parametre kümesi olmasına izin verir ve PKIX profilleri adlandırılmış biçimi gerektirir. PDFium Component, bir id-ecPublicKey sertifikası parametresiz, örtük parametreli ya da açık parametreli geldiğinde pcsInvalid döndürür; çünkü açık parametreler, saldırganın yalnızca standart bir eğriye benzeyen bir eğriyi betimlemesine izin verir. ISO/TS 32002 listesinde sadece bulunmayan, doğru adlandırılmış bir eğri — yukarıdaki brainpoolP160r1 ya da secp256k1 gibi — ise pcsUnsupported alır
Geçersiz, desteklenmez ya da belirsiz: durumu dürüstçe okumak
AlgorithmPolicyStatus'un üç geçersiz olmayan durumu farklı şeyler demektir ve onları tek bir "başarısız" kovasına dökmek, denetçilerin gereksindiği bilgiyi çöpe atar. pcsInvalid, tanınan bir algoritma kombinasyonunun bozuk ya da uyuşmaz olduğunu demektir; TPadesValidationResult.Issues'a ppeiSignatureAlgorithmMismatch ekler ve toplu IntegrityStatus'u pcsInvalid'a çeker, böylece CMS imza değeri doğru çıksa bile IsCryptographicallyValid False döndürür. pcsUnsupported, eğrinin ya da digestin profilin adlandırdıklarının dışında olduğunu demektir; bu bir yetenek sonucudur, kurcalama kanıtı değil. pcsIndeterminate, imzalayan sertifikasının tespit edilemediğini demektir; genellikle birkaç adaylı bir CertificateSet ve tam bir issuerAndSerialNumber eşleşmesi yokluğudur ve kod eğriyi tahmin etmeyi reddeder; v3.124.0'dan beri ayrıca, güncel doğrulama için artık üzerinde anlaşma olmayan SHA-1 üzerinde RSA ya da SHA-224 gibi 112 bitlik bir digesti de işaretler. Aynı ayrım, CryptoAPI'si Ed25519 ya da Ed448 doğrulayamayan bir makinede EdDSA için de geçerlidir: CmsSignatureStatus pcsUnsupported kalırken AlgorithmPolicyStatus hâlâ pcsValid olabilir; çünkü kodlama doğrudu, eksik olan yalnızca doğrulayıcıydı. Adobe'dan ya da DSS tabanlı bir doğrulayıcıdan gelen bir redin peşindeseniz doğrulayıcıların PAdES imzalarını neden reddettiği rehberi öteki yaygın nedenleri kapsar
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; // çevrimdışı, iptal sorgusu yok
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;
pcsValid neyi garanti etmez?
AlgorithmPolicyStatus = pcsValid, yalnızca bir ECDSA ya da EdDSA imzasının onaylı bir eğriyle, uyumlu ve doğru kodlanmış bir digest kullandığını ve bir RSA imzasının güncel bir takımdan digest kullandığını sertifikalar; imza değerinin doğru olup olmadığı hakkında hiçbir şey söylemez. v3.124.0 öncesinde EvaluatePadesSignatureAlgorithm'ın RSA dalı bilinçli olarak genişti: PKCS #1 yayı 1.2.840.113549.1.1.* altındaki her signatureAlgorithm, eski sha1WithRSAEncryption dahil, pcsValid döndürüyordu. PDFiumPas v3.124.0'dan beri RSA dalı ETSI TS 119 312 imza takımlarını uygular. MD2, MD4 ve MD5 digestleri pcsInvalid'dır. SHA-1 ile SHA-224 gibi 112 bitlik digestler pcsIndeterminate'tir; böylece bir SHA-1 imzası genel bütünlük sonucunu korur ve reddedilmek yerine inceleme için işaretlenir. İmza algoritmasının sabitlediği digestten farklı bir digestAlgorithm — SHA-1 digest üzerinde sha256WithRSAEncryption gibi — ya da RSA olmayan bir imzalayan anahtarında RSA imza algoritması pcsInvalid'dır, ppeiSignatureAlgorithmMismatch olarak bildirilir ve bütünlüğü düşürür. Bulunamayan bir imzalayan sertifikası, ECDSA için hâlihazırda yaptığı gibi pcsIndeterminate verir; tanınmayan digestler ya da imza olmayan RSA OID'leri pcsUnsupported verir. Modül uzunluğu hâlâ denetlenmez; PSS parametreleri burada doğrulanmaz (RFC 4055 RSASSA-PSS-params makalesi imzalama tarafında nasıl kodlandıklarını anlatır) ve SHA-1 ya da MD5 digestleri ayrıca ayrı ppeiBadDigestAlgorithm sorununu yükseltir. Aynı şekilde imza matematiği, sertifika zinciri ve iptal, Windows CryptoAPI'den gelen CmsSignatureStatus, CertificateTrustStatus ve RevocationStatus'un işi olarak kalır. pcsValid'u "algoritma profili sağlam" diye görün, asla "bu anahtar yeterince güçlü" diye değil
İmzalı PDF fatura, sözleşme ya da arşiv paketi kabul eden bir Delphi uygulaması için pratik kurulum kısadır: ValidatePadesTrust koşturun, ppeiSignatureAlgorithmMismatch'ta reddedin, pcsUnsupported ile pcsIndeterminate'i bir insana yönlendirin ve RSA anahtar boyutu tabanınızı kendiniz zorunlu kılın; politika bunu yapmayacaktır. PDFium Component for Delphi and Lazarus, PAdES doğrulayıcıyı, kanıt raporu kurucusunu ve imzalama hattını taşır; böylece aynı kütüphane bu imzaları hem üretir hem uçtan uca denetler