בנה יישומי רשת מאובטחים באמצעות SSL ו- JSSE API

האינטרנט הוא מקום מסוכן. פשוט קל מדי לחטט, לזייף ולגנוב מידע לא מוגן בזמן שהוא עובר מעל החוטים. בחודש שעבר כתבתי את המאמר האחרון בסדרה על אישורי X.509 ותשתית מפתח ציבורי (PKI), הטכנולוגיות המאבטחות את מרבית פעילות המסחר האלקטרוני באינטרנט. לקראת סוף המאמר הצעתי לעיין בפרוטוקול SSL (Secure Socket Layer) כדי ללמוד כיצד משתמשים בתעודות X.509 בפועל. SSL הוא אפליקציית הרוצח X.509 - כמעט כל דפדפן ושרתי האינטרנט והיישומים הפופולריים ביותר תומכים בו.

החודש אבדוק את SSL כפי שהוא מיושם על ידי JSSE (Java Secure Socket Extension), ואראה לכם כיצד לבנות יישומי רשת מאובטחים ב- Java באמצעות SSL ו- JSSE.

נתחיל בהדגמה פשוטה. JSSE מספק ערכת כלים SSL ליישומי Java. בנוסף למחלקות ולממשקים הדרושים, JSSE מספק מתג ניפוי שגיאות בשורת פקודה, שבו תוכלו להשתמש כדי לצפות בפרוטוקול SSL בפעולה. בנוסף לספק מידע שימושי לניפוי באגים ביישום סורר, משחק עם ערכת הכלים הוא דרך נהדרת להרטיב את הרגליים באמצעות SSL ו- JSSE.

כדי להריץ את ההפגנה, תחילה עליך לאסוף את הכיתה הבאה:

מבחן מחלקה ציבורית {ציבורי ריק סטטי ראשי (מחרוזת [] ארסטרינג) {נסה {new java.net.URL ("//" + arstring [0] + "/"). getContent (); } לתפוס (חריג חריג) {exception.printStackTrace (); }}}

לאחר מכן, עליך להפעיל איתור באגים ב- SSL ולהפעיל את היישום הנ"ל. היישום מתחבר לאתר המאובטח שציינת בשורת הפקודה באמצעות פרוטוקול SSL באמצעות HTTPS. האפשרות הראשונה טוענת את המטפל בפרוטוקול HTTPS. האפשרות השנייה, אפשרות הבאגים, גורמת לתוכנית להדפיס את התנהגותה. הנה הפקודה (החלף בשם שרת אינטרנט מאובטח):

 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 שואפת לספק ליישומים ערכת כלים מאובטחת כמו שקעים. באופן אידיאלי, אמור להיות קל להמיר יישום המשתמש בשקעים רגילים ליישום המשתמש ב- SSL.

SSL מטפל בשלוש בעיות אבטחה חשובות:

  1. הוא מספק אימות, המסייע להבטיח את הלגיטימיות של הגופים המעורבים בדיאלוג.
  2. זה מספק פרטיות. SSL מסייע להבטיח כי צד שלישי אינו יכול לפענח את הדיאלוג בין שתי ישויות.
  3. זה שומר על שלמות. השימוש ב- MAC (קוד אימות הודעה), הדומה לבדיקת בדיקה, מסייע להבטיח שדיאלוג בין שתי ישויות אינו משתנה על ידי צד שלישי.

SSL מסתמך בכבדות על הצפנת מפתח ציבורי וגם מפתח סודי. היא משתמשת בקריפטוגרפיה של מפתח סודי כדי להצפין בכמות גדולה את הנתונים שהוחלפו בין שתי יישומים. SSL מספק את הפיתרון האידיאלי מכיוון שאלגוריתמים של מפתח סודי הם מאובטחים ומהירים כאחד. הצפנת מפתח ציבורי, שהיא איטית יותר מהצפנה של מפתח סודי, היא בחירה טובה יותר לאימות והחלפת מפתחות.

