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

Δημιουργήστε εφαρμογές ασφαλούς δικτύου με SSL και το JSSE API

Το Διαδίκτυο είναι ένα επικίνδυνο μέρος. Είναι απλώς πολύ εύκολο να κατασκοπεύσετε, να πλαστογραφήσετε και να κλέψετε μη προστατευμένες πληροφορίες καθώς ταξιδεύει πάνω από τα καλώδια. Τον περασμένο μήνα, έγραψα το τελικό άρθρο σε μια σειρά για πιστοποιητικά X.509 και υποδομή δημόσιου κλειδιού (PKI), τις τεχνολογίες που διασφαλίζουν τη μεγαλύτερη δραστηριότητα ηλεκτρονικού εμπορίου στο Διαδίκτυο. Κοντά στο τέλος του άρθρου, πρότεινα να κοιτάξετε το πρωτόκολλο SSL (Secure Socket Layer) για να μάθετε πώς χρησιμοποιούνται τα πιστοποιητικά X.509 στην πράξη. Το SSL είναι η εφαρμογή killer X.509 - υποστηρίζει σχεδόν κάθε πρόγραμμα περιήγησης και οι πιο δημοφιλείς διακομιστές Ιστού και εφαρμογών.

Αυτό το μήνα, θα εξερευνήσω το SSL όπως υλοποιήθηκε από το JSSE (Java Secure Socket Extension) και θα σας δείξω πώς να δημιουργήσετε εφαρμογές ασφαλούς δικτύου στην Java χρησιμοποιώντας SSL και JSSE.

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

Για να εκτελέσετε την επίδειξη, πρέπει πρώτα να συντάξετε την ακόλουθη τάξη:

 δημόσια τάξη Δοκιμή {public static void main (String [] arstring) {try {new java.net.URL ("//" + arstring [0] + "/"). getContent (); } catch (Εξαίρεση εξαίρεσης) {exception.printStackTrace (); }}} 

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

 java -Djava.protocol.handler.pkgs = com.sun.net.ssl.internal.www.protocol -Djavax.net.debug = ssl Δοκιμή 

Πρέπει να εγκαταστήσετε το JSSE. ανατρέξτε στους πόρους εάν δεν είστε σίγουροι πώς.

Τώρα ας αρχίσουμε τις επιχειρήσεις και να μιλήσουμε για SSL και JSSE.

Μια σύντομη ματιά στο SSL

Ο κωδικός στην εισαγωγή δείχνει τον ευκολότερο τρόπο προσθήκης SSL στις εφαρμογές σας - μέσω του java.net.URL τάξη. Αυτή η προσέγγιση είναι χρήσιμη, αλλά δεν είναι αρκετά ευέλικτη για να σας επιτρέψει να δημιουργήσετε μια ασφαλή εφαρμογή που χρησιμοποιεί γενικές πρίζες.

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

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

Το SSL αντιμετωπίζει τρία σημαντικά θέματα ασφαλείας:

  1. Παρέχει έλεγχο ταυτότητας, η οποία βοηθά στη διασφάλιση της νομιμότητας των οντοτήτων που συμμετέχουν σε ένα διάλογο.
  2. Παρέχει ιδιωτικότητα. Το SSL βοηθά στην εγγύηση ότι ένα τρίτο μέρος δεν μπορεί να αποκρυπτογραφήσει το διάλογο μεταξύ δύο οντοτήτων.
  3. Διατηρεί την ακεραιότητα. Η χρήση ενός MAC (κωδικός ελέγχου ταυτότητας μηνύματος), ο οποίος είναι παρόμοιος με ένα άθροισμα ελέγχου, διασφαλίζει ότι ο διάλογος μεταξύ δύο οντοτήτων δεν τροποποιείται από τρίτο μέρος.

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

Η υλοποίηση αναφοράς JSSE της Sun συνοδεύεται από όλη την απαραίτητη τεχνολογία για την προσθήκη SSL στις εφαρμογές σας. Περιλαμβάνει υποστήριξη κρυπτογράφησης RSA (Rivest-Shamir-Adleman) - το de facto πρότυπο για την ασφάλεια στο Διαδίκτυο. Περιλαμβάνει μια εφαρμογή SSL 3.0 - το τρέχον πρότυπο SSL - και το TLS (Transport Layer Security) 1.0, την επόμενη γενιά SSL. Το JSSE παρέχει επίσης μια σειρά από API για τη δημιουργία και τη χρήση ασφαλών πριζών.

Το API JSSE

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

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

SSLSocketFactory

Μέθοδοι στο javax.net.ssl.SSLSocketFactory τάξη εμπίπτουν σε τρεις κατηγορίες. Η πρώτη αποτελείται από μια μεμονωμένη στατική μέθοδο που ανακτά την προεπιλεγμένη εργοστασιακή υποδοχή SSL: στατικό SocketFactory getDefault ().

