Artikel Teknis

Policy ECDSA dan EdDSA ISO/TS 32002 di PDFium Delphi

PDFium Component for Delphi memeriksa setiap signature PDF ECDSA dan EdDSA terhadap profil algoritma ISO/TS 32002 dan melaporkan vonisnya di TPadesSignatureValidation.AlgorithmPolicyStatus. Hanya P-256, P-384, P-521, tiga curve Brainpool r1, Ed25519, dan Ed448 yang bisa lolos, masing-masing dengan digest yang cocok, dan ketidakcocokan memunculkan ppeiSignatureAlgorithmMismatch. Policy ini lebih penting daripada kelihatannya. Signature di atas brainpoolP160r1, atau key P-256 yang menandatangani digest SHA-512, bisa terverifikasi dengan sempurna di level matematika, jadi Windows CryptoAPI melaporkan nilai signature sebagai baik sementara validator PDF 2.0 yang ketat menolak berkasnya. Pemeriksaan policy menutup celah itu, dan sengaja dipisahkan dari pertanyaan apakah byte signature benar secara kriptografis

Apa yang sebenarnya diizinkan ISO/TS 32002 untuk signature elliptic curve?

ISO/TS 32002 mengizinkan tepat enam curve ECDSA dan dua skema EdDSA di signature PDF, dan mengaitkan tiap curve ke ukuran digest yang boleh dibawanya. PDFium Component menenkode tabel itu di PadesCurveDigestAllowed, berkunci OID curve dari sertifikat signer. Curve NIST ketat: digest harus selebar bit yang sama dengan curve-nya, SHA-2 atau SHA-3. Curve Brainpool lebih longgar dan menerima lebarnya sendiri atau apa pun yang lebih lebar:

  • P-256 (1.2.840.10045.3.1.7): hanya SHA-256 atau SHA3-256
  • P-384 (1.3.132.0.34): hanya SHA-384 atau SHA3-384
  • P-521 (1.3.132.0.35): hanya SHA-512 atau SHA3-512
  • brainpoolP256r1 (1.3.36.3.3.2.8.1.1.7): digest SHA-2 atau SHA-3 apa pun dari 256 sampai 512 bit
  • brainpoolP384r1 (1.3.36.3.3.2.8.1.1.11): SHA-2 / SHA-3 384 atau 512 bit
  • brainpoolP512r1 (1.3.36.3.3.2.8.1.1.13): hanya SHA-512 atau SHA3-512
Matriks profil algoritma ISO TS 32002 di PDFium Component: P-256, P-384, dan P-521 hanya menerima lebar digest yang cocok di PadesCurveDigestAllowed, brainpoolP256r1 menerima 256 sampai 512 bit, brainpoolP384r1 menerima 384 dan 512, Ed25519 dan Ed448 mendeklarasikan SHA-512 dan SHAKE256 dengan panjang 512, dan ketidakcocokan memunculkan ppeiSignatureAlgorithmMismatch sementara curve di luar profil mengembalikan pcsUnsupported
Enam curve ECDSA dan dua skema EdDSA bisa lolos, dan tiap curve terikat pada lebar digest yang boleh dibawanya; sisanya invalid atau unsupported, tak pernah diterima diam-diam

EdDSA tidak punya pilihan curve dan tidak punya pilihan digest, dan persis itu sebabnya aturannya menyangkut encoding alih-alih kekuatan. Per RFC 8419, SignerInfo Ed25519 harus mendeklarasikan SHA-512 sebagai digestAlgorithm-nya tanpa parameter, dan SignerInfo Ed448, di jalur signed-attributes yang selalu dipakai PAdES, harus mendeklarasikan id-shake256-len (2.16.840.1.101.3.4.2.18) dengan parameter INTEGER tepat 512. Untuk kedua skema, AlgorithmIdentifier signature dan AlgorithmIdentifier public key sertifikat tidak boleh membawa parameter apa pun. Produsen yang menulis NULL di sana — kebiasaan yang encoder RSA tanamkan ke banyak library ASN.1 — menghasilkan signature yang tak patuh meski key dan nilai signature-nya baik-baik saja

Cara PDFium Component mengekstrak triple algoritma dari CMS

