Τεχνικό άρθρο

Τεχνικό άρθρο: PAdES Digital Signatures in Delphi with PDFlibPas στα ελληνικά

Αυτή η τοπικοποιημένη έκδοση εστιάζει στο PAdES Digital Signatures in Delphi: Signing and Validation with PDFlibPas και χρησιμοποιεί το ενημερωμένο αγγλικό άρθρο ως τεχνική βάση για ομάδες Delphi, PDF και λογισμικού εγγράφων

Η σελίδα μετατρέπει το ενημερωμένο άρθρο βάσης σε συγκεκριμένα σημεία ελέγχου για σχεδιασμό, υλοποίηση και επικύρωση

Τι συγχρονίστηκε από το αγγλικό άρθρο

Το αγγλικό κείμενο βάσης έχει επεκταθεί με πρακτικό πλαίσιο, τεχνικές αποφάσεις και συγκεκριμένα παραδείγματα, επομένως αυτή η σελίδα λειτουργεί ως οδηγός εργασίας και όχι ως σύντομη περίληψη

Σημαντικές ενότητες του ενημερωμένου άρθρου βάσης:

  • Χρησιμοποιήστε πρώτα μικρά αναπαραγώγιμα αρχεία εισόδου
  • Διατηρήστε αμετάβλητα ονόματα προϊόντων, API, αρχείων και literal τιμές
  • Αποθηκεύστε την έξοδο του validator και τις πληροφορίες έκδοσης μαζί με το παραγόμενο δείγμα

Πρακτικές επιλογές υλοποίησης

Ξεκινήστε από τον τύπο αρχείου, το αναμενόμενο αποτέλεσμα και την κατάσταση σφάλματος που πρέπει να δει ο χρήστης. Έπειτα συνδέστε κάθε κλήση API με ένα ελέγξιμο αποτέλεσμα, ώστε η επικύρωση, τα logs και η υποστήριξη να μπορούν να αναπαράγουν το σενάριο του πελάτη

  • Χρησιμοποιήστε πρώτα μικρά αναπαραγώγιμα αρχεία εισόδου
  • Διατηρήστε αμετάβλητα ονόματα προϊόντων, API, αρχείων και literal τιμές
  • Αποθηκεύστε την έξοδο του validator και τις πληροφορίες έκδοσης μαζί με το παραγόμενο δείγμα

Κώδικας και σημεία API

Τα παραδείγματα κώδικα διατηρούνται αμετάβλητα ώστε οι προγραμματιστές να τα συγκρίνουν απευθείας με έργα Delphi, C++Builder και Lazarus/FPC

var
  Pdf: TPDFlib;
  SignId: Integer;
begin
  Pdf := TPDFlib.Create;
  try
    SignId := Pdf.NewSignProcessFromFile('invoice.pdf', '');
    if SignId = 0 then
      raise Exception.Create('cannot open source PDF');
    Pdf.SetSignProcessField(SignId, 'Sig1');
    Pdf.SetSignProcessPFXFromFile(SignId, 'company.pfx', PfxPassword);
    Pdf.SetSignProcessInfo(SignId, 'Approved', 'Vienna', 'billing@example.com');
    Pdf.SetSignProcessCustomSubFilter(SignId, 'ETSI.CAdES.detached');
    Pdf.SetSignProcessDigestAlgorithm(SignId, 2);          // SHA-256
    Pdf.SetSignProcessReserveContentsBytes(SignId, 8192);  // room for a timestamp later
    Pdf.EndSignProcessToFile(SignId, 'invoice-signed.pdf');
    if Pdf.GetSignProcessResult(SignId) <> 1 then
      raise Exception.CreateFmt('signing failed, code %d',
        [Pdf.GetSignProcessResult(SignId)]);
    Pdf.ReleaseSignProcess(SignId);
  finally
    Pdf.Free;
  end;
end;
var
  Pdf: TPDFlib;
  StsId: Integer;
  HashHex, TstDer, TsAttr, AugmentedCms: AnsiString;
