Teknik Makale

ISO/TS 32002 ECDSA ve EdDSA Denetimleri, PDFium Delphi

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
PDFium Component'te ISO TS 32002 algoritma profili matrisi: PadesCurveDigestAllowed içinde P-256, P-384 ve P-521 yalnızca eşleşen digest genişliklerini kabul eder; brainpoolP256r1 256-512 biti, brainpoolP384r1 384 ve 512'yi kabul eder; Ed25519 ile Ed448, 512 uzunluklu SHA-512 ve SHAKE256 bildirir; uyuşmazlıklar ppeiSignatureAlgorithmMismatch yükseltirken profil dışı eğriler pcsUnsupported döndürür
Altı ECDSA eğrisi ve iki EdDSA şeması geçebilir ve her eğri taşıyabileceği digest genişliklerine bağlıdır; gerisi geçersiz ya da desteklenmezdir, asla sessizce kabul edilmez

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

PDFium Component bir PDF imza algoritma üçlüsünü nasıl inceler: InspectPadesSignatureAlgorithm, CMS'teki ilk SignerInfo'nun digestAlgorithm ile signatureAlgorithm'ını okur; imzalayan sertifikasını en çok 64 aday arasından tam issuerAndSerialNumber eşleşmesiyle sabitler; eğri için SubjectPublicKeyInfo'yu okur ve EvaluatePadesSignatureAlgorithm, AlgorithmPolicyStatus'u döndürür
PDFium kendi başına CMS'i doğrulamaz ne de imzalayan eğrisini açar; PAdES katmanı bu yüzden SignedData'yı ayrıştırır ve her ham OID'i, açıklanabilir bir red için kayıtta saklar
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

PDFium Component'te EvaluatePadesSignatureAlgorithm karar yolu: digest uyuşmazlığı pcsInvalid ile ppeiSignatureAlgorithmMismatch kurar; brainpoolP160r1 gibi ISO TS 32002 profili dışında adlandırılmış bir eğri pcsUnsupported kurar; tespit edilemeyen imzalayan sertifikası pcsIndeterminate kurar; v3.124.0'dan beri RSA dalı ETSI TS 119 312 digest takımlarını uygular, anahtar boyutu hâlâ uygulamaya kalır
Geçerli olmayan üç durum farklı şeyler demektir: invalid bozuk kombinasyonun kanıtıdır, unsupported bir yetenek sonucudur, indeterminate ise kodun tahmin etmeyi reddettiği anlamına gelir
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