上一篇《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來處理通訊的問題,應該會比自己寫的性能好一些。