Η δεύτερη κατηγορία αποτελείται από τέσσερις μεθόδους που προέρχονται από javax.net.SocketFactory που αντικατοπτρίζουν τους τέσσερις βασικούς κατασκευαστές που βρέθηκαν στο java.net.Socket κλάσης και μια μέθοδο που τυλίγει μια υπάρχουσα υποδοχή με μια υποδοχή SSL. Ο καθένας επιστρέφει μια υποδοχή SSL:

  1. Socket createSocket (String host, int port)
  2. Socket createSocket (String host, int port, InetAddress clientHost, int clientPort)
  3. Socket createSocket (InetAddress host, int port)
  4. Socket createSocket (InetAddress host, int port, InetAddress clientHost, int clientPort)
  5. Socket createSocket (Socket socket, String host, int port, boolean autoClose)

Οι δύο μέθοδοι στην τρίτη κατηγορία επιστρέφουν τη λίστα των SSL cipher suites που είναι ενεργοποιημένες από προεπιλογή και την πλήρη λίστα των υποστηριζόμενων SSL cipher suites:

  1. Συμβολοσειρά [] getDefaultCipherSuites ()
  2. Συμβολοσειρά [] getSupportedCipherSuites ()

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

SSLServerSocketFactory

Μέθοδοι στο javax.net.ssl.SSLServerSocketFactory τάξη εμπίπτουν στις ίδιες τρεις κατηγορίες με SSLSocketFactory. Πρώτον, υπάρχει η μεμονωμένη στατική μέθοδος που ανακτά το εργοστάσιο προεπιλεγμένης υποδοχής διακομιστή SSL: static ServerSocketFactory getDefault ().

Οι μέθοδοι που επιστρέφουν υποδοχές διακομιστή SSL αντικατοπτρίζουν τους κατασκευαστές που βρέθηκαν στο java.net.ServerSocket τάξη:

  1. ServerSocket createServerSocket (int θύρα)
  2. ServerSocket createServerSocket (int port, int backlog)
  3. ServerSocket createServerSocket (int port, int backlog, InetAddress διεύθυνση)

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

  1. Συμβολοσειρά [] getDefaultCipherSuites ()
  2. Συμβολοσειρά [] getSupportedCipherSuites ()

Μέχρι στιγμής, το API είναι αρκετά απλό.

Υποδοχή SSL

Τα πράγματα γίνονται ενδιαφέροντα στο javax.net.ssl.SSLSocket τάξη. Υποθέτω ότι είστε ήδη εξοικειωμένοι με τις μεθόδους που παρέχει ο γονέας της, το Πρίζα τάξη, οπότε θα επικεντρωθώ στις μεθόδους που παρέχουν λειτουργικότητα που σχετίζεται με το SSL.

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

  1. Συμβολοσειρά [] getEnabledCipherSuites ()
  2. Συμβολοσειρά [] getSupportedCipherSuites ()
  3. void setEnabledCipherSuites (String [] σουίτες)

Αυτές οι δύο μέθοδοι καθορίζουν εάν η υποδοχή μπορεί να δημιουργήσει νέες συνεδρίες SSL, οι οποίες διατηρούν λεπτομέρειες σύνδεσης - όπως το κοινό μυστικό κλειδί - μεταξύ συνδέσεων:

  1. boolean getEnableSessionCreation ()
  2. void setEnableSessionCreation (boolean σημαία)

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

  1. boolean getNeedClientAuth ()
  2. void setNeedClientAuth (boolean ανάγκη)

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

  1. boolean getUseClientMode ()
  2. void setUseClientMode (λειτουργία boolean)

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

Μέθοδος SSLSession getSession () ανακτά την περίοδο λειτουργίας SSL. Σπάνια θα πρέπει να έχετε απευθείας πρόσβαση στη συνεδρία SSL.

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

  1. void addHandshakeCompletedListener (ΧειραψίαCompletedListener listener)
  2. ακύρωση αφαίρεσηςHandshakeCompletedListener (HandshakeCompletedListener listener)

SSLServerSocket

ο javax.net.ssl.SSLServerSocket τάξη είναι παρόμοια με το javax.net.ssl.SSLSocket τάξη; δεν απαιτεί ιδιαίτερη προσοχή. Στην πραγματικότητα, το σύνολο των μεθόδων javax.net.ssl.SSLServerSocket κλάση είναι ένα υποσύνολο των μεθόδων στο javax.net.ssl.SSLSocket τάξη.

Οι δύο πρώτες μέθοδοι που αναφέρονται παρακάτω ανακτούν τις ενεργοποιημένες και υποστηριζόμενες σουίτες κρυπτογράφησης SSL. Η τρίτη μέθοδος ορίζει την ενεργοποιημένη κρυπτογράφηση σουίτα:

  1. Συμβολοσειρά [] getEnabledCipherSuites ()
  2. Συμβολοσειρά [] getSupportedCipherSuites ()
  3. void setEnabledCipherSuites (String [] σουίτες)

Αυτές οι δύο μέθοδοι ελέγχουν εάν η υποδοχή διακομιστή μπορεί να δημιουργήσει νέες συνεδρίες SSL:

  1. boolean getEnableSessionCreation ()
  2. void setEnableSessionCreation (boolean σημαία)

