一文詳解如何通過(guò)Java實(shí)現(xiàn)SSL交互功能
創(chuàng)建證書(shū)
因?yàn)橐a(chǎn)生 key 信任庫(kù),要求的密碼至少的 6位數(shù),所以密碼設(shè)為 123456
- 首先 生成 根密鑰和根證書(shū)
- 然后 生成 客戶(hù)端密鑰 和 客戶(hù)端證書(shū),使用根證書(shū)對(duì)應(yīng)客戶(hù)端證書(shū)簽名,將根證書(shū)和簽名客戶(hù)端證書(shū)添加到密鑰庫(kù)中
- 最后 同上生成 服務(wù)端簽名證書(shū),并將根證書(shū)和服務(wù)端證書(shū)添加到 密鑰庫(kù)中。
對(duì)應(yīng)的腳本如下所示:
#!/bin/bash
# 加個(gè)前綴
PFX='file'
# Password 這里只能使用單引號(hào)
PASS='123456'
echo "---- 產(chǎn)生根相關(guān)文件 ----"
echo "創(chuàng)建自簽名的根密鑰"
openssl genrsa -out ${PFX}.rootkey.pem 2048
echo "生成根證書(shū)"
openssl req -x509 -new -key ${PFX}.rootkey.pem -out ${PFX}.root.crt -subj "/C=CN/ST=GD/L=GZ/O=RootCA/OU=RootCA/CN=RootCA"
echo "----- 產(chǎn)生客戶(hù)端相關(guān)文件 -----"
echo "生成客戶(hù)端密鑰"
openssl genrsa -out ${PFX}.clientkey.pem 2048
echo "生成客戶(hù)端證書(shū)請(qǐng)求文件,使用根證書(shū)進(jìn)行簽發(fā)"
openssl req -new -key ${PFX}.clientkey.pem -out ${PFX}.client.csr -subj "/C=CN/ST=GD/L=GZ/O=BMW/OU=Vehicle/CN=Vehicle1"
echo "用根證書(shū)來(lái)簽發(fā)客戶(hù)端請(qǐng)求文件,生成客戶(hù)端證書(shū)client.crt"
openssl x509 -req -in ${PFX}.client.csr -CA ${PFX}.root.crt -CAkey ${PFX}.rootkey.pem -CAcreateserial -days 3650 -out ${PFX}.client.crt
echo "打包客戶(hù)端資料為pkcs12格式(client.pkcs12)"
openssl pkcs12 -export -in ${PFX}.client.crt -inkey clientkey.pem -out ${PFX}.client.pkcs12 -passin "pass:$PASS" -passout "pass:$PASS"
echo "生成信任客戶(hù)端的keystore,把根證書(shū)以及需要信任的客戶(hù)端的證書(shū)添加到這個(gè)keystore"
keytool -importcert -alias ca -file ${PFX}.root.crt -keystore ${PFX}.clienttrust.jks -storepass "$PASS" <<EOF
是
EOF
keytool -importcert -alias clientcert -file ${PFX}.client.crt -keystore ${PFX}.clienttrust.jks -storepass "$PASS"
echo "--------產(chǎn)生服務(wù)端相關(guān)文件----"
echo "生成服務(wù)器端的密匙"
openssl genrsa -out ${PFX}.serverkey.pem 2048
echo "生成服務(wù)器端證書(shū)的請(qǐng)求文件。請(qǐng)求根證書(shū)來(lái)簽發(fā)"
openssl req -new -key ${PFX}.serverkey.pem -out ${PFX}.server.csr -subj "/C=CN/ST=GD/L=GZ/O=BMW/OU=IT/CN=Broker"
echo "用根證書(shū)來(lái)簽發(fā)服務(wù)器端請(qǐng)求文件,生成服務(wù)器端證書(shū)server.crt"
openssl x509 -req -in ${PFX}.server.csr -CA ${PFX}.root.crt -CAkey ${PFX}.rootkey.pem -CAcreateserial -days 3650 -out ${PFX}.server.crt
echo "打包服務(wù)器端資料為pkcs12格式(server.pkcs12 )"
openssl pkcs12 -export -in ${PFX}.server.crt -inkey ${PFX}.serverkey.pem -out ${PFX}.server.pkcs12 -passin "pass:$PASS" -passout "pass:$PASS"
echo "生成信任服務(wù)器端的keystore,把根證書(shū)以及需要信任的服務(wù)端的證書(shū)添加到這個(gè)keystore"
keytool -importcert -alias ca -file ${PFX}.root.crt -keystore ${PFX}.servertrust.jks -storepass "$PASS" <<EOF
是
EOF
keytool -importcert -alias servercert -file ${PFX}.server.crt -keystore ${PFX}.servertrust.jks -storepass "$PASS"生成的文件目錄,如下圖所示:

