Προγραμματισμός

Στην Java εμπιστευόμαστε

Εμπιστεύεστε όλους; Μην εμπιστεύεσαι κανέναν? Ακούγεται λίγο σαν Φάκελοι Χ, αλλά όταν πρόκειται για εμπιστευτικές πληροφορίες, το να γνωρίζεις με ποιον εμπιστεύεσαι είναι εξίσου σημαντικό με το να ξέρεις τι εμπιστεύεσαι. Αυτή η ιδέα είναι τόσο σημαντική για τις εφαρμογές όσο και για τους ανθρώπους. Μετά από όλα, έχουμε κάνει τις εφαρμογές τους θεματοφύλακες των πληροφοριών μας και τους διαχειριστές των πόρων μας. Είναι αλήθεια σε ολόκληρη την επιχείρηση - οι εφαρμογές περιέχουν σημαντικές πληροφορίες για την επιχείρησή μας και τους πελάτες μας - και ισχύει στην επιφάνεια εργασίας. Δεν μπορώ να σας πω πόσες φορές μου ρωτήθηκε πώς να γράψω μια μικροεφαρμογή που σαρώνει τη μονάδα δίσκου ενός χρήστη, ώστε ένας χρήστης να μπορεί να ελέγξει το πρόγραμμα περιήγησης ενός άλλου χρήστη ή να καταγράψει ιδιωτικές πληροφορίες.

Η Java, που είναι η πλατφόρμα ανάπτυξης δικτύου, είναι υποχρεωμένη να αντιμετωπίσει το πρόβλημα της εμπιστοσύνης. Το αποτέλεσμα είναι το Java Security API και το Java Cryptography Architecture.

Μια σύντομη ματιά προς τα πίσω

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

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

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

Ένας αλγόριθμος ψηφιακής υπογραφής λαμβάνει ως εισαγωγή ενός μηνύματος και ένα ιδιωτικό κλειδί μιας οντότητας και δημιουργεί μια ψηφιακή υπογραφή. Η ψηφιακή υπογραφή δημιουργείται με τέτοιο τρόπο ώστε ο καθένας να μπορεί να πάρει το δημόσιο κλειδί της οντότητας και να το χρησιμοποιήσει για να επαληθεύσει ότι στην πραγματικότητα η οντότητα υπέγραψε το εν λόγω μήνυμα. Επιπλέον, εάν το αρχικό μήνυμα έχει παραβιαστεί, η υπογραφή δεν μπορεί πλέον να επαληθευτεί. Οι ψηφιακές υπογραφές παρέχουν ένα επιπλέον πλεονέκτημα: όταν μια οντότητα έχει υπογράψει και διανείμει ένα μήνυμα, είναι αδύνατο για τον εντολέα της να αρνηθεί την υπογραφή του μηνύματος (χωρίς να ισχυρίζεται ότι κλέφθηκε το ιδιωτικό του κλειδί, ούτως ή άλλως).

Των κινητήρων και των παρόχων

Το Java Cryptography API ορίζει την εργαλειοθήκη Java για ασφάλεια και έλεγχο ταυτότητας. Η Αρχιτεκτονική Κρυπτογραφίας Java (JCA) περιγράφει τον τρόπο χρήσης του API. Για να εξασφαλιστεί ο υψηλότερος βαθμός ευελιξίας τόσο για τον προγραμματιστή όσο και για τον τελικό χρήστη, η JCA ακολουθεί δύο κατευθυντήριες αρχές:

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

  2. Η αρχιτεκτονική πρέπει να υποστηρίζει την ανεξαρτησία και τη διαλειτουργικότητα της εφαρμογής. Ένας προγραμματιστής πρέπει να είναι σε θέση να γράφει εφαρμογές χωρίς να τις συνδέει με την εφαρμογή ενός αλγορίθμου ενός συγκεκριμένου προμηθευτή. Επιπλέον, οι υλοποιήσεις ενός αλγορίθμου που παρέχονται από διαφορετικούς προμηθευτές πρέπει να λειτουργούν μεταξύ τους.

