1.Keytool工具生成SSL證書
? ? keytool即JDK中自帶的證書生成工具慨代,常見的還有openssl工具。
? ? ?1.生成一個自簽名的CA證書,為了給Client和Server的證書簽名活玲。
? ? ? 命令:keytool -genkeypair (-keyalg RSA) -alias TEST_ROOT -keystore test_root.jks
? ? ? 解釋:生成一對密鑰潮饱,存儲在test_root.jks中,條目別名為TEST_ROOT括尸。輸入該命令后會提示輸入個人信息巷蚪。當(dāng)命令完成后,會在test_root.jks中生成一個自簽名的證書濒翻。當(dāng)然屁柏,私鑰也保存在該文件中。這里密鑰庫的密碼和密鑰密碼都設(shè)置成123456
如果不加keyalg 的有送,默認(rèn)是DSA算法生成
2.為server生成一對密鑰(也就是一個自簽名的證書)
?命令:keytool -genkeypair -alias TEST_SERVER -keystore test_server.jks
解釋:生成一對密鑰淌喻,存儲在test_server.jks中,條目別名TEST_SERVER雀摘。
3.生成一個簽名請求文件(即請求CA給server自簽名的證書簽名)
命令:keytool -certreq -file test_server.csr -alias TEST_SERVER -keystore test_server.jks
解釋:為存儲在test_server.jks中的別名TEST_SERVER生成一個證書請求文件test_server.csr裸删。
4.ca為server的證書簽名
命令:keytool -gencert -infile test_server.csr -outfile test_server.cer -alias TEST_ROOT -keystore TEST_ROOT.jks
解釋:利用TEST_ROOT.jks中條目別名為TEST_ROOT的私鑰為test_server.csr證書請求對應(yīng)的證書簽名,并將簽名后的證書保存在test_server.cer中阵赠。
5.導(dǎo)出ca證書
命令:keytool -exportcert -alias TEST_ROOT -file test_root.cer -keystore test_root.jks
解釋:將test_root.jks中條目別名為TEST_ROOT的證書導(dǎo)出到test_root.cer中涯塔。
6.將ca證書導(dǎo)入到test_server.jks中肌稻。
命令:keytool -importcert -alias TEST_ROOT -file test_root.cer -keystore TEST_SERVER.jks
解釋:將test_root.cer證書文件導(dǎo)入TEST_SERVER.jks中,條目別名為TEST_ROOT伤塌。這里需要注意灯萍,在TEST_SERVER.jks中之前應(yīng)不存在條目別名為TEST_ROOT,這樣才會以信任證書的方式導(dǎo)入每聪。
7.更新server證書
命令:keytool -importcert -alias TEST_SERVER -file test_server.cer -keystore TEST_SERVER.jks
解釋:將test_server.cer導(dǎo)入到TEST_SERVER.jks中旦棉。因?yàn)樵谏蓅erver密鑰對的時候,在test_server.jks中已經(jīng)存在自簽的證書药薯,條目別名為TEST_SERVER绑洛。而該命令保存的別名還是TEST_SERVER。意思上就是更新證書童本。將之前自簽名的證書替換為ca簽名過的證書真屯。
8.查看test_server.jks中server的證書和ca證書。
命令:keytool -list -v -keystore test_server.jks
從條目test_server中可以看出穷娱,該證書被ca簽名后绑蔫,ca的證書也保存在該條目中,形成證書鏈泵额。
對于Client的證書生成參考2-7步配深。在該過程中生成的文件如下:
2.在Java中實(shí)現(xiàn)SSL通信
? Java中主要通過JSSE(Java Secure Socket Extension)來完成安全套接字的編寫。其中主要涉及了一些類嫁盲,例如Keystore篓叶、KeyManagerFactory、TrustManagerFactory羞秤、SSLContext等缸托。它們的關(guān)系如下圖(圖的來源于文章結(jié)尾的鏈接中)
編程的步驟如下:
1.利用keystore類完成客戶端和服務(wù)器的證書庫和信任庫的加載
2.利用加載的證書庫和信任庫,完成KeyManagerFactory和TrustManagerFactory的初始化瘾蛋。
3.生成SSLContext對象俐镐,并用keyManagerFactory和TrustManagerFactory生成的keyManager和TrustManager完成初始化。
4.利用SSLContext對象哺哼,生成對應(yīng)的SSLSocket和SSLServerSocket京革。
需要的文件
1.test_root.jks(存儲了ca證書,因?yàn)閏lient和server的證書都是被ca簽名的幸斥,所以ca證書被信任匹摇,則client和server的證書都會被信任)。
2.test_client.jks(存儲了client的證書)甲葬。
3.test_server.jks(存儲了server的證書)廊勃。
客戶端代碼
public class Client {
? ? public static void main(String[] args) throws Exception {
? ? ? ? String clientKeyStoreFile = "d:\\keystore\\test_client.jks";
? ? ? ? String clientKeyStorePwd = "123456";
? ? ? ? String clientKeyPwd = "123456";
? ? ? ? String clientTrustKeyStoreFile = "d:\\keystore\\test_root.jks";
? ? ? ? String clientTrustKeyStorePwd = "123456";
? ? ? ? //生成client的keystore對象
? ? ? ? KeyStore clientKeyStore = KeyStore.getInstance("JKS");
? ? ? ? clientKeyStore.load(new FileInputStream(clientKeyStoreFile), clientKeyStorePwd.toCharArray());
? ? ? ? //生成信任證書的keystore對象
? ? ? ? KeyStore clientTrustKeyStore = KeyStore.getInstance("JKS");
? ? ? ? clientTrustKeyStore.load(new FileInputStream(clientTrustKeyStoreFile), clientTrustKeyStorePwd.toCharArray());
? ? ? ? KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
? ? ? ? kmf.init(clientKeyStore, clientKeyPwd.toCharArray());
? ? ? ? TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
? ? ? ? tmf.init(clientTrustKeyStore);
? ? ? ? SSLContext sslContext = SSLContext.getInstance("TLSv1");
? ? ? ? sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
? ? ? ? SSLSocketFactory socketFactory = sslContext.getSocketFactory();
? ? ? ? Socket socket = socketFactory.createSocket("localhost", Server.SERVER_PORT);
? ? ? ? PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
? ? ? ? BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
? ? ? ? send("hello", out);
? ? ? ? send("exit", out);
? ? ? ? receive(in);
? ? ? ? socket.close();
? ? }
? ? public static void send(String s, PrintWriter out) throws IOException {
? ? ? ? System.out.println("Sending: " + s);? ?
? ? ? ? out.println(s);
? ? }
? ? public static void receive(BufferedReader in) throws IOException {
? ? ? ? String s;
? ? ? ? while ((s = in.readLine()) != null) {
? ? ? ? ? ? System.out.println("Reveived: " + s);
? ? ? ? }
? ? }
}
服務(wù)器代碼
public class Server implements Runnable,HandshakeCompletedListener {
????public static final int SERVER_PORT = 11123;
? ? private final Socket s;
? ? private String peerCerName;
? ? public Server(Socket s) {
? ? ? this.s = s;
? ? }
? ? public static void main(String[] args) throws Exception {
? ? ? ? String serverKeyStoreFile = "d:\\keystore\\test_server.jks";
? ? ? ? String serverKeyStorePwd = "123456";
? ? ? ? String ServerKeyPwd = "123456";
? ? ? ? String serverTrustKeyStoreFile = "d:\\keystore\\test_root.jks";
? ? ? ? String serverTrustKeyStorePwd = "123456";
? ? ? ? /*
? ? ? ? * 加載server.keystore
? ? ? ? *
? ? ? ? */
? ? ? ? KeyStore serverKeyStore = KeyStore.getInstance("JKS");
? ? ? ? serverKeyStore.load(new FileInputStream(serverKeyStoreFile), serverKeyStorePwd.toCharArray());
? ? ? ? /*
? ? ? ? * 加載servertrust.keystore
? ? ? ? *
? ? ? ? */
? ? ? ? KeyStore serverTrustKeyStore = KeyStore.getInstance("JKS");
? ? ? ? serverTrustKeyStore.load(new FileInputStream(serverTrustKeyStoreFile), serverTrustKeyStorePwd.toCharArray());
? ? ? ? KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
? ? ? ? kmf.init(serverKeyStore, ServerKeyPwd.toCharArray());
? ? ? ? TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
? ? ? ? tmf.init(serverTrustKeyStore);
? ? ? ? SSLContext sslContext = SSLContext.getInstance("TLSv1");
? ? ? ? sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
? ? ? ? SSLServerSocketFactory sslServerSocketFactory = sslContext.getServerSocketFactory();
? ? ? ? SSLServerSocket sslServerSocket = (SSLServerSocket) sslServerSocketFactory.createServerSocket(SERVER_PORT);
? ? ? ? //設(shè)置雙向驗(yàn)證
? ? ? ? sslServerSocket.setNeedClientAuth(true);
? ? ? ? while (true) {
? ? ? ? ? ? SSLSocket s = (SSLSocket)sslServerSocket.accept();
? ? ? ? ? ? Server cs = new Server(s);
? ? ? ? ? ? s.addHandshakeCompletedListener(cs);
? ? ? ? ? ? new Thread(cs).start();
? ? ? ? }
? ? }
? ? @Override
? ? public void run() {
? ? ? ? try {
? ? ? ? ? ? BufferedReader reader = new BufferedReader(new InputStreamReader(s.getInputStream()));
? ? ? ? ? ? PrintWriter writer = new PrintWriter(s.getOutputStream(), true);
? ? ? ? ? ? writer.println("Welcome~, enter exit to leave.");
? ? ? ? ? ? String message;
? ? ? ? ? ? while ((message = reader.readLine()) != null && !message.trim().equalsIgnoreCase("exit")) {
? ? ? ? ? ? ? ? writer.println("Echo: " + message);
? ? ? ? ? ? }
? ? ? ? ? ? writer.println("Bye~, " + peerCerName);
? ? ? ? } catch (Exception e) {
? ? ? ? ? ? e.printStackTrace();
? ? ? ? } finally {
? ? ? ? ? ? try {
? ? ? ? ? ? ? ? s.close();
? ? ? ? ? ? } catch (IOException e) {
? ? ? ? ? ? ? ? e.printStackTrace();
? ? ? ? ? ? }
? ? ? ? }
? ? }
? ? @Override
? ? public void handshakeCompleted(HandshakeCompletedEvent event) {
? ? ? ? try {
? ? ? ? ? ? X509Certificate cert = (X509Certificate) event.getPeerCertificates()[0];
? ? ? ? ? ? peerCerName = cert.getSubjectX500Principal().getName();
? ? ? ? } catch (SSLPeerUnverifiedException ex) {
? ? ? ? ? ? ex.printStackTrace();
? ? ? ? }
? ? }
}
運(yùn)行結(jié)果:
說明SSL握手成功建立。
參考的文章:https://www.ibm.com/developerworks/cn/java/j-lo-socketkeytool/