在这篇文章中,我将创建一个HTTPS服务器和HTTPS客户端演示,它可以使用Java在服务器和客户端之间建立HTTPS通信。当我们想要测试我们对SSL通信的理解时,这应该非常有用。我们将同时使用详细的SSL客户端和简单的HttpsURLConnection作为HTTPS客户端。
在创建实际的HTTPS服务器和HTTPS客户端之前,我们首先需要生成服务器和客户端将使用的密钥库和信任库。要生成密钥库,我们可以按照 Java中的不同类型的密钥库——JKS中的说明进行操作。在这个演示中,我们将使用生成的密钥库作为密钥库和信任库。在实际应用中,密钥库和信任库应该是单独的文件。
HTTPS服务器
现在我们创建HTTPS服务器。以下是代码片段。
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.security.KeyStore;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
public class HTTPSServer {
private int port = 9999;
private boolean isServerDone = false;
public static void main(String[] args){
HTTPSServer server = new HTTPSServer();
server.run();
}
HTTPSServer(){
}
HTTPSServer(int port){
this.port = port;
}
// 创建并初始化SSLContext
private SSLContext createSSLContext(){
try{
KeyStore keyStore = KeyStore.getInstance("JKS");
keyStore.load(new FileInputStream("test.jks"),"passphrase".toCharArray());
// 创建密钥管理器
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
keyManagerFactory.init(keyStore, "passphrase".toCharArray());
KeyManager[] km = keyManagerFactory.getKeyManagers();
// 创建信任管理器
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("SunX509");
trustManagerFactory.init(keyStore);
TrustManager[] tm = trustManagerFactory.getTrustManagers();
// 初始化SSLContext
SSLContext sslContext = SSLContext.getInstance("TLSv1");
sslContext.init(km, tm, null);
return sslContext;
} catch (Exception ex){
ex.printStackTrace();
}
return null;
}
// 开始运行服务器
public void run(){
SSLContext sslContext = this.createSSLContext();
try{
// 创建服务器套接字工厂
SSLServerSocketFactory sslServerSocketFactory = sslContext.getServerSocketFactory();
// 创建服务器套接字
SSLServerSocket sslServerSocket = (SSLServerSocket) sslServerSocketFactory.createServerSocket(this.port);
System.out.println("SSL服务器已启动");
while(!isServerDone){
SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept();
// 启动服务器线程
new ServerThread(sslSocket).start();
}
} catch (Exception ex){
ex.printStackTrace();
}
}
// 处理来自客户端的套接字的线程
static class ServerThread extends Thread {
private SSLSocket sslSocket = null;
ServerThread(SSLSocket sslSocket){
this.sslSocket = sslSocket;
}
public void run(){
sslSocket.setEnabledCipherSuites(sslSocket.getSupportedCipherSuites());
try{
// 开始握手
sslSocket.startHandshake();
// 建立连接后获取会话
SSLSession sslSession = sslSocket.getSession();
System.out.println("SSLSession :");
System.out.println("\t协议 : "+sslSession.getProtocol());
System.out.println("\t密码套件 : "+sslSession.getCipherSuite());
// 开始处理应用程序内容
InputStream inputStream = sslSocket.getInputStream();
OutputStream outputStream = sslSocket.getOutputStream();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
PrintWriter printWriter = new PrintWriter(new OutputStreamWriter(outputStream));
String line = null;
while((line = bufferedReader.readLine()) != null){
System.out.println("输入 : "+line);
if(line.trim().isEmpty()){
break;
}
}
// 写入数据
printWriter.print("HTTP/1.1 200\r\n");
printWriter.flush();
sslSocket.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
}
它将首先使用正确配置的密钥库和信任库设置SSL服务器,然后它将开始监听客户端连接。一旦收到客户端连接,就会产生一个新的ServerThread来处理客户端连接。
在ServerThread中,它将首先开始与客户端执行握手,一旦握手成功完成,服务器和客户端之间就可以交换应用程序数据。当服务器从客户端接收应用程序数据时,它将向客户端发送200(OK)的响应代码。
HTTPS客户端
HTTPS客户端类似于上面创建的HTTPS服务器,
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.security.KeyStore;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
public class HTTPSClient {
private String host = "127.0.0.1";
private int port = 9999;
public static void main(String[] args){
HTTPSClient client = new HTTPSClient();
client.run();
}
HTTPSClient(){
}
HTTPSClient(String host, int port){
this.host = host;
this.port = port;
}
// 创建并初始化SSLContext
private SSLContext createSSLContext(){
try{
KeyStore keyStore = KeyStore.getInstance("JKS");
keyStore.load(new FileInputStream("test.jks"),"passphrase".toCharArray());
// 创建密钥管理器
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
keyManagerFactory.init(keyStore, "passphrase".toCharArray());
KeyManager[] km = keyManagerFactory.getKeyManagers();
// 创建信任管理器
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("SunX509");
trustManagerFactory.init(keyStore);
TrustManager[] tm = trustManagerFactory.getTrustManagers();
// 初始化SSLContext
SSLContext sslContext = SSLContext.getInstance("TLSv1");
sslContext.init(km, tm, null);
return sslContext;
} catch (Exception ex){
ex.printStackTrace();
}
return null;
}
// 开始运行服务器
public void run(){
SSLContext sslContext = this.createSSLContext();
try{
// 创建套接字工厂
SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
// 创建套接字
SSLSocket sslSocket = (SSLSocket) sslSocketFactory.createSocket(this.host, this.port);
System.out.println("SSL客户端已启动");
new ClientThread(sslSocket).start();
} catch (Exception ex){
ex.printStackTrace();
}
}
// 处理到服务器的套接字的线程
static class ClientThread extends Thread {
private SSLSocket sslSocket = null;
ClientThread(SSLSocket sslSocket){
this.sslSocket = sslSocket;
}
public void run(){
sslSocket.setEnabledCipherSuites(sslSocket.getSupportedCipherSuites());
try{
// 开始握手
sslSocket.startHandshake();
// 建立连接后获取会话
SSLSession sslSession = sslSocket.getSession();
System.out.println("SSLSession :");
System.out.println("\t协议 : "+sslSession.getProtocol());
System.out.println("\t密码套件 : "+sslSession.getCipherSuite());
// 开始处理应用程序内容
InputStream inputStream = sslSocket.getInputStream();
OutputStream outputStream = sslSocket.getOutputStream();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
PrintWriter printWriter = new PrintWriter(new OutputStreamWriter(outputStream));
// 写入数据
printWriter.println("Hello server");
printWriter.println();
printWriter.flush();
String line = null;
while((line = bufferedReader.readLine()) != null){
System.out.println("输入 : "+line);
if(line.trim().equals("HTTP/1.1 200\r\n")){
break;
}
}
sslSocket.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
}
它将首先使用正确的密钥库和信任库设置SSL客户端。然后它将连接到服务器,并将产生一个新的ClientThread来处理SSL连接。
在ClientThread中,首先要启用要使用的密码套件。然后客户端开始启动握手并在握手完成后发送应用程序数据。
另一个HTTPS客户端
这是一个另一个HTTPS客户端,它只使用简单的HttpsURLConnection来封装所有细节,例如握手。
import java.net.URL;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLSession;
public class HTTPSClient {
// 为演示目的禁用主机名验证
static {
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String s, SSLSession sslSession) {
return true;
}
});
}
public static void main(String[] args){
// 初始化配置
System.setProperty("javax.net.ssl.trustStore", "test.jks");
System.setProperty("javax.net.ssl.trustStoreType", "jks");
try{
URL url = new URL("https://127.0.0.1:9999");
HttpsURLConnection client = (HttpsURLConnection) url.openConnection();
System.out.println("返回 : "+client.getResponseCode());
} catch (Exception ex){
ex.printStackTrace();
}
}
}
在这里,为了演示的目的,我们手动禁用了主机名验证。在实际应用中,应该谨慎处理这个问题,因为它带来了潜在的安全漏洞。
此外,我们使用系统属性来配置客户端用来验证服务器证书的信任库。
以上演示了一个简单但完整的HTTPS服务器和客户端应用程序。您可以从Oracle获取有关Java中的SSL连接模型的更多知识。
Thanks for the info, It helped me .