Για να ικανοποιήσουν αυτές τις δύο απαιτήσεις, οι προγραμματιστές του Java Cryptography API βασίστηκαν το σχέδιό τους σε ένα σύστημα κινητήρων και παρόχων.

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

Ο κανονικός κινητήρας στο JCA είναι μια κλάση που παρέχει μια στατική μέθοδο (ή μεθόδους) που ονομάζεται getInstance (), η οποία επιστρέφει μια παρουσία μιας κλάσης που εφαρμόζει έναν κρυπτογραφικά σημαντικό αλγόριθμο. ο getInstance () Η μέθοδος διατίθεται τόσο σε μορφή ενός επιχειρήματος όσο και σε δύο επιχειρήματα. Και στις δύο περιπτώσεις, το πρώτο επιχείρημα είναι το όνομα του αλγορίθμου. Το JCA παρέχει μια λίστα τυπικών ονομάτων, αν και δεν θα παρέχονται όλα σε κάποια συγκεκριμένη έκδοση. Το δεύτερο επιχείρημα επιλέγει έναν πάροχο.

Ο πάροχος SUN

Μόνο ένας πάροχος - ΗΛΙΟΣ - παρέχεται στο JDK 1.1. Το SUN παρέχει τόσο την εφαρμογή του NIST Digital Signature Algorithm (DSA), όσο και την εφαρμογή των αλγορίθμων MD5 και NIST SHA-1.

Class MessageDigest

Θα ξεκινήσουμε κοιτάζοντας έναν κώδικα που δημιουργεί μια σύνοψη μηνυμάτων από ένα μήνυμα.

MessageDigest messagedigest = MessageDigest.getInstance ("SHA");

MessageDigest messagedigest = MessageDigest.getInstance ("SHA", "SUN");

Όπως ανέφερα πριν από λίγο, το getInstance () Η μέθοδος έρχεται σε δύο γεύσεις. Το πρώτο απαιτεί μόνο τον αλγόριθμο που πρέπει να καθοριστεί. Το δεύτερο απαιτεί τόσο τον αλγόριθμο όσο και τον πάροχο να καθοριστούν. Και οι δύο επιστρέφουν μια παρουσία μιας κλάσης που εφαρμόζει τον αλγόριθμο SHA.

Στη συνέχεια, διαβιβάζουμε το μήνυμα μέσω της γεννήτριας σύνθεσης μηνυμάτων.

int n = 0; byte [] rgb = νέο byte [1000]; ενώ ((n = inputstreamMessage.read (rgb))> -1) {messagedigest.update (rgb, 0, n); }

Εδώ, υποθέτουμε ότι το μήνυμα είναι διαθέσιμο ως ροή εισόδου. Αυτός ο κωδικός λειτουργεί καλά για μεγάλα μηνύματα άγνωστου μήκους. ο εκσυγχρονίζω() Η μέθοδος δέχεται επίσης ένα byte ως όρισμα για μηνύματα μήκους μερικών byte, και πίνακα byte για μηνύματα σταθερού ή προβλέψιμου μεγέθους.

rgb = messagedigest.digest ();

Το τελικό βήμα περιλαμβάνει τη δημιουργία του ίδιου του μηνύματος. Η προκύπτουσα σύνοψη κωδικοποιείται σε μια σειρά byte.

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

Φυσικά, ένας από τους κινδύνους μιας τέτοιας αφηρημένης προσέγγισης είναι η αυξημένη πιθανότητα να μην αναγνωρίσουμε εσφαλμένη έξοδο που προκύπτει από σφάλματα. Δεδομένου του ρόλου της κρυπτογραφίας, αυτό μπορεί να είναι ένα σημαντικό πρόβλημα.

Σκεφτείτε το σφάλμα "off-by-one" στη γραμμή ενημέρωσης παρακάτω:

int n = 0; byte [] rgb = νέο byte [1000]; ενώ ((n = inputstreamMessage.read (rgb))> -1) {messagedigest.update (rgb, 0, n - 1); }

