java使用https协议访问(自签名证书,运行时指定信任库(不修改系统证书))
需求:在本地实现java的文字聊天功能。编写两个java对象,一个对象代表服务器,一个对象代表客端,一个代表客户端。客户端输入文字,服务器端接收并响应结果给客户端。
前置条件:首先生成密钥库和证书
# 生成服务器密钥库和自签名证书
keytool -genkeypair -alias server -keyalg RSA -keysize 2048 -storetype JKS -keystore server.keystore -validity 365 -storepass serverpass -keypass serverpass -dname "CN=localhost, OU=Example, O=Example, L=City, ST=State, C=US"# 导出服务器证书
keytool -exportcert -alias server -keystore server.keystore -file server.crt -storepass serverpass# 生成客户端信任库并导入服务器证书
keytool -importcert -alias server -file server.crt -keystore client.truststore -storepass clientpass -noprompt
服务器端口代码如下:
package com.example.demo.demos;import javax.net.ssl.*;
import java.io.*;
import java.security.KeyStore;public class Server {private static int requestCount = 0; // 计数器记录请求次数public static void main(String[] args) {try {// 加载服务器密钥库(包含私钥和证书)KeyStore keyStore = KeyStore.getInstance("JKS");keyStore.load(new FileInputStream("server.keystore"), "serverpass".toCharArray());// 创建密钥管理器工厂KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");kmf.init(keyStore, "serverpass".toCharArray());// 创建SSL上下文并初始化SSLContext sslContext = SSLContext.getInstance("TLS");sslContext.init(kmf.getKeyManagers(), null, null);// 创建SSL服务器套接字工厂SSLServerSocketFactory sslServerSocketFactory = sslContext.getServerSocketFactory();// 创建SSL服务器套接字并绑定端口SSLServerSocket sslServerSocket = (SSLServerSocket) sslServerSocketFactory.createServerSocket(8443);// 启用所有支持的密码套件和协议sslServerSocket.setEnabledCipherSuites(sslServerSocket.getSupportedCipherSuites());sslServerSocket.setEnabledProtocols(sslServerSocket.getSupportedProtocols());System.out.println("服务器已启动,等待客户端连接...");// 接受客户端连接SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept();// 处理客户端请求try (BufferedReader in = new BufferedReader(new InputStreamReader(sslSocket.getInputStream()));PrintWriter out = new PrintWriter(sslSocket.getOutputStream(), true)) {String inputLine;while ((inputLine = in.readLine()) != null) {requestCount++; // 每次请求计数器+1String response = String.format("第%d次返回结果:%s", requestCount, inputLine); // 格式化响应内容System.out.println("客户端消息: " + inputLine);out.println(response); // 发送格式化后的响应}}sslSocket.close();sslServerSocket.close();} catch (Exception e) {e.printStackTrace();}}
}
客户端代码:
package com.example.demo.demos;import javax.net.ssl.*;
import java.io.*;
import java.security.KeyStore;public class Client {public static void main(String[] args) {try {// 加载客户端信任库(包含服务器证书)KeyStore trustStore = KeyStore.getInstance("JKS");trustStore.load(new FileInputStream("client.truststore"), "clientpass".toCharArray());// 创建信任管理器工厂TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");tmf.init(trustStore);// 创建SSL上下文并初始化SSLContext sslContext = SSLContext.getInstance("TLS");sslContext.init(null, tmf.getTrustManagers(), null);// 创建SSL套接字工厂SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();// 创建SSL套接字并连接到服务器SSLSocket sslSocket = (SSLSocket) sslSocketFactory.createSocket("localhost", 8443);// 启用所有支持的密码套件和协议sslSocket.setEnabledCipherSuites(sslSocket.getSupportedCipherSuites());sslSocket.setEnabledProtocols(sslSocket.getSupportedProtocols());// 向服务器发送消息try (PrintWriter out = new PrintWriter(sslSocket.getOutputStream(), true);BufferedReader in = new BufferedReader(new InputStreamReader(sslSocket.getInputStream()));BufferedReader stdIn = new BufferedReader(new InputStreamReader(System.in))) {System.out.println("请输入内容:");String userInput;while ((userInput = stdIn.readLine()) != null) {out.println(userInput);System.out.println("服务器返回: " + in.readLine());System.out.println("请继续输入内容:");}}sslSocket.close();} catch (Exception e) {e.printStackTrace();}}
}
关键配置说明
-
服务器配置:
- 使用
server.keystore
存储服务器私钥和证书 - 通过
KeyManagerFactory
加载服务器密钥库 - 服务器使用默认信任库验证客户端证书(如果需要双向认证)
- 使用
-
客户端配置:
- 使用
client.truststore
存储信任的服务器证书 - 通过
TrustManagerFactory
加载客户端信任库 - 客户端通过
SSLContext
初始化自定义信任管理器
- 使用
结果:客户端多次输入,服务器均正常返回。