יישום הייחוס של JSSE של Sun מגיע עם כל הטכנולוגיה הדרושה להוספת SSL ליישומים שלך. הוא כולל תמיכה בקריפטוגרפיה של RSA (Rivest-Shamir-Adleman) - הסטנדרט בפועל לאבטחה באינטרנט. הוא כולל יישום של SSL 3.0 - תקן SSL הנוכחי - ו- TLS (Transport Layer Security) 1.0, הדור הבא של SSL. JSSE מספקת גם חבילת ממשקי API ליצירה ושימוש בשקעים מאובטחים.

ממשק ה- API של JSSE

ארכיטקטורת האבטחה של Java משתמשת בתבנית העיצוב של Factory בכבדות. עבור חסרי היזמים, דפוס העיצוב של המפעל משתמש בחפצי מפעל מיוחדים כדי לבנות מקרים, במקום להתקשר ישירות לבונים שלהם. (ראה משאבים ליתרונות והחסרונות של מחלקת המפעלים.)

ב- JSSE הכל מתחיל במפעל; יש מפעל לשקעי SSL ומפעל לשקעי שרת SSL. מכיוון ששקעים גנריים ושקעי שרת הם כבר די בסיסיים לתכנות רשת Java, אני מניח שאתה מכיר את השניים ואתה מבין את התפקידים וההבדלים ביניהם. אם לא, אני ממליץ להרים ספר טוב על תכנות רשת Java.

SSLSocketFactory

השיטות javax.net.ssl.SSLSocketFactoryבכיתה מתחלקות לשלוש קטגוריות. המורכב הראשון של שיטה סטטית אחת שמאחזרת במפעל שקע SSL המחדל: static 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 המופעלות כברירת מחדל, ואת הרשימה המלאה של חבילות הצפנה SSL הנתמכות:

  1. String [] getDefaultCipherSuites()
  2. String [] getSupportedCipherSuites()

חבילת צופן היא שילוב של אלגוריתמים קריפטוגרפיים המגדירים רמת אבטחה מסוימת עבור חיבור SSL. חבילת צופן מגדירה האם החיבור מוצפן, האם אומת תקינות התוכן וכיצד מתרחש אימות.

SSLServerSocketFactory

השיטות javax.net.ssl.SSLServerSocketFactoryבשיעור נכללות באותן שלוש קטגוריות כמו SSLSocketFactory. ראשית, יש את שיטת סטטי היחידה שמאחזרת במפעל שקע שרת SSL המחדל: static ServerSocketFactory getDefault().

השיטות להחזרת שקעי שרת SSL משקפות את הבונים שנמצאו java.net.ServerSocketבכיתה:

  1. ServerSocket createServerSocket(int port)
  2. ServerSocket createServerSocket(int port, int backlog)
  3. ServerSocket createServerSocket(int port, int backlog, InetAddress address)

לבסוף, SSLServerSocketFactoryהתכונות כוללות את שתי השיטות שמחזירות את רשימת הצפנים המופעלת כברירת מחדל ורשימת הצפנים הנתמכים, בהתאמה:

  1. String [] getDefaultCipherSuites()
  2. String [] getSupportedCipherSuites()

עד כה, ה- API די פשוט.

SSLSocket

הדברים נעשים מעניינים javax.net.ssl.SSLSocketבכיתה. אני מניח שאתה כבר מכיר את השיטות המסופקות על ידי ההורה שלו, Socketהכיתה, ולכן אתרכז בשיטות המספקות פונקציונליות הקשורה ל- SSL.

בדומה לשתי מחלקות המפעל SSL, שתי השיטות הראשונות המפורטות להלן מאחזרות את חבילות הצפנה המופעלות והתומכות ב- SSL, בהתאמה. השיטה השלישית קובעת את סוויטות הצופן המופעלות. יישום יכול להשתמש בפעולה השלישית כדי לשדרג או לשדרג לאחור את טווח האבטחה המקובלת שהיישום יאפשר:

  1. String [] getEnabledCipherSuites()
  2. String [] getSupportedCipherSuites()
  3. void setEnabledCipherSuites(String [] suites)