Οι προγραμματιστές C, C ++ και Java χρησιμοποιούν το όριο-μείον-ένα ιδίωμα τόσο συχνά που η πληκτρολόγηση γίνεται σχεδόν αυτόματη - ακόμη και όταν δεν είναι κατάλληλη. Ο παραπάνω κώδικας θα μεταγλωττιστεί και το εκτελέσιμο θα εκτελεστεί χωρίς σφάλμα ή προειδοποίηση, αλλά η προκύπτουσα ανάληψη μηνυμάτων θα είναι λάθος.

Ευτυχώς, το JCA είναι καλά μελετημένο και καλά σχεδιασμένο, καθιστώντας πιθανές παγίδες όπως αυτές παραπάνω σχετικά σπάνιες.

Πριν προχωρήσουμε στις γεννήτριες ζεύγους κλειδιών, ρίξτε μια ματιά

MessageDigestGenerator, ο πλήρης πηγαίος κώδικας για ένα πρόγραμμα που δημιουργεί μια συλλογή μηνυμάτων.

Κατηγορία KeyPairGenerator

Για να δημιουργήσουμε μια ψηφιακή υπογραφή (και κρυπτογράφηση δεδομένων), χρειαζόμαστε κλειδιά.

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

KeyPairGenerator keypairgenerator = KeyPairGenerator.getInstance ("DSA");

Όπως και στο παραπάνω παράδειγμα σύνοψης μηνυμάτων, αυτός ο κώδικας δημιουργεί μια παρουσία μιας κλάσης που δημιουργεί κλειδιά συμβατά με DSA. Ένα δεύτερο (αν είναι απαραίτητο) όρισμα καθορίζει τον πάροχο.

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

keypairgenerator.initialize (1024, νέο SecureRandom ());

Τα κλειδιά που βασίζονται σε διαφορετικούς αλγόριθμους διαφέρουν ως προς τον τρόπο δημιουργίας τους, αλλά έχουν μια κοινή παράμετρο - το κλειδί δύναμη. Η δύναμη είναι ένας σχετικός όρος που αντιστοιχεί περίπου στο πόσο δύσκολο θα είναι το κλειδί να «σπάσει». Εάν χρησιμοποιείτε τον αρχικοποιητή ανεξάρτητο από τον αλγόριθμο, μπορείτε να καθορίσετε μόνο την ισχύ - τυχόν τιμές που εξαρτώνται από τον αλγόριθμο προϋποθέτουν λογικές προεπιλογές.

DSAKeyPairGenerator dsakeypairgenerator = (DSAKeyPairGenerator) keypairgenerator; DSAParams dsaparams = νέο DSAParams () {ιδιωτικό BigInteger p = BigInteger (...); ιδιωτικό BigInteger q = BigInteger (...); ιδιωτικό BigInteger g = BigInteger (...); δημόσιο BigInteger getP () {return p; } δημόσιο BigInteger getQ () {return q; } δημόσιο BigInteger getG () {return g; }} dsakeypairgenerator.initialize (dsaparams, νέο SecureRandom ());

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

Για να προετοιμάσουμε μια γεννήτρια ζευγών κλειδιών DSA, χρειαζόμαστε τρεις τιμές: το prime Π, το subprime Ε, και η βάση ΣΟΛ. Αυτές οι τιμές καταγράφονται σε μια παρουσία εσωτερικής κλάσης που μεταβιβάζεται στο αρχικοποιώ () μέθοδος.

ο SecureRandom Η κλάση παρέχει μια ασφαλή πηγή τυχαίων αριθμών που χρησιμοποιούνται στη δημιουργία ζεύγους κλειδιών.

επιστροφή keypairgenerator.generateKeyPair ();

Το τελευταίο βήμα περιλαμβάνει τη δημιουργία του ίδιου του ζεύγους κλειδιών.

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

Υπογραφή τάξης

Η δημιουργία και η χρήση μιας παρουσίας του Υπογραφή Η τάξη δεν διαφέρει ουσιαστικά από κανένα από τα δύο προηγούμενα παραδείγματα. Οι διαφορές βρίσκονται στον τρόπο χρήσης της παρουσίας - είτε για την υπογραφή είτε για την επαλήθευση ενός μηνύματος.