Οι ακόλουθες μέθοδοι καθορίζουν εάν οι αποδεκτές υποδοχές θα απαιτούν έλεγχο ταυτότητας πελάτη:

  1. boolean getNeedClientAuth ()
  2. void setNeedClientAuth (boolean σημαία)

Οι παρακάτω μέθοδοι αλλάζουν την αποδεκτή υποδοχή από τη λειτουργία πελάτη σε λειτουργία διακομιστή:

  1. boolean getUseClientMode ()
  2. void setUseClientMode (boolean σημαία)

Ένα απλό παράδειγμα

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

Ο διακομιστής, που φαίνεται παρακάτω, χρησιμοποιεί το JSSE για να δημιουργήσει μια ασφαλή υποδοχή διακομιστή. Ακούει στην υποδοχή διακομιστή για συνδέσεις από ασφαλείς πελάτες. Κατά την εκτέλεση του διακομιστή, πρέπει να καθορίσετε το keystore που θα χρησιμοποιηθεί. Το keystore περιέχει το πιστοποιητικό του διακομιστή. Έχω δημιουργήσει ένα απλό keystore που περιέχει ένα μόνο πιστοποιητικό. (Ανατρέξτε στην ενότητα Πόροι για λήψη του πιστοποιητικού.)

εισαγωγή java.io.InputStream; εισαγωγή java.io.InputStreamReader; εισαγωγή java.io.BufferedReader; εισαγωγή java.io.IOException; εισαγωγή javax.net.ssl.SSLSocket; εισαγωγή javax.net.ssl.SSLServerSocket; εισαγωγή javax.net.ssl.SSLServerSocketFactory; δημόσια τάξη EchoServer {public static void main (String [] arstring) {try {SSLServerSocketFactory sslserversocketfactory = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault (); SSLServerSocket sslserversocket = (SSLServerSocket) sslserversocketfactory.createServerSocket (9999); SSLSocket sslsocket = (SSLSocket) sslserversocket.accept (); InputStream inputstream = sslsocket.getInputStream (); InputStreamReader inputstreamreader = νέο InputStreamReader (inputstream); BufferedReader bufferedreader = νέο BufferedReader (inputstreamreader); String string = null; ενώ ((string = bufferedreader.readLine ())! = null) {System.out.println (string); System.out.flush (); }} catch (Εξαίρεση εξαίρεσης) {exception.printStackTrace (); }}} 

Χρησιμοποιήστε την ακόλουθη εντολή για να ξεκινήσετε τον διακομιστή (foobar είναι το όνομα του αρχείου keystore και ο κωδικός πρόσβασης):

 java -Djavax.net.ssl.keyStore = foobar -Djavax.net.ssl.keyStorePassword = foobar EchoServer 

Ο πελάτης, που φαίνεται παρακάτω, χρησιμοποιεί το JSSE για ασφαλή σύνδεση με τον διακομιστή. Κατά την εκτέλεση του προγράμματος-πελάτη, πρέπει να καθορίσετε το κατάστημα αξιοπιστίας που θα χρησιμοποιήσετε, το οποίο περιέχει τη λίστα των αξιόπιστων πιστοποιητικών. Έχω δημιουργήσει ένα απλό κατάστημα αξιοπιστίας που περιέχει ένα μόνο πιστοποιητικό. (Ανατρέξτε στην ενότητα Πόροι για λήψη του πιστοποιητικού.)

εισαγωγή java.io.InputStream; εισαγωγή java.io.OutputStream; εισαγωγή java.io.InputStreamReader; εισαγωγή java.io.OutputStreamWriter; εισαγωγή java.io.BufferedReader; εισαγωγή java.io.BufferedWriter; εισαγωγή java.io.IOException; εισαγωγή javax.net.ssl.SSLSocket; εισαγωγή javax.net.ssl.SSLSocketFactory; δημόσια τάξη EchoClient {public static void main (String [] arstring) {try {SSLSocketFactory sslsocketfactory = (SSLSocketFactory) SSLSocketFactory.getDefault (); SSLSocket sslsocket = (SSLSocket) sslsocketfactory.createSocket ("localhost", 9999); InputStream inputstream = System.in; InputStreamReader inputstreamreader = νέο InputStreamReader (inputstream); BufferedReader bufferedreader = νέο BufferedReader (inputstreamreader); OutputStream outputstream = sslsocket.getOutputStream (); OutputStreamWriter outputstreamwriter = νέο OutputStreamWriter (έξοδος ροής); BufferedWriter bufferedwriter = νέο BufferedWriter (outputstreamwriter); Συμβολοσειρά συμβολοσειράς = null; while ((string = bufferedreader.readLine ())! = null) {bufferedwriter.write (συμβολοσειρά + '\ n'); bufferedwriter.flush (); }} catch (Εξαίρεση εξαίρεσης) {exception.printStackTrace (); }}} 

Χρησιμοποιήστε την ακόλουθη εντολή για να ξεκινήσετε τον υπολογιστή-πελάτη (foobar είναι το όνομα του αρχείου truststore και ο κωδικός πρόσβασης):

 java -Djavax.net.ssl.trustStore = foobar -Djavax.net.ssl.trustStorePassword = foobar EchoClient