#編碼
編寫(xiě) SSL Server
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLServerSocket;
import java.net.Socket;
public class SSLServer {
private SSLServerSocket sslServerSocket;
public static void main(String[] args) throws Exception {
SSLServer server = new SSLServer();
server.initTestServer();
server.process();
}
private SSLServerSocket initTestServer() throws Exception {
// ssl-cert 放在 測(cè)試 resources 目錄下
String certDic = SSLServer.class.getClassLoader().getResource("ssl-cert").getPath();
sslServerSocket = initSocket(certDic + "/file.server.pkcs12",
certDic + "/file.clienttrust.jks",
"123456");
// 如果為 false 表示單向認(rèn)證,否則為雙向認(rèn)證
sslServerSocket.setNeedClientAuth(false);
System.out.println("Server test initialted!");
return sslServerSocket;
}
private SSLServerSocket initSocket(String keyPath, String trustPath, String password) throws Exception {
SSLContext context = SSLContext.getInstance("TLSv1.2");
char[] keystorePass = password.toCharArray();
context.init(SSLUtils.creatKey(keyPath, keystorePass).getKeyManagers(),
SSLUtils.creatTrustJks(trustPath, keystorePass).getTrustManagers(),
null);
return (SSLServerSocket) context.getServerSocketFactory().createServerSocket(9999);
}
//服務(wù)端回復(fù)客戶(hù)端
private void process() throws Exception {
String bye = "Hello, I am Server!";
while (true) {
Socket socket = sslServerSocket.accept();
System.out.println("Received: " + SSLUtils.read(socket));
SSLUtils.write(bye, socket);
}
}
}編寫(xiě) SSL Client
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
public class SSLClient {
private SSLSocket sslSocket;
public static void main(String[] args) throws Exception {
SSLClient client = new SSLClient();
client.init();
client.process();
}
private void init() throws Exception {
// ssl-cert 放在 測(cè)試 resources 目錄下
String certDic = this.getClass().getClassLoader().getResource("ssl-cert").getPath();
initSocket(certDic + "/file.server.pkcs12", certDic + "/file.servertrust.jks", "123456");
System.out.println("Client initiated.");
}
private void initSocket(String keystorePath, String trustStorePath, String password) throws Exception {
char[] keystorePass = password.toCharArray();
KeyManager[] keyManagers = null;
KeyManagerFactory keyManagerFactory = SSLUtils.creatKey(keystorePath, keystorePass);
if (keyManagerFactory != null) {
keyManagers = keyManagerFactory.getKeyManagers();
}
TrustManager[] trustManagers = null;
TrustManagerFactory trustManagerFactory = SSLUtils.creatTrustJks(trustStorePath, keystorePass);
if (trustManagerFactory != null) {
trustManagers = trustManagerFactory.getTrustManagers();
}
SSLContext context = SSLContext.getInstance("TLSv1.2");
context.init(keyManagers, trustManagers, null);
String host = "127.0.0.1";
sslSocket = (SSLSocket) context.getSocketFactory().createSocket(host, 9999);
}
public void process() throws Exception {
String hello = "Client Hello";
SSLUtils.write(hello, sslSocket);
System.out.println(SSLUtils.read(sslSocket));
}
}通用編碼
import org.apache.commons.lang3.StringUtils;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.TrustManagerFactory;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.security.KeyStore;
public class SSLUtils {
public static void write(String message, Socket sslSocket) throws Exception {
OutputStream out = sslSocket.getOutputStream();
byte[] messageBytes = message.getBytes(StandardCharsets.UTF_8);
out.write(messageBytes, 0, messageBytes.length);
out.flush();
}
public static String read(Socket sslSocket) throws Exception {
InputStream in = sslSocket.getInputStream();
byte[] buffer = new byte[50];
in.read(buffer);
return new String(buffer);
}
public static KeyManagerFactory creatKey(String keystorePath, char[] keystorePass) throws Exception {
if (StringUtils.isEmpty(keystorePath)) {
return null;
}
KeyManagerFactory kmf = KeyManagerFactory.getInstance("sunx509");
kmf.init(loadKeyStore(keystorePath, keystorePass), keystorePass);
return kmf;
}
public static TrustManagerFactory creatTrustJks(String trustClientKeystorePath, char[] keystorePass) throws Exception {
if (StringUtils.isEmpty(trustClientKeystorePath)) {
return null;
}
TrustManagerFactory tmf = TrustManagerFactory.getInstance("sunx509");
tmf.init(loadKeyStore(trustClientKeystorePath, keystorePass));
return tmf;
}
private static KeyStore loadKeyStore(String keystorePath, char[] keystorePass) throws Exception {
KeyStore serverKeyStore = KeyStore.getInstance(StringUtils.substringAfterLast(keystorePath, "."));
serverKeyStore.load(new FileInputStream(keystorePath), keystorePass);
return serverKeyStore;
}
}測(cè)試
啟動(dòng) SSLServer 和 SSLClient 可以得到的結(jié)果如下所示:


使用 wireshark 抓包如下所示:

這就是對(duì)應(yīng)的 SSL 連接
到此這篇關(guān)于一文詳解如何通過(guò)Java實(shí)現(xiàn)SSL交互功能的文章就介紹到這了,更多相關(guān)Java實(shí)現(xiàn)SSL交互內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
利用JavaFX工具構(gòu)建Reactive系統(tǒng)
這篇文章主要介紹了使用JavaFX構(gòu)建Reactive系統(tǒng),利用JavaFX工具集中的新的超棒特性來(lái)構(gòu)建響應(yīng)式的快速應(yīng)用程序,感興趣的小伙伴們可以參考一下2016-02-02
SpringBoot使用spring.config.import多種方式導(dǎo)入配置文件
本文主要介紹了SpringBoot使用spring.config.import多種方式導(dǎo)入配置文件,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-05-05
Java統(tǒng)計(jì)英文句子中出現(xiàn)次數(shù)最多的單詞并計(jì)算出現(xiàn)次數(shù)的方法
這篇文章主要介紹了Java統(tǒng)計(jì)英文句子中出現(xiàn)次數(shù)最多的單詞并計(jì)算出現(xiàn)次數(shù)的方法,涉及java針對(duì)英文句子的字符串遍歷、轉(zhuǎn)換、正則替換、計(jì)算等相關(guān)操作技巧,需要的朋友可以參考下2018-01-01
Java 多用戶(hù)登錄限制的實(shí)現(xiàn)方法
最近沒(méi)有事情做,閑的發(fā)呆,于是寫(xiě)個(gè)東東練練手。這篇文章主要介紹了Java 多用戶(hù)登錄限制的實(shí)現(xiàn)方法的相關(guān)資料,需要的朋友可以參考下2016-11-11
Java刷視頻瀏覽量點(diǎn)贊量的實(shí)現(xiàn)代碼
這篇文章主要介紹了Java刷視頻瀏覽量點(diǎn)贊量的實(shí)現(xiàn)代碼,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2017-04-04
springboot+redis過(guò)期事件監(jiān)聽(tīng)實(shí)現(xiàn)過(guò)程解析
這篇文章主要介紹了springboot+redis過(guò)期事件監(jiān)聽(tīng)實(shí)現(xiàn)過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-03-03