PDFium sendiri tidak bisa menjawab pertanyaan ini, karena API signature publiknya membaca dictionary signature tapi tidak memverifikasi CMS maupun mengekspos curve sertifikat signer. Karena itu lapisan inspeksi PAdES yang dibangun di atas PDFium meng-parse CMS SignedData (RFC 5652) sendiri. InspectPadesSignatureAlgorithm membaca digestAlgorithm dan signatureAlgorithm milik SignerInfo pertama, lalu menemukan sertifikat signer dan membaca SubjectPublicKeyInfo-nya untuk mendapatkan algoritma key dan curve-nya. Pencarian sertifikat sengaja dibatasi: paling banyak 64 sertifikat di set certificates CMS diperiksa, pencocokannya perbandingan byte persis atas issuer dan serial number dari issuerAndSerialNumber, dan kode jatuh ke "satu-satunya sertifikat yang ada" hanya saat set itu memuat tepat satu sertifikat yang bisa di-parse. Mengambil sertifikat EC pertama dari set tak berurut itu mudah, dan itu akan membiarkan sertifikat CA memutuskan curve yang dikira dipakai signer

Cara PDFium Component menginspeksi triple algoritma signature PDF: InspectPadesSignatureAlgorithm membaca digestAlgorithm dan signatureAlgorithm milik SignerInfo pertama di CMS, menyematkan sertifikat signer lewat pencocokan issuerAndSerialNumber persis di antara paling banyak 64 kandidat, membaca SubjectPublicKeyInfo untuk curve-nya, dan EvaluatePadesSignatureAlgorithm mengembalikan AlgorithmPolicyStatus
PDFium sendiri tidak memverifikasi CMS maupun mengekspos curve signer, jadi lapisan PAdES meng-parse SignedData dan menyimpan setiap OID mentah di record untuk penolakan yang bisa dijelaskan
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 menerapkan policy sebagai bagian dari pass kepatuhannya, mulai dari sertifikat yang ia temukan di dalam CMS, dan TPdf.ValidatePadesTrust menjalankannya ulang terhadap sertifikat signer yang benar-benar dipakai Windows CryptoAPI untuk verifikasi, jadi sertifikat yang dilaporkan CryptoAPI yang punya kata final. Setiap input mentah mendarat di TPadesSignatureAlgorithmInfo, termasuk DigestParametersPresent, DigestParameterBits, SignatureParametersPresent, dan PublicKeyParametersAreNamedCurve, jadi penolakan selalu bisa dijelaskan dari record, bukan dari satu baris log

Mengapa signature P-256 dengan SHA3-256 gagal memenuhi policy?

Signature P-256 gagal memenuhi policy PDFium Component kapan pun digestAlgorithm milik CMS dan digest yang tersirat di signatureAlgorithm ECDSA tidak sepakat, bahkan kalau keduanya masing-masing bisa diterima untuk curve itu. EvaluatePadesSignatureAlgorithm pertama memetakan ecdsa-with-SHA256, ecdsa-with-SHA3-256, dan saudara-saudaranya ke sebuah digest, membandingkannya dengan digestAlgorithm yang dideklarasikan, dan mengembalikan pcsInvalid atas perbedaan apa pun sebelum tabel curve dikonsultasikan. Kasusnya nyata: sebuah tool signing mengganti hash-nya ke SHA3-256 tapi mempertahankan identifier ecdsa-with-SHA256 yang hard-coded, dan hasilnya berkas yang tak bisa ditafsirkan konsisten oleh verifier mana pun yang patuh. Fungsinya publik, jadi matriksnya bisa disematkan di unit test tanpa membangun 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 tidak cocok

  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, kini kongruen
  Assert(EvaluatePadesSignatureAlgorithm(Info) = pcsUnsupported); // curve tidak ada di profil
end;

Encoding curve-nya mendapat ketelitian yang sama. RFC 5480 §2.1.1 membiarkan ECParameters berupa OID named curve, implicit curve (NULL), atau set parameter eksplisit lengkap, dan profil PKIX mensyaratkan bentuk named. PDFium Component mengembalikan pcsInvalid saat sertifikat id-ecPublicKey membawa tanpa parameter, parameter implisit, atau parameter eksplisit, karena parameter eksplisit membiarkan penyerang mendeskripsikan curve yang sekadar menyerupai yang standar. Named curve yang benar tapi sekadar tak ada di daftar ISO/TS 32002, seperti brainpoolP160r1 di atas atau secp256k1, mendapat pcsUnsupported

Invalid, unsupported, atau indeterminate: membaca statusnya dengan jujur