begin
  Pdf := TPDFlib.Create;
  try
    StsId := Pdf.NewPAdESSignatureTimeStampProcessFromFile('invoice-signed.pdf', '');
    Pdf.SetPAdESSignatureTimeStampField(StsId, 'Sig1');
    Pdf.SetPAdESSignatureTimeStampDigestAlgorithm(StsId, 2);
    HashHex := Pdf.GetPAdESSignatureValueHashHex(StsId);
    // both calls below are application code: an HTTP POST to your TSA,
    // and a CMS re-encode that attaches the token as an unsigned attribute
    TstDer := RequestTimeStampToken(HashHex);
    TsAttr := Pdf.BuildPAdESSignatureTimeStampAttribute(TstDer);
    AugmentedCms := AttachUnsignedAttribute(Pdf.GetPAdESSignatureCMSBytes(StsId), TsAttr);
    Pdf.SetPAdESSignatureCMSBytes(StsId, AugmentedCms);
    Pdf.EndPAdESSignatureTimeStampProcessToFile(StsId, 'invoice-bt.pdf');
    if Pdf.GetPAdESSignatureTimeStampProcessResult(StsId) <> 1 then
      raise Exception.Create('timestamp embedding failed');
    Pdf.ReleasePAdESSignatureTimeStampProcess(StsId);
  finally
    Pdf.Free;
  end;
end;
var
  Doc: TPDFlibSignDoc;
  Names: TStringList;
  I: Integer;
  B0, B1, B2, B3, FileSize: Int64;
begin
  FileSize := TFile.GetSize('invoice-bt.pdf');  // before Open: SignDoc holds a share lock
  Doc := TPDFlibSignDoc.Create;
  try
    if not Doc.Open('invoice-bt.pdf', '', False) then
      raise Exception.Create('cannot open for audit');
    Names := TStringList.Create;
    try
      Doc.GetSignatureFieldNames(Names);
      for I := 0 to Names.Count - 1 do
        if Doc.GetSignatureValueObjNum(Names[I]) > 0 then   // >0 means actually signed
        begin
          B0 := StrToInt64(string(Doc.GetSignatureValueByName(Names[I], 11)));
          B1 := StrToInt64(string(Doc.GetSignatureValueByName(Names[I], 12)));
          B2 := StrToInt64(string(Doc.GetSignatureValueByName(Names[I], 13)));
          B3 := StrToInt64(string(Doc.GetSignatureValueByName(Names[I], 14)));
          if (B0 = 0) and (B2 + B3 = FileSize) then
            Writeln(Names[I], ': covers the file to EOF')
          else
            Writeln(Names[I], ': earlier revision, or unexpected ByteRange layout');
        end;
    finally
      Names.Free;
    end;
    Doc.Close;
  finally
    Doc.Free;
  end;
end;

Έλεγχος πριν από τη δημοσίευση

Ελέγξτε το αρχείο εξόδου με τα ίδια εργαλεία που θα χρησιμοποιήσει ο πελάτης ή το αρχείο. Καταγράψτε έκδοση component, δεδομένα δοκιμής, έκδοση validator και παρατηρούμενο αποτέλεσμα για ακριβή εντοπισμό μελλοντικής παλινδρόμησης

Συμπληρωματική τεχνική ανάλυση

Αυτή η εκτεταμένη ενότητα συνδέεται με το άρθρο Τεχνικό άρθρο: PAdES Digital Signatures in Delphi with PDFlibPas στα ελληνικά και αναπτύσσει την ίδια ροή εργασίας από την οπτική μιας ομάδας που χρειάζεται να μπορεί να εντοπίσει αργότερα τις αποφάσεις στον δημιουργό, στον μηχανισμό ελέγχου και στο καταγεγραμμένο αποτέλεσμα. Το αγγλικό βασικό κείμενο στη συνδεδεμένη σελίδα μέσω hreflang δείχνει γιατί δεν αρκεί μια απλή μετάφραση των επικεφαλίδων· το ουσιαστικό είναι να εξηγηθεί γιατί το έγγραφο θεωρείται ολοκληρωμένο μόνο όταν οι κανόνες, η έξοδος και τα ίχνη ελέγχου είναι πραγματικά ευθυγραμμισμένα