Υπογραφή υπογραφής = Signature.getInstance ("DSA");

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

signature.initSign (ιδιωτικό κλειδί);

Για να υπογράψουμε ένα μήνυμα, πρέπει πρώτα να προετοιμάσουμε την παρουσία υπογραφής με το ιδιωτικό κλειδί της οντότητας που υπογράφει το μήνυμα.

signature.initVerify (publickey);

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

int n = 0; byte [] rgb = νέο byte [1000]; ενώ ((n = inputstreamMessage.read (rgb))> -1) {signature.update (rgb, 0, n); }

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

Το τελευταίο βήμα συνίσταται στη δημιουργία της υπογραφής ή στην επαλήθευση μιας υπογραφής.

rgb = signature.sign ();

Εάν υπογράφουμε ένα μήνυμα, το σημάδι() Η μέθοδος επιστρέφει την υπογραφή.

signature.verify (rgbSignature);

Εάν επαληθεύουμε την υπογραφή που δημιουργήθηκε προηγουμένως από ένα μήνυμα, πρέπει να χρησιμοποιήσουμε το επαληθεύω() μέθοδος. Παίρνει ως παράμετρο την υπογραφή που δημιουργήθηκε προηγουμένως και καθορίζει εάν εξακολουθεί να ισχύει.

Προτού ολοκληρώσουμε τα πράγματα, ρίξτε μια ματιά στο Sign.java, τον πλήρη πηγαίο κώδικα για ένα πρόγραμμα που υπογράφει ένα μήνυμα και το Verify.java, τον πλήρη πηγαίο κώδικα για ένα πρόγραμμα που επαληθεύει ένα μήνυμα.

συμπέρασμα

Εάν οπλιστείτε με τα εργαλεία και τις τεχνικές που έχω παρουσιάσει αυτόν τον μήνα, θα είστε περισσότερο από έτοιμοι να ασφαλίσετε τις εφαρμογές σας. Το Java Cryptography API κάνει τη διαδικασία σχεδόν αβίαστη. Η έκδοση 1.2 του Java Developers Kit υπόσχεται ακόμη περισσότερα. Μείνετε συντονισμένοι.

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

Ο Todd Sundsted γράφει προγράμματα από τότε που οι υπολογιστές έγιναν διαθέσιμοι σε βολικά μοντέλα επιτραπέζιων υπολογιστών. Αν και αρχικά ενδιαφερόταν να δημιουργήσει κατανεμημένες εφαρμογές αντικειμένων στο C ++, ο Todd προχώρησε στη γλώσσα προγραμματισμού Java όταν έγινε η προφανής επιλογή για κάτι τέτοιο. Εκτός από το γράψιμο, ο Todd είναι πρόεδρος της Etcee που προσφέρει υπηρεσίες κατάρτισης, καθοδήγησης, παροχής συμβουλών και ανάπτυξης λογισμικού.

Μάθετε περισσότερα σχετικά με αυτό το θέμα

  • Κάντε λήψη του πλήρους πηγαίου κώδικα //www.javaworld.com/jw-01-1999/howto/jw-01-howto.zip
  • Επισκόπηση API ασφαλείας Java //www.javasoft.com/products/jdk/1.1/docs/guide/security/JavaSecurityOverview.html
  • Αρχιτεκτονική κρυπτογραφίας Java //www.javasoft.com/products/jdk/1.1/docs/guide/security/CryptoSpec.html
  • Σελίδα ασφαλείας της Sun's Java //java.sun.com/security/index.html
  • Συχνές ερωτήσεις της RSA για την κρυπτογραφία //www.rsa.com/rsalabs/faq/
  • Κρυπτογραφική πολιτική και πληροφορίες //www.crypto.com/
  • Διαβάστε τις προηγούμενες στήλες του Howd To Java //www.javaworld.com/topicalindex/jw-ti-howto.html

Αυτή η ιστορία, "Στην Java εμπιστευόμαστε" δημοσιεύθηκε αρχικά από το JavaWorld.