Ketiga status non-valid milik AlgorithmPolicyStatus bermakna berbeda, dan melumaskannya jadi satu keranjang "gagal" membuang informasi yang dibutuhkan auditor. pcsInvalid berarti kombinasi algoritma yang dikenali itu malformed atau tidak cocok; ia menambahkan ppeiSignatureAlgorithmMismatch ke TPadesValidationResult.Issues dan menyeret IntegrityStatus agregat ke pcsInvalid, jadi IsCryptographicallyValid mengembalikan False bahkan saat nilai signature CMS lolos pemeriksaan. pcsUnsupported berarti curve atau digestnya di luar yang disebut profil, itu hasil kapabilitas, bukan bukti perusakan. pcsIndeterminate berarti sertifikat signer tidak bisa disematkan, biasanya CertificateSet dengan beberapa kandidat tanpa kecocokan issuerAndSerialNumber yang persis, jadi kode menolak menebak curve-nya; sejak v3.124.0 ia juga menandai signature RSA di atas SHA-1 atau digest 112 bit seperti SHA-224, yang tak lagi disepakati untuk validasi masa kini. Pemisahan yang sama berlaku untuk EdDSA di mesin yang CryptoAPI-nya tak bisa memverifikasi Ed25519 atau Ed448: CmsSignatureStatus tetap pcsUnsupported sementara AlgorithmPolicyStatus masih bisa pcsValid, karena encoding-nya benar dan yang hilang hanya verifier-nya. Kalau Anda sedang mengejar penolakan dari Adobe atau validator berbasis DSS, panduan kenapa validator menolak signature PAdES membahas penyebab umum yang lain

Jalur keputusan EvaluatePadesSignatureAlgorithm di PDFium Component: digest yang tidak cocok menyetel pcsInvalid dan ppeiSignatureAlgorithmMismatch, named curve di luar profil ISO TS 32002 seperti brainpoolP160r1 menyetel pcsUnsupported, sertifikat signer yang tak bisa disematkan menyetel pcsIndeterminate, dan sejak v3.124.0 cabang RSA menerapkan suite digest ETSI TS 119 312, dengan ukuran key tetap diserahkan ke aplikasi
Ketiga status non-valid bermakna berbeda: invalid adalah bukti kombinasi yang rusak, unsupported adalah hasil kapabilitas, dan indeterminate berarti kode menolak menebak
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, tanpa revocation
    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;

Apa yang tidak dijamin pcsValid?

AlgorithmPolicyStatus = pcsValid hanya mensertifikasi bahwa signature ECDSA atau EdDSA memakai curve yang disetujui dengan digest yang kongruen dan terenkode benar, dan bahwa signature RSA memakai digest dari suite masa kini; ia tak mengatakan apa pun tentang benar-tidaknya nilai signature itu. Sebelum v3.124.0 cabang RSA milik EvaluatePadesSignatureAlgorithm sengaja dibuat longgar: signatureAlgorithm apa pun di bawah arc PKCS #1 1.2.840.113549.1.1.* mengembalikan pcsValid, termasuk sha1WithRSAEncryption yang legacy. Sejak PDFiumPas v3.124.0 cabang RSA menerapkan suite signature ETSI TS 119 312. Digest MD2, MD4, dan MD5 adalah pcsInvalid. SHA-1 dan digest 112 bit seperti SHA-224 adalah pcsIndeterminate, jadi signature SHA-1 mempertahankan hasil integritas menyeluruhnya dan ditandai untuk review alih-alih ditolak. digestAlgorithm yang berbeda dari digest yang ditetapkan algoritma signature, seperti sha256WithRSAEncryption di atas digest SHA-1, atau algoritma signature RSA di key signer non-RSA, adalah pcsInvalid, dilaporkan sebagai ppeiSignatureAlgorithmMismatch dan menggagalkan integritas. Sertifikat signer yang tak ditemukan memberi pcsIndeterminate, seperti yang sudah terjadi untuk ECDSA, dan digest yang tak dikenali atau OID RSA non-signature memberi pcsUnsupported. Panjang modulus tetap tidak diperiksa, parameter PSS tidak divalidasi di sini (artikel RSASSA-PSS-params RFC 4055 membahas cara mereka terenkode di sisi signing), dan digest SHA-1 atau MD5 tambahan memunculkan issue terpisah ppeiBadDigestAlgorithm. Demikian pula, matematika signature, rantai sertifikat, dan revocation tetap tugas CmsSignatureStatus, CertificateTrustStatus, dan RevocationStatus, yang datang dari Windows CryptoAPI. Perlakukan pcsValid sebagai "profil algoritmanya berlaku", jangan pernah sebagai "key ini cukup kuat"

Untuk aplikasi Delphi yang menerima invoice, kontrak, atau paket arsip PDF yang ditandatangani, susunan praktisnya pendek: jalankan ValidatePadesTrust, tolak saat ada ppeiSignatureAlgorithmMismatch, arahkan pcsUnsupported dan pcsIndeterminate ke manusia, dan tegakkan floor ukuran key RSA Anda sendiri karena policy tidak akan melakukannya. PDFium Component untuk Delphi dan Lazarus mengirim validator PAdES, pembangun laporan bukti, dan pipeline signing, jadi library yang sama bisa memproduksi signature-signature ini dan memeriksanya ujung ke ujung