These two methods determine whether the socket can establish new SSL sessions, which maintain connection details -- like the shared secret key -- between connections:

  1. boolean getEnableSessionCreation()
  2. void setEnableSessionCreation(boolean flag)

The next two methods determine whether the socket will require client authentication. The methods only make sense when invoked on server mode sockets. Remember, according to the SSL specification, client authentication is optional. For example, most Web applications don't require it:

  1. boolean getNeedClientAuth()
  2. void setNeedClientAuth(boolean need)

The methods below change the socket from client mode to server mode. This affects who initiates the SSL handshake and who authenticates first:

  1. boolean getUseClientMode()
  2. void setUseClientMode(boolean mode)

Method void startHandshake() forces an SSL handshake. It's possible, but not common, to force a new handshake operation in an existing connection.

Method SSLSession getSession() retrieves the SSL session. You will seldom need to access the SSL session directly.

The two methods listed below add and remove an SSL handshake listener object. The handshake listener object is notified whenever an SSL handshake operation completes on the socket.

  1. void addHandshakeCompletedListener(HandshakeCompletedListener listener)
  2. void removeHandshakeCompletedListener(HandshakeCompletedListener listener)

SSLServerSocket

The javax.net.ssl.SSLServerSocket class is similar to the javax.net.ssl.SSLSocket class; it doesn't require much individual attention. In fact, the set of methods on javax.net.ssl.SSLServerSocket class is a subset of the methods on the javax.net.ssl.SSLSocket class.

The first two methods listed below retrieve the enabled and supported SSL cipher suites. The third method sets the enabled cipher suite:

  1. String [] getEnabledCipherSuites()
  2. String [] getSupportedCipherSuites()
  3. void setEnabledCipherSuites(String [] suites)

These two methods control whether or not the server socket can establish new SSL sessions:

  1. boolean getEnableSessionCreation()
  2. void setEnableSessionCreation(boolean flag)

The following methods determine whether the accepted sockets will require client authentication:

  1. boolean getNeedClientAuth()
  2. void setNeedClientAuth(boolean flag)

The methods below change the accepted socket from client mode to server mode:

  1. boolean getUseClientMode()
  2. void setUseClientMode(boolean flag)

A simple example

To make this toolkit tutorial clearer, I've included the source code for a simple server and a compatible client below. It's a secure variation on the typical echo application that many introductory networking texts provide.

The server, shown below, uses JSSE to create a secure server socket. It listens on the server socket for connections from secure clients. When running the server, you must specify the keystore to use. The keystore contains the server's certificate. I have created a simple keystore that contains a single certificate. (See Resources to download the certificate.)

import java.io.InputStream; import java.io.InputStreamReader; import java.io.BufferedReader; import java.io.IOException; import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLServerSocket; import javax.net.ssl.SSLServerSocketFactory; public class 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 = new InputStreamReader(inputstream); BufferedReader bufferedreader = new BufferedReader(inputstreamreader); String string = null; while ((string = bufferedreader.readLine()) != null) { System.out.println(string); System.out.flush(); } } catch (Exception exception) { exception.printStackTrace(); } } } 

Use the following command to start the server (foobar is both the name of the keystore file and its password):

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

The client, shown below, uses JSSE to securely connect to the server. When running the client, you must specify the truststore to use, which contains the list of trusted certificates. I have created a simple truststore that contains a single certificate. (See Resources to download the certificate.)

import java.io.InputStream; import java.io.OutputStream; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocketFactory; public class 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 = new InputStreamReader(inputstream); BufferedReader bufferedreader = new BufferedReader(inputstreamreader); OutputStream outputstream = sslsocket.getOutputStream(); OutputStreamWriter outputstreamwriter = new OutputStreamWriter(outputstream); BufferedWriter bufferedwriter = new BufferedWriter(outputstreamwriter); String string = null; while ((string = bufferedreader.readLine()) != null) { bufferedwriter.write(string + '\n'); bufferedwriter.flush(); } } catch (Exception exception) { exception.printStackTrace(); } } } 

השתמש בפקודה הבאה כדי להפעיל את הלקוח ( foobarהוא גם שם קובץ truststore וגם הסיסמה שלו):

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