Java實(shí)現(xiàn)SSL Socket長連接方式
一、單向認(rèn)證
1、生成服務(wù)端密鑰(配置了jdk的環(huán)境變量即可用keytool命令)
命令:keytool -genkey -keystore server_ks.jks -storepass server_password -keyalg RSA -keypass server_password
結(jié)果:會生成server_ks.jks密鑰文件
操作:將生成的server_ks.jks密鑰文件配置到服務(wù)端
2、生成服務(wù)端證書
命令:keytool -export -keystore server_ks.jks -storepass server_password -file server.cer
結(jié)果:會生成server.cer證書文件
操作:將生成的server.cer證書文件配置到客戶端
3、服務(wù)端代碼
package com.ssl.server; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLServerSocket; import java.io.*; import java.net.Socket; import java.nio.charset.StandardCharsets; import java.security.KeyStore; public class ServerTest { public static File writeToFile1 = new File("C:\\Users\\Li\\Desktop\\temp\\配置.txt"); public static File writeToFile2 = new File("C:\\Users\\Li\\Desktop\\temp\\配置2.txt"); public static void main(String[] args) { new Thread(new ServerOne()).start(); } public static class ServerOne implements Runnable { //SSL協(xié)議版本 private static final String TLS = "TLSv1.2"; //KeyStore的類型 private static final String PROVIDER = "SunX509"; //秘鑰類型,java默認(rèn)是JKS private static final String STORE_TYPE = "JKS"; //秘鑰的路徑 private static final String KEY_STORE_NAME = "E:\\02-IDEA_Project\\Test02_HttpsServer\\server_ks.jks"; //Server的端口 private static final int DEFAULT_PORT = 8090; //自定義端口 //秘鑰的密碼 private static final String SERVER_KEY_STORE_PASSWORD = "server_password"; @Override public void run() { SSLServerSocket sslServerSocket = null; try { //獲取SSLContext SSLContext sslContext = SSLContext.getInstance(TLS); //生成秘鑰的manager KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(PROVIDER); //加載秘鑰 KeyStore keyStoreOne = KeyStore.getInstance(STORE_TYPE); keyStoreOne.load(new FileInputStream(KEY_STORE_NAME), SERVER_KEY_STORE_PASSWORD.toCharArray()); //秘鑰初始化 keyManagerFactory.init(keyStoreOne, SERVER_KEY_STORE_PASSWORD.toCharArray()); //初始化SSLContext sslContext.init(keyManagerFactory.getKeyManagers(), null, null); //獲取SSLContext的SocketFactory sslServerSocket = (SSLServerSocket) sslContext.getServerSocketFactory().createServerSocket(DEFAULT_PORT); //是否開啟雙向驗(yàn)證 sslServerSocket.setNeedClientAuth(false); System.out.println("服務(wù)器已開啟,等待連接 ....."); while (true) { Socket accept = sslServerSocket.accept(); accept.setKeepAlive(true); System.out.println("客戶端 : " + accept.getInetAddress().getHostAddress()); try ( InputStream inputStream = accept.getInputStream(); OutputStream outputStream = accept.getOutputStream(); ) { byteMsgReader(inputStream, outputStream); // socket.shutdownOutput(); // 長連接則不關(guān)閉輸出流 // socket.shutdownInput(); // 長連接則不關(guān)閉輸入流 } catch (Exception e) { System.out.println(e.toString()); } } } catch (Exception e) { e.printStackTrace(); try { if (sslServerSocket != null) { sslServerSocket.close(); System.out.println("服務(wù)器關(guān)閉!"); } } catch (Exception ex) { ex.printStackTrace(); } } } } /** * 字節(jié)流-解析客戶端的消息 */ public static void byteMsgReader(InputStream inputStream, OutputStream outputStream) throws Exception { BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream); FileOutputStream bufferedOutputStream1 = new FileOutputStream(writeToFile1, false); FileOutputStream bufferedOutputStream2 = new FileOutputStream(writeToFile2, false); byte[] b = new byte[4096]; int i = 0; while ((i = bufferedInputStream.read(b)) > 0) { bufferedOutputStream1.write(b, 0, i); bufferedOutputStream1.flush(); if (i < b.length) { // 根據(jù)讀取的長度是否滿格判斷當(dāng)前文件是否已讀取完畢 bufferedOutputStream1.close(); break; } } System.out.println("接收完成第一個(gè)文件"); byteMsgWriter(outputStream); i = 0; byte[] b2 = new byte[4096]; while ((i = bufferedInputStream.read(b2)) > 0) { bufferedOutputStream2.write(b2, 0, i); bufferedOutputStream2.flush(); if (i < b2.length) { // 根據(jù)讀取的長度是否滿格判斷當(dāng)前文件是否已讀取完畢 bufferedOutputStream2.close(); break; } } System.out.println("接收完成第二個(gè)文件"); byteMsgWriter(outputStream); } /** * 字節(jié)流-發(fā)送給客戶端回執(zhí)消息 */ public static void byteMsgWriter(OutputStream outputStream) throws Exception { BufferedOutputStream bufferedOutputStreamBack = new BufferedOutputStream(outputStream); bufferedOutputStreamBack.write("服務(wù)端已成功接收文件.".getBytes(StandardCharsets.UTF_8)); bufferedOutputStreamBack.write("\n".getBytes(StandardCharsets.UTF_8)); bufferedOutputStreamBack.flush(); // 第一次發(fā)送消息,長連接實(shí)現(xiàn)方式 } }
4、客戶端代碼
package com.ssl.client; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocket; import javax.net.ssl.TrustManagerFactory; import java.io.*; import java.security.KeyStore; import java.security.cert.CertificateFactory; public class ClientTest { public static File readFile = new File("C:\\Users\\Li\\Desktop\\temp\\sqlV2.0.sql"); public static void main(String[] args) { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } new Thread(new ClientOne()).start(); } public static class ClientOne implements Runnable { private static final String TLS = "TLSv1.2"; private static final String PROVIDER = "SunX509"; private static final String STORE_TYPE = "JKS"; private static final String TRUST_STORE_NAME = "E:\\02-IDEA_Project\\Test01_HttpsClient\\server.cer"; @Override public void run() { SSLSocket socket = null; try { SSLContext sslContext = SSLContext.getInstance(TLS); //生成信任證書Manager,默認(rèn)系統(tǒng)會信任CA機(jī)構(gòu)頒發(fā)的證書,自定的證書需要手動的加載 TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(PROVIDER); KeyStore keyStore = KeyStore.getInstance(STORE_TYPE); keyStore.load(null); //生成驗(yàn)證工廠 CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); //生成別名(可以隨便填寫) String certificateAlias = Integer.toString(0); //加載證書 keyStore.setCertificateEntry(certificateAlias, certificateFactory.generateCertificate(new FileInputStream(TRUST_STORE_NAME))); //初始化 trustManagerFactory.init(keyStore); //初始化 sslContext.init(null, trustManagerFactory.getTrustManagers(), null); socket = (SSLSocket) sslContext.getSocketFactory().createSocket("localhost", 8090); socket.setKeepAlive(true); // socket.setSoTimeout(10000); //設(shè)置超時(shí)時(shí)間 try ( InputStream inputStream = socket.getInputStream(); OutputStream outputStream = socket.getOutputStream(); ) { byteMsgWriter(outputStream, inputStream); } catch (Exception ex) { ex.printStackTrace(); } } catch (Exception e) { e.printStackTrace(); try { if (socket != null) { socket.close(); System.out.println("客戶端關(guān)閉"); } } catch (Exception ex) { ex.printStackTrace(); } } } } /** * 字節(jié)流-發(fā)送給服務(wù)端 */ public static void byteMsgWriter(OutputStream outputStream, InputStream inputStream) throws Exception { BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(readFile)); BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(outputStream); //mark后讀取超過readlimit字節(jié)數(shù)據(jù),mark標(biāo)記就會失效 bufferedInputStream.mark(909600000); byte[] b = new byte[4096]; int i; while ((i = bufferedInputStream.read(b)) > 0) { bufferedOutputStream.write(b, 0, i); bufferedOutputStream.flush(); // 第一次發(fā)送消息,長連接實(shí)現(xiàn)方式 } bufferedInputStream.reset(); Thread.sleep(1); // 必須加休眠,否則第二個(gè)文件流會發(fā)生錯(cuò)亂 characterMsgReader(inputStream); // 從服務(wù)端接收消息 while ((i = bufferedInputStream.read(b)) > 0) { bufferedOutputStream.write(b, 0, i); bufferedOutputStream.flush(); // 第二次發(fā)送消息,長連接實(shí)現(xiàn)方式 } characterMsgReader(inputStream); // 從服務(wù)端接收消息 } /** * 字符流-解析服務(wù)端的回執(zhí)消息-不間斷解析 */ public static void characterMsgReader(InputStream inputStream) throws Exception { BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); String line; while ((line = bufferedReader.readLine()) != null) { System.out.println("服務(wù)端消息: " + line); } } }
二、雙向認(rèn)證
1、生成客戶端密鑰
命令:keytool -genkey -keystore client_ks.jks -storepass client_password -keyalg RSA -keypass client_password
結(jié)果:會生成client_ks.jks密鑰文件
操作:將生成的client_ks.jks密鑰文件配置到客戶端
2、生成客戶端證書
命令:keytool -export -keystore client_ks.jks -storepass client_password -file client.cer
結(jié)果:會生成client.cer證書文件
3、將server端證書添加到serverTrust_ks.jks文件中
命令:keytool -import -keystore serverTrust_ks.jks -storepass client -file server.cer
結(jié)果:會生成serverTrust_ks.jks密鑰文件
操作:將生成的serverTrust_ks.jks密鑰文件配置到客戶端
4、將client端證書添加到clientTrust_ks.jks文件中
命令:keytool -import -keystore clientTrust_ks.jks -storepass server -file client.cer
結(jié)果:會生成clientTrust_ks.jks密鑰文件
操作:將生成的clientTrust_ks.jks密鑰文件配置到服務(wù)端
5、服務(wù)端代碼
package com.ssl.server; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLServerSocket; import javax.net.ssl.TrustManagerFactory; import java.io.*; import java.net.Socket; import java.nio.charset.StandardCharsets; import java.security.KeyStore; public class Server { public static File writeToFile1 = new File("C:\\Users\\Li\\Desktop\\temp\\配置.txt"); public static File writeToFile2 = new File("C:\\Users\\Li\\Desktop\\temp\\配置2.txt"); public static void main(String[] args) { new Thread(new ServerOne()).start(); } public static class ServerOne implements Runnable { //SSL協(xié)議版本 private static final String TLS = "TLSv1.2"; //KeyStore的類型 private static final String PROVIDER = "SunX509"; //秘鑰類型,java默認(rèn)是JKS,Android不支持JKS,只能用BKS private static final String STORE_TYPE = "JKS"; //秘鑰的路徑 private static final String KEY_STORE_NAME = "E:\\02-IDEA_Project\\Test02_HttpsServer\\server_ks.jks"; private static final String TRUST_STORE_NAME = "E:\\02-IDEA_Project\\Test02_HttpsServer\\clientTrust_ks.jks"; //Server的端口 private static final int DEFAULT_PORT = 8090; //自定義端口 //秘鑰的密碼 private static final String SERVER_KEY_STORE_PASSWORD = "server_password"; //秘鑰的密碼 private static final String SERVER_TRUST_KEY_STORE_PASSWORD = "server";//密碼 @Override public void run() { SSLServerSocket sslServerSocket = null; try { //獲取SSLContext SSLContext sslContext = SSLContext.getInstance(TLS); //生成秘鑰的manager KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(PROVIDER); //加載信任的證書 TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(PROVIDER); //加載秘鑰 KeyStore keyStoreOne = KeyStore.getInstance(STORE_TYPE); KeyStore keyStoreTwo = KeyStore.getInstance(STORE_TYPE); keyStoreOne.load(new FileInputStream(KEY_STORE_NAME), SERVER_KEY_STORE_PASSWORD.toCharArray()); keyStoreTwo.load(new FileInputStream(TRUST_STORE_NAME), SERVER_TRUST_KEY_STORE_PASSWORD.toCharArray()); //秘鑰初始化 keyManagerFactory.init(keyStoreOne, SERVER_KEY_STORE_PASSWORD.toCharArray()); trustManagerFactory.init(keyStoreTwo); //初始化SSLContext sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null); //獲取SSLContext的SocketFactory sslServerSocket = (SSLServerSocket) sslContext.getServerSocketFactory().createServerSocket(DEFAULT_PORT); //是否開啟雙向驗(yàn)證 sslServerSocket.setNeedClientAuth(true); System.out.println("服務(wù)器已開啟,等待連接 ....."); while (true) { Socket accept = sslServerSocket.accept(); accept.setKeepAlive(true); System.out.println("客戶端 : " + accept.getInetAddress().getHostAddress()); try ( InputStream inputStream = accept.getInputStream(); OutputStream outputStream = accept.getOutputStream(); ) { byteMsgReader(inputStream, outputStream); // socket.shutdownOutput(); // 長連接則不關(guān)閉輸出流 // socket.shutdownInput(); // 長連接則不關(guān)閉輸入流 } catch (Exception e) { System.out.println(e.toString()); } } } catch (Exception e) { e.printStackTrace(); try { if (sslServerSocket != null) { sslServerSocket.close(); System.out.println("服務(wù)器關(guān)閉!"); } } catch (Exception ex) { ex.printStackTrace(); } } } } /** * 字節(jié)流-解析客戶端的消息 */ public static void byteMsgReader(InputStream inputStream, OutputStream outputStream) throws Exception { BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream); FileOutputStream bufferedOutputStream1 = new FileOutputStream(writeToFile1, false); FileOutputStream bufferedOutputStream2 = new FileOutputStream(writeToFile2, false); byte[] b = new byte[4096]; int i = 0; while ((i = bufferedInputStream.read(b)) > 0) { bufferedOutputStream1.write(b, 0, i); bufferedOutputStream1.flush(); if (i < b.length) { // 根據(jù)讀取的長度是否滿格判斷當(dāng)前文件是否已讀取完畢 bufferedOutputStream1.close(); break; } } System.out.println("接收完成第一個(gè)文件"); byteMsgWriter(outputStream); i = 0; byte[] b2 = new byte[4096]; while ((i = bufferedInputStream.read(b2)) > 0) { bufferedOutputStream2.write(b2, 0, i); bufferedOutputStream2.flush(); if (i < b2.length) { // 根據(jù)讀取的長度是否滿格判斷當(dāng)前文件是否已讀取完畢 bufferedOutputStream2.close(); break; } } System.out.println("接收完成第二個(gè)文件"); byteMsgWriter(outputStream); } /** * 字節(jié)流-發(fā)送給客戶端回執(zhí)消息 */ public static void byteMsgWriter(OutputStream outputStream) throws Exception { BufferedOutputStream bufferedOutputStreamBack = new BufferedOutputStream(outputStream); bufferedOutputStreamBack.write("服務(wù)端已成功接收文件.".getBytes(StandardCharsets.UTF_8)); bufferedOutputStreamBack.write("\n".getBytes(StandardCharsets.UTF_8)); bufferedOutputStreamBack.flush(); // 第一次發(fā)送消息,長連接實(shí)現(xiàn)方式 } }
6、客戶端代碼
package com.ssl.client; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocket; import javax.net.ssl.TrustManagerFactory; import java.io.*; import java.security.KeyStore; public class Client { public static File readFile = new File("C:\\Users\\Li\\Desktop\\temp\\sqlV2.0.sql"); public static void main(String[] args) { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } new Thread(new ClientOne()).start(); } public static class ClientOne implements Runnable { private static final String TLS = "TLSv1.2"; private static final String PROVIDER = "SunX509"; private static final String STORE_TYPE = "JKS"; private static final String TRUST_STORE_NAME = "E:\\02-IDEA_Project\\Test01_HttpsClient\\serverTrust_ks.jks"; private static final String KEY_STORE_NAME = "E:\\02-IDEA_Project\\Test01_HttpsClient\\client_ks.jks"; private static final String CLIENT_KEY_STORE_PASSWORD = "client_password"; //密碼 private static final String CLIENT_TRUST_KEY_STORE_PASSWORD = "client";//密碼 @Override public void run() { SSLSocket socket = null; try { SSLContext sslContext = SSLContext.getInstance(TLS); KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(PROVIDER); //生成信任證書Manager,默認(rèn)系統(tǒng)會信任CA機(jī)構(gòu)頒發(fā)的證書,自定的證書需要手動的加載 TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(PROVIDER); KeyStore keyStoreOne = KeyStore.getInstance(STORE_TYPE); KeyStore keyStoreTwo = KeyStore.getInstance(STORE_TYPE); //加載client端密鑰 keyStoreOne.load(new FileInputStream(KEY_STORE_NAME), CLIENT_KEY_STORE_PASSWORD.toCharArray()); //信任證書 keyStoreTwo.load(new FileInputStream(TRUST_STORE_NAME), CLIENT_TRUST_KEY_STORE_PASSWORD.toCharArray()); keyManagerFactory.init(keyStoreOne, CLIENT_KEY_STORE_PASSWORD.toCharArray()); trustManagerFactory.init(keyStoreTwo); //初始化 sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null); //此種寫法代表客戶端信任所有證書 /*TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() { public java.security.cert.X509Certificate[] getAcceptedIssuers() { return new java.security.cert.X509Certificate[]{}; } public void checkClientTrusted(X509Certificate[] chain, String authType) { } public void checkServerTrusted(X509Certificate[] chain, String authType) { } }}; //初始化 sslContext.init(keyManagerFactory.getKeyManagers(), trustAllCerts, null);*/ socket = (SSLSocket) sslContext.getSocketFactory().createSocket("localhost", 8090); socket.setKeepAlive(true); // socket.setSoTimeout(10000); try ( InputStream inputStream = socket.getInputStream(); OutputStream outputStream = socket.getOutputStream(); ) { byteMsgWriter(outputStream, inputStream); } catch (Exception ex) { ex.printStackTrace(); } } catch (Exception e) { e.printStackTrace(); try { if (socket != null) { socket.close(); System.out.println("客戶端關(guān)閉"); } } catch (Exception ex) { ex.printStackTrace(); } } } } /** * 字節(jié)流-發(fā)送給服務(wù)端 */ public static void byteMsgWriter(OutputStream outputStream, InputStream inputStream) throws Exception { BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(readFile)); BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(outputStream); //mark后讀取超過readlimit字節(jié)數(shù)據(jù),mark標(biāo)記就會失效 bufferedInputStream.mark(909600000); byte[] b = new byte[4096]; int i; while ((i = bufferedInputStream.read(b)) > 0) { bufferedOutputStream.write(b, 0, i); bufferedOutputStream.flush(); // 第一次發(fā)送消息,長連接實(shí)現(xiàn)方式 } bufferedInputStream.reset(); Thread.sleep(1); // 必須加休眠,否則第二個(gè)文件流會發(fā)生錯(cuò)亂 characterMsgReader(inputStream); // 從服務(wù)端接收消息 while ((i = bufferedInputStream.read(b)) > 0) { bufferedOutputStream.write(b, 0, i); bufferedOutputStream.flush(); // 第二次發(fā)送消息,長連接實(shí)現(xiàn)方式 } characterMsgReader(inputStream); // 從服務(wù)端接收消息 } /** * 字符流-解析服務(wù)端的回執(zhí)消息-不間斷解析 */ public static void characterMsgReader(InputStream inputStream) throws Exception { BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); String line; while ((line = bufferedReader.readLine()) != null) { System.out.println("服務(wù)端消息: " + line); } } }
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
SpringBoot結(jié)合Maven項(xiàng)目依賴版本沖突問題解決
本文主要介紹了SpringBoot結(jié)合Maven項(xiàng)目依賴版本沖突問題解決,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-06-06解決SSLContext.getInstance()中參數(shù)設(shè)置TLS版本無效的問題
這篇文章主要介紹了解決SSLContext.getInstance()中參數(shù)設(shè)置TLS版本無效的問題,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-01-01華為鴻蒙系統(tǒng)應(yīng)用開發(fā)工具 DevEco Studio的安裝和使用圖文教程
HUAWEI DevEco Studio 是華為消費(fèi)者業(yè)務(wù)為開發(fā)者提供的集成開發(fā)環(huán)境(IDE),旨在幫助開發(fā)者快捷、方便、高效地使用華為EMUI開放能力。這篇文章主要介紹了華為鴻蒙系統(tǒng)應(yīng)用開發(fā)工具 DevEco Studio的安裝和使用圖文教程,需要的朋友可以參考下2021-04-04SpringBoot實(shí)現(xiàn)接口防刷的五種方案
接口防刷是保障系統(tǒng)安全與穩(wěn)定性的重要措施,惡意的高頻請求不僅會消耗服務(wù)器資源,還可能導(dǎo)致數(shù)據(jù)異常,甚至系統(tǒng)癱瘓,本文將介紹在SpringBoot框架下實(shí)現(xiàn)接口防刷的5種技術(shù)方案,需要的朋友可以參考下2025-04-04靈活控制任務(wù)執(zhí)行時(shí)間的Cron表達(dá)式范例
這篇文章主要為大家介紹了靈活控制任務(wù)執(zhí)行時(shí)間的Cron表達(dá)式范例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-10-10一篇超詳細(xì)的Spring Boot整合Mybatis文章
大家都知道springboot搭建一個(gè)spring框架只需要秒秒鐘。下面通過實(shí)例代碼給大家介紹一下springboot與mybatis的完美融合,非常不錯(cuò),具有參考借鑒價(jià)值,感興趣的朋友一起看看吧2021-07-07Java實(shí)現(xiàn)PDF轉(zhuǎn)HTML/Word/Excel/PPT/PNG的示例代碼
這篇文章主要為大家介紹了如何利用Java語言是PDF轉(zhuǎn)HTML、Word、Excel、PPT和PNG功能,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解一下2022-05-05