A HTTPS client and HTTPS server demo in Java

  Pi Ke        2015-10-23 09:04:36       70,987        5          English  简体中文  Tiếng Việt 

在这篇文章中,我将创建一个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连接模型的更多知识。

JAVA  SSL  DEMO  HTTPS 

       

  RELATED


  5 COMMENTS


shashi kumar [Reply]@ 2017-07-05 04:35:20

Thanks for the info, It helped me .

Anonymous [Reply]@ 2019-01-15 15:56:01

I get javax.net.ssl.SSLHandshakeException: no cipher suites in common with this.

I tried this code to generate SSL key file

        try{
            KeyStore keyStore = KeyStore.getInstance("JKS");
            keyStore.load(null,null);

            keyStore.store(new FileOutputStream("test.jks"), "passphrase".toCharArray());
        }catch(Exception ex){
            ex.printStackTrace();
        }
Ke Pi [Reply]@ 2019-01-17 09:11:30

This error means no suitable cipher suite can be used to do handshake. This can be caused by many reasons.

Please check java.security file to ensure that the algorithms are not disabled for the keys/certificates generated in test.jks. Typical cases are that weak algorithms like(RC4) are used and are not allowed.

Anonymous [Reply]@ 2022-03-10 09:12:06

Getting below wxception:

java.io.FileNotFoundException: test.jks (The system cannot find the file specified)

Ke Pi [Reply]@ 2022-04-04 05:08:08

do u have the test.jks? this is just an example, you should create ur own keystore.



  RANDOM FUN

Code blue