Στα άρθρα υλοποίησης είναι χρήσιμο να χωρίζεται ο σχεδιασμός από την επαλήθευση. Πρώτα ορίζεται ο τύπος αρχείου, το αναμενόμενο αποτέλεσμα και η κατάσταση σφάλματος που πρέπει να δει ο χρήστης, και έπειτα κάθε κλήση API συνδέεται με ένα αποτέλεσμα που μπορεί να ελεγχθεί ξανά στο ίδιο σενάριο. Αυτό ισχύει τόσο για ροές PDF όσο και για λειτουργίες υπολογιστικών φύλλων: τα παραδείγματα κώδικα παραμένουν αμετάβλητα, αλλά το συνοδευτικό κείμενο πρέπει να εξηγεί γιατί η έκδοση του component, το αναγνωριστικό του προτύπου, τα δεδομένα εισόδου και η κατάσταση επικύρωσης πρέπει να καταγράφονται μαζί

Εξίσου σημαντικό είναι να διατηρηθούν ακριβώς όπως στο αγγλικό πρωτότυπο τα ονόματα προϊόντων, τα ονόματα API, τα ονόματα αρχείων και οι literal τιμές. Έτσι μένει κοινό το πλαίσιο αναφοράς για ανάπτυξη, υποστήριξη και έλεγχο ποιότητας και μειώνεται ο κίνδυνος η τοπική έκδοση να καταλήξει σε ελεύθερη περίφραση χωρίς συγκεκριμένο τεχνικό περιεχόμενο. Αν το άρθρο περιέχει κώδικα, τα σχόλια και τα tokens του πρέπει να παραμείνουν ανέπαφα, επειδή αυτά είναι που συνδέουν το κείμενο με το πραγματικό έργο

Όταν η σελίδα διαβάζεται μετά τη δημοσίευση, είναι χρήσιμο να θεωρείται μέρος μιας αλυσίδας ιχνηλασιμότητας. Μια καλή σημείωση επικύρωσης περιγράφει τι δοκιμάστηκε, ποιο εργαλείο αξιολόγησε το αποτέλεσμα, ποιες εκδόσεις συμμετείχαν και πού αποθηκεύεται η απόδειξη επιτυχίας ή αποτυχίας. Όταν προκύψει αργότερα μια παλινδρόμηση, η αρχειοθετημένη αναφορά και το αντίστοιχο αρχείο εισόδου αξίζουν πολύ περισσότερο από την απλή εντύπωση ότι "τότε πέρασε"

Για αυτόν τον τοπικοποιημένο κλάδο ισχύει λοιπόν ένας απλός κανόνας: διατηρήστε μαζί τις βασικές αποφάσεις, τα σημεία επαλήθευσης και το πλαίσιο του κώδικα, ώστε το άρθρο να παραμένει χρήσιμο όχι μόνο στο πρώτο διάβασμα αλλά και σε μεταγενέστερο εντοπισμό σφαλμάτων, έλεγχο και σύγκριση εκδόσεων. Αυτή είναι η διαφορά ανάμεσα σε μια σύντομη περίληψη και σε ένα έγγραφο εργασίας που εξακολουθεί να έχει αξία μετά από αρκετές εκδόσεις.

  • Χρησιμοποιήστε πρώτα μικρά αναπαραγώγιμα αρχεία εισόδου
  • Διατηρήστε αμετάβλητα ονόματα προϊόντων, API, αρχείων και literal τιμών
  • Αποθηκεύστε μαζί έκδοση component, έξοδο validator και δεδομένα εισόδου
  • Κρατήστε τα blocks κώδικα και τα σχόλια ακριβώς όπως στο πρωτότυπο

Σχετική ανάγνωση