Το να αποφασίσει κανείς ότι μια υπογραφή PDF είναι πιστοποιημένη κατά eIDAS σημαίνει ότι απαντά σε μια ερώτηση που δεν έχει καμία σχέση με την κρυπτογραφία: εκδόθηκε το πιστοποιητικό από υπηρεσία εμπιστοσύνης που κάποιο κράτος μέλος κατέγραψε ως πιστοποιημένη, τη στιγμή που έγινε η υπογραφή. Η απάντηση ζει σε μια λίστα εμπιστοσύνης, ένα έγγραφο XML που δημοσιεύεται ανά περιοχή, και όλη η αξία εκείνου του εγγράφου εξαρτάται από τη γνησιότητά του. Έτσι το στοιχείο PDFium αρνείται να κοιτάξει μέσα σε μια μέχρι κάποιος να το πιστοποιήσει. Η TPdfEuropeanTrustedList.ParseAuthenticated δίνει τα πλήρη γυμνά bytes σε έναν IPdfTrustedListAuthenticator που παρέχει ο καλών πριν αναλύσει ούτε μία υπηρεσία, και δημιουργεί snapshot μόνο αν εκείνος ο authenticator περάσει ρητά
Εκείνη η σειρά είναι ο σχεδιασμός. Όλα τα άλλα σε αυτή τη λειτουργία προκύπτουν από αυτόν, συμπεριλαμβανομένων των μερών που φαίνονται δύστροπα
Αναλυμένο δεν σημαίνει έμπιστο
Μια λίστα εμπιστοσύνης που αναλύεται καθαρά σας λέει ότι το XML είναι καλά σχηματισμένο. Δεν σας λέει τίποτα για το ποιος το έγραψε. Εφόσον η λίστα είναι αυτό πάνω στο οποίο ακουμπά ολόκληρη η απόφασή σας για πιστοποιημένη κατάσταση, η αποδοχή μιας επειδή αναλύθηκε θα έκανε την απόφαση άνευ σημασίας: ένας επιτιθέμενος που μπορεί να αντικαταστήσει τη λίστα μπορεί να δηλώσει τη δική του αρχή πιστοποίησης πιστοποιημένη
Ο ίδιος συλλογισμός ισχύει για την προσωρινή αποθήκευση, και αυτή είναι η παγίδα που αξίζει να ονομαστεί. Η cache snapshots αποθηκεύει το αρχικό XML μαζί με ένα digest SHA-256, και θα ήταν εύκολο να θεωρηθεί ταιριάζον digest στη φόρτωση ως απόδειξη ότι η λίστα είναι γνήσια. Δεν είναι. Ένα digest υπολογισμένο από την ίδια διεργασία που αποθήκευσε το αρχείο, χωρίς καμία εμπλοκή κλειδιού, επαληθεύει μόνο ότι τα bytes δεν άλλαξαν από τότε που τα γράψατε· αν η λίστα ήταν δόλια όταν cache-άρθηκε, το digest επιβεβαιώνει ότι είναι η ίδια δόλια λίστα. Έτσι η φόρτωση cached snapshot περνά από τον ίδιο authenticator με την ανάλυση φρέσκιας. Η ακεραιότητα και η γνησιότητα είναι διαφορετικές ιδιότητες και μόνο μία από τις δύο χρειάζεται κλειδί
uses
FPdfTrustedList;
type
TListAuthenticator = class(TInterfacedObject, IPdfTrustedListAuthenticator)
public
function Authenticate(const XmlData: TBytes;
out AuthenticationDetails: string): Boolean;
end;
function TListAuthenticator.Authenticate(const XmlData: TBytes;
out AuthenticationDetails: string): Boolean;
begin
// Η πολιτική σας ζει εδώ: επαληθεύστε την περιβαλλόμενη υπογραφή
// XMLDSIG έναντι του πιστοποιητικού υπογραφής λίστας που
// καρφιτσώσατε εκτός ζώνης, και περιγράψτε τι ελέγξατε για το
// ίχνος ελέγχου
Result := VerifyEnvelopedXmlSignature(XmlData, FPinnedListSigner);
if Result then
AuthenticationDetails := 'XMLDSIG verified against pinned LOTL signer';
end;
var
List: TPdfEuropeanTrustedList;
Cache: TFileStream;
begin
List := TPdfEuropeanTrustedList.ParseAuthenticated(RawXml,
TListAuthenticator.Create, TPdfTrustedListOptions.Default);
// Το snapshot υπάρχει μόνο επειδή ο authenticator είπε ναι
Cache := TFileStream.Create('tl-de.snapshot', fmCreate);
try
List.SaveCache(Cache);
finally
Cache.Free;
end;
end;
Ο validator δεν κατέχει πολιτική δικτύου
Ένας validator PAdES δεν έχει δουλειά να αποφασίζει πώς να φτάσει τη λίστα λιστών εμπιστοσύνης, αν θα περάσει από proxy, πόσες φορές να επαναλάβει, ή τι θα κάνει όταν μια περιοχή είναι απρόσιτη. Εκείνες είναι αποφάσεις εφαρμογής και ανάπτυξης, και σε ρυθμιζόμενα περιβάλλοντα ελέγχονται συχνά. Έτσι οι ενημερώσεις φτάνουν μέσω IPdfTrustedListSource, που του δίνεται URI και όριο bytes και επιστρέφει bytes
Αυτό που επιβάλλει πράγματι το στοιχείο είναι οι αναλλοίωτες που κάνουν μια ενημέρωση ενημέρωση και όχι αντικατάσταση. Η Update απαιτεί η περιοχή να μην έχει αλλάξει, ο αύξων αριθμός να αυξάνεται αυστηρά, και η ώρα έκδοσης να μην οπισθοχωρεί. Εκείνοι οι τρεις έλεγχοι νικούν τις πιο προφανείς επιθέσεις υποβάθμισης: την αναπαραγωγή παλαιότερης λίστας που εξακολουθεί να καταγράφει υπηρεσία που έχει από τότε αποσυρθεί, ή την αντικατάσταση με λίστα άλλης περιοχής του οποίου τις υπηρεσίες δεν προορίζατε ποτέ να εμπιστευτείτε
Όρια parser, και καθόλου DTD
Το TPdfTrustedListOptions οριοθετεί το μέγεθος XML, τον αριθμό tokens, το βάθος φωλεύσης, τον αριθμό υπηρεσιών, τον αριθμό πιστοποιητικών και το μέγεθος μεμονωμένου πιστοποιητικού, με μια κλάση-συνάρτηση Default που παρέχει χρησιμοποιήσιμες τιμές. Οι λίστες εμπιστοσύνης είναι δημοσιευμένα έγγραφα προβλέψιμου μεγέθους, οπότε τα όρια είναι φθηνό να οριστούν και δεν υπάρχει νόμιμη λίστα που χρειάζεται να τα ξεπεράσει
Χωριστά και άνευ όρων, ο parser απορρίπτει δηλώσεις DTD και entity. Αυτό κλείνει και την άρνηση εξυπηρέτησης με διαστολή entities και τη διαδρομή αποκάλυψης εξωτερικών entities σε μία άρνηση, και δεν κοστίζει τίποτα επειδή οι λίστες εμπιστοσύνης δεν χρησιμοποιούν entities. Κάθε parser XML προσβάσιμος από μη αξιόπιστη είσοδο πρέπει να ρυθμίζεται έτσι· η διαφορά εδώ είναι ότι η άρνηση δεν είναι ρυθμίσιμη, οπότε δεν μπορεί να κλείσει από μια καλοπροαίρετη αλλαγή επιλογής
Η πιστοποιημένη κατάσταση καταγράφεται δίπλα στην εμπιστοσύνη αλυσίδας, δεν συγχωνεύεται σε αυτήν
Η πλευρά αξιολόγησης είναι σκόπιμα χωριστή. Το TPadesTrustValidationOptions.QualifiedTrustEvaluator παίρνει έναν IPdfQualifiedTrustEvaluator, που υλοποιεί το snapshot της λίστας εμπιστοσύνης. Κατά την επαλήθευση ο evaluator λαμβάνει το φύλλο πιστοποιητικό, την αλυσίδα και μια ώρα επαλήθευσης, αντιστοιχίζει πιστοποιητικά υπηρεσιών με ακριβή σύγκριση DER απέναντι στον υπογράφοντα και την αλυσίδα, συνδυάζει την κατάσταση υπηρεσίας, το αναγνωριστικό τύπου υπηρεσίας και τα URI προσδιοριστών σε εκείνη τη χρονική στιγμή, και επιστρέφει εγγραφή αξιολόγησης
Το αποτέλεσμα καταλήγει σε δύο σημεία σε κάθε υπογραφή: QualifiedTrustStatus ως αδρή κατάσταση, και QualifiedTrust ως πλήρης αξιολόγηση με περιοχή, όνομα παρόχου, όνομα υπηρεσίας, αναγνωριστικό τύπου, κατάσταση και ώρα έναρξης κατάστασης. Αυτό που δεν κάνει είναι να αλλάξει το CertificateTrustStatus. Η εμπιστοσύνη αλυσίδας συστήματος και η πιστοποιημένη κατάσταση απαντούν διαφορετικές ερωτήσεις, και μια αναφορά που τις συμπτύσσει δεν μπορεί να διακρίνει το «έμπιστο αλλά όχι πιστοποιημένο» από το «πιστοποιημένο αλλά η αλυσίδα δεν επαληθεύεται», αμφότερα πραγματικά και αμφότερα που χρειάζονται διαφορετική διαχείριση
var
Options: TPadesTrustValidationOptions;
Report: TPadesValidationResult;
I: Integer;
begin
Options := TPadesTrustValidationOptions.Default;
Options.CheckRevocation := True;
Options.QualifiedTrustEvaluator := List; // το επικυρωμένο snapshot
Options.QualifiedValidationTime := SigningTime; // όχι Now
Report := Pdf.ValidatePadesTrust(Options);
for I := 0 to High(Report.Signatures) do
if Report.Signatures[I].QualifiedTrustStatus = pcsValid then
Writeln(Format('signature %d qualified by %s / %s (%s)',
[I, Report.Signatures[I].QualifiedTrust.Territory,
Report.Signatures[I].QualifiedTrust.ProviderName,
Report.Signatures[I].QualifiedTrust.ServiceName]))
else if Report.Signatures[I].QualifiedTrustStatus = pcsIndeterminate then
// Καμία αντίστοιχη υπηρεσία, ή το snapshot δεν μπορεί να
// απαντήσει για αυτή την ώρα
Writeln(Format('signature %d: qualified status undetermined', [I]));
end;
Γιατί η ώρα επαλήθευσης δεν είναι τώρα
Επειδή η πιστοποίηση είναι ιδιότητα μιας στιγμής. Μια υπηρεσία εμπιστοσύνης μπορεί να λάβει πιστοποιημένη κατάσταση, αργότερα να της αποσυρθεί, και ακόμη αργότερα να επανέλθει, και κάθε μία από εκείνες τις μεταβάσεις κουβαλά ώρα έναρξης στη λίστα. Μια υπογραφή που έγινε όσο η υπηρεσία ήταν πιστοποιημένη παραμένει πιστοποιημένη και μετά· μια υπογραφή που έγινε πριν τη χορήγηση δεν γίνεται πιστοποιημένη αναδρομικά. Η αξιολόγηση έναντι της τρέχουσας ώρας δίνει επομένως λάθος απάντηση και προς τις δύο κατευθύνσεις
Η λίστα κουβαλά ό,τι χρειάζεται γι' αυτό: κάθε εγγραφή υπηρεσίας έχει ώρα έναρξης κατάστασης και σημαία που διακρίνει ιστορικές εγγραφές από τρέχουσες, και ο evaluator τις συνδυάζει έναντι της ώρας που εσείς δίνετε. Στην πράξη εκείνη η ώρα προέρχεται από αξιόπιστη χρονική σήμανση στην υπογραφή και όχι από την ώρα υπογραφής που διεκδικεί το CMS, που είναι ο λόγος που το υλικό μακροχρόνιας επικύρωσης έχει σημασία ακόμη και για μια ερώτηση που μοιάζει με αναζήτηση πολιτικής· η πλευρά χρονικής σήμανσης και DSS καλύπτεται στο άρθρο για τη μακροχρόνια υπογραφή
Τι εξακολουθείτε να χρειάζεται να χτίσετε
Τρία πράγματα, και κανένα δεν ανήκει σε βιβλιοθήκη PDF. Ο authenticator, που σημαίνει πραγματική επαλήθευση XMLDSIG απέναντι σε πιστοποιητικό υπογραφής λίστας που αποκτήσατε μέσω καναλιού που εμπιστεύεστε. Η πολιτική ανάκτησης, που σημαίνει πώς και πόσο συχνά ανανεώνετε, και τι κάνει η εφαρμογή σας όταν μια ανανέωση αποτύχει. Και το εδαφικό εύρος, που σημαίνει ποιες λίστες κουβαλάτε καθόλου, που είναι επιχειρησιακή απόφαση για σε ποια κράτη μέλη υπογράφουν οι αντισυμβαλλόμενοί σας
Αυτό που παίρνετε από το στοιχείο είναι το μέρος που είναι εύκολο να πάει διακριτικά στραβά: σειρά πιστοποίησης-πριν-ανάλυσης, οριοθετημένη ανάλυση XML χωρίς entities, μονότονες αναλλοίωτες ενημέρωσης, αντιστοίχιση υπηρεσιών με ακριβές DER, ιστορική αξιολόγηση κατάστασης, και αποτέλεσμα που παραμένει χωριστό από τη συνηθισμένη εμπιστοσύνη αλυσίδας. Αν το άμεσο πρόβλημά σας είναι πιο βασικό, ότι ένας validator απορρίπτει υπογραφή που πιστεύετε ότι είναι εντάξει, οι συνηθισμένες αιτίες καταγράφονται στο γιατί validators απορρίπτουν υπογραφές PAdES, και η επιφάνεια επιθεώρησης υπογραφών περιγράφεται στο επιθεώρηση υπογραφών και επιπέδων PAdES. Οι δυνατότητες του στοιχείου καταγράφονται στη σελίδα προϊόντος PDFium Delphi component