1. 程式人生 > >Java SSL Socket通訊示例

Java SSL Socket通訊示例

上一篇《OpenSSL與KeyStore指令小集》裡面說到,最近研究SSL加密,會給出一個Java的小示例。複製一份可以執行的程式碼到生產上是非常不負責任的行為,不過小示例可以帶我們入門,快速看清事物的本質。羅馬不是一天建成的。

本文將給出一個Java SSL Socket的小例子,包括了Server和Client。希望大家上手之後,要多去研究相關的資料,理解基礎概念。Java的優點是封裝得比較徹底,需要介入的地方比較少,缺點是隨著Java版本的升級和發展,會有很多新的概念和類湧出來,都要搞清楚要費不少力,另外程式碼量也比較大(生產級別的程式碼)。

具體程式碼

從最簡單來說,Java裡面只需要配置幾個系統屬性,建立及呼叫幾個SSL相關的物件即可。這四個屬性分別是:

  • javax.net.ssl.keyStore
    本方的密碼,證書等存放地點(KeyStore檔案地址)。
  • javax.net.ssl.keyStorePassword
    KeyStore的密碼。沒有密碼可以不填。
  • javax.net.ssl.trustStore
    受信任證書的存放地點(TrustKeyStore檔案地址)。
  • javax.net.ssl.trustStorePassword
    TrustKeyStore的密碼。沒有密碼可以不填。

KeyStore型別預設是JKS型別的,不是的話,還需要設定 javax.net.ssl.keyStoreType和javax.net.ssl.trustStoreType。

Server端程式碼

每一次收新的連線,都新開一個執行緒接待。生產上請用執行緒池等技術。更推薦用Netty或Mina等框架處理。

public class SslServer {
		public static void main(String[] args) throws Exception {
			System.setProperty("javax.net.debug", "ssl,handshake");

			System.setProperty("javax.net.ssl.keyStore", "./cfg/server.jks");
			System.setProperty("javax.net.ssl.keyStorePassword", "123456");
			System.setProperty("javax.net.ssl.trustStore", "./cfg/clienttrust.jks");
			System.setProperty("javax.net.ssl.trustStorePassword", "123456");

			SSLServerSocketFactory serverSocketFactory = (SSLServerSocketFactory) SSLServerSocketFactory
					.getDefault();
			SSLServerSocket serverSocket = (SSLServerSocket) serverSocketFactory
					.createServerSocket(9100);
			// 要求客戶端身份驗證
			serverSocket.setNeedClientAuth(true);

			while (true) {
				SSLSocket socket = (SSLSocket) serverSocket.accept();
				Accepter accepter = new Accepter(socket);
				accepter.service();
			}
		}

		static class Accepter implements Runnable {
			private SSLSocket socket;

			public Accepter(SSLSocket socket) {
				this.socket = socket;
			}

			public void service() {
				Thread thread = new Thread(this);
				thread.start();
			}

			@Override
			public void run() {
				try {
					InputStream inputStream = socket.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 e) {
					// replace with other code
					e.printStackTrace();
				}
			}
		}
	}

Client程式碼

建立連線,併發一個訊息給Server。很簡單。記得換行符以及呼叫flush方法。

public class SslClient {
		public static void main(String[] args) throws Exception {
			System.setProperty("javax.net.debug", "ssl,handshake");

			System.setProperty("javax.net.ssl.keyStore", "./cfg/client.jks");
			System.setProperty("javax.net.ssl.keyStorePassword", "123456");
			System.setProperty("javax.net.ssl.trustStore", "./cfg/servertrust.jks");
			System.setProperty("javax.net.ssl.trustStorePassword", "123456");

			SSLSocketFactory sslsocketfactory = (SSLSocketFactory) SSLSocketFactory
					.getDefault();
			SSLSocket sslsocket = (SSLSocket) sslsocketfactory.createSocket(
					"127.0.0.1", 9100);

			OutputStream outputStream = sslsocket.getOutputStream();
			BufferedWriter bufferedWriter = new BufferedWriter(
					new OutputStreamWriter(outputStream));
			bufferedWriter.write("沉睡的雄獅\n");
			bufferedWriter.flush();

			TimeUnit.SECONDS.sleep(2000);
		}
	}

結束語

JDK後來加了SSLEngine這個類,具有非同步通訊的能力。不過看官方文件,給出的程式碼很長。還是那句話,有條件的推薦用Netty或者Mina來處理通訊的問題,應該會比自己寫的效能好一些。