在这篇文章中,我将创建一个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 .