目的
数据加密的方式有很多,主要分有对称加密和非对称加密,为了保证数据的安全,我们往往使用的是非对称加密,就比如HTTPS就是使用TLS非对称加密,我们这里就来简单实现一下TLS的加密隧道,并且添加信任自己生成的SSL证书。
HTTPS协议
HTTPS是一种通过计算机网络进行安全通信的协议。它是基于HTTP的加密协议,用于在网络中安全地传输数据。HTTPS通过使用TLS或其前身SSL来加密通信内容,以确保数据在传输过程中的保密性和完整性。
SSL证书
我们可以通过java的keytool生成证书,重点是-ext
配置的域名和ip,不然浏览器会提示不安全。
# 生成密钥对,包含有效期365天,-ext 设置域名
keytool -genkeypair -alias mycert -keyalg RSA -keysize 2048 -keystore keystore.jks -validity 365 -ext SAN=dns:www.test.com,ip:192.168.35.124
# 生成证书请求
keytool -certreq -alias mycert -keystore keystore.jks -file server.csr
# 生成自签名证书
keytool -gencert -alias mycert -keystore keystore.jks -infile server.csr -outfile server.crt -validity 365
安装证书
双击上面生成的server.crt
安装证书,添加到受信任的根证书颁发机构
。
可以在Internet属性
->内容
->证书
->受信任的根证书颁发机构
中查看
注意:证书更换要重新启动浏览器才生效。
Java代码
服务端
首先我们做实现一个带HTTPS的Web服务端,HTTP协议我们可以通过ServerSocket
去完成协议,而HTTPS我们使用SSLServerSocket
,加了KeyStore
和SSLContext
多了些SSL内容。下面代码就打印HTTP信息,并且返回Hello HTTPS!
的HTTP报文。注意判断HTTP报文结束是两个换行,不然后会阻塞等待内容。
public class ServerMain {
public static void main(String[] args) {
//创建线程池
ExecutorService executorService = Executors.newFixedThreadPool(5);
try {
//载入keystore
char[] keystorePassword = "password".toCharArray();
char[] keyPassword = "password".toCharArray();
InputStream resourceAsStream = ServerMain.class.getClassLoader().getResourceAsStream("keystore.jks");
KeyStore keystore = KeyStore.getInstance("JKS");
keystore.load(resourceAsStream, keystorePassword);
//创建和初始化SSLContext
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(keystore, keyPassword);
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(keystore);
SSLContext context = SSLContext.getInstance("TLS");
context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
//创建SSLServerSocket
SSLServerSocketFactory factory = context.getServerSocketFactory();
SSLServerSocket serverSocket = (SSLServerSocket) factory.createServerSocket(12345);
while (true) {
//接受客户端连接
SSLSocket socket = (SSLSocket) serverSocket.accept();
// 在线程池中处理客户端连接
executorService.submit(() -> {
try {
handleClient(socket);
} catch (IOException e) {
e.printStackTrace();
}
});
}
} catch (Exception e) {
e.printStackTrace();
}
}
private static void handleClient(SSLSocket socket) throws IOException {
try (
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()))
) {
// 读取HTTP请求
StringBuilder request = new StringBuilder();
String line;
while ((line = reader.readLine()) != null && !line.isEmpty()) {
request.append(line).append("\r\n");
}
System.out.println("Received from client: " + request);
// 写入HTTP响应
String response = "HTTP/1.1 200 OK\r\n" +
"Content-Type: text/html; charset=UTF-8\r\n" +
"Connection: close\r\n" + // Close connection after sending response
"\r\n" +
"Hello HTTPS!";
writer.write(response);
writer.flush();
} finally {
// 关闭 socket
socket.close();
}
}
}
服务端目录结构
通过浏览器输入地址:https://192.168.35.124:12345/
访问测试
尝试删掉证书,浏览器显示,java后台也报错了javax.net.ssl.SSLHandshakeException: Received fatal alert: certificate_unknown
客户端
客户端代码就简单发送了Hello from client
后面加了两个换行保证了结束。
public class ClientMain {
public static void main(String[] args) throws Exception {
// 载入 truststore
char[] truststorePassword = "password".toCharArray();
KeyStore truststore = KeyStore.getInstance("JKS");
InputStream resourceAsStream = ClientMain.class.getClassLoader().getResourceAsStream("keystore.jks");
truststore.load(resourceAsStream, truststorePassword);
// 创建和初始化 SSLContext
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(truststore);
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, tmf.getTrustManagers(), null);
// 创建 SSLSocket
SSLSocketFactory factory = context.getSocketFactory();
SSLSocket socket = (SSLSocket) factory.createSocket("127.0.0.1", 12345);
try(BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));){
writer.write("Hello from client\r\n\r\n");
writer.flush();
// 读取响应请求
StringBuilder response = new StringBuilder();
String line;
while ((line = reader.readLine()) != null && !line.isEmpty()) {
response.append(line).append("\r\n");
}
System.out.println("Received from server: " + response);
}finally {
// 关闭 socket
socket.close();
}
}
}
客户端目录结构
客户端控制台输出
服务端控制台输出