A HTTPS client and HTTPS server demo in Java

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

Trong bài viết này, tôi sẽ tạo một bản demo máy chủ HTTPS và máy khách HTTPS có thể thiết lập giao tiếp HTTPS giữa máy chủ và máy khách bằng Java. Điều này sẽ rất hữu ích khi chúng ta muốn kiểm tra sự hiểu biết của mình về giao tiếp SSL. Chúng ta sẽ sử dụng cả một máy khách SSL chi tiết và một HttpsURLConnection đơn giản làm máy khách HTTPS.

Trước khi tạo máy chủ HTTPS và máy khách HTTPS thực tế, trước tiên chúng ta cần tạo kho khóa và kho tin cậy để máy chủ và máy khách sử dụng. Để tạo kho khóa, chúng ta có thể làm theo hướng dẫn tại Các loại kho khóa khác nhau trong Java -- JKS. Trong bản demo này, chúng ta sẽ sử dụng kho khóa được tạo ra làm cả kho khóa và kho tin cậy. Trong các ứng dụng thực tế, kho khóa và kho tin cậy nên là các tệp riêng biệt.

Máy chủ HTTPS

Bây giờ chúng ta tạo máy chủ HTTPS.  Dưới đây là đoạn mã.

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;
	}
	
	// Tạo và khởi tạo SSLContext
	private SSLContext createSSLContext(){
		try{
			KeyStore keyStore = KeyStore.getInstance("JKS");
			keyStore.load(new FileInputStream("test.jks"),"passphrase".toCharArray());
			
			// Tạo quản lý khóa
			KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
			keyManagerFactory.init(keyStore, "passphrase".toCharArray());
			KeyManager[] km = keyManagerFactory.getKeyManagers();
			
			// Tạo quản lý tin cậy
			TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("SunX509");
			trustManagerFactory.init(keyStore);
			TrustManager[] tm = trustManagerFactory.getTrustManagers();
			
			// Khởi tạo SSLContext
			SSLContext sslContext = SSLContext.getInstance("TLSv1");
			sslContext.init(km,  tm, null);
			
			return sslContext;
		} catch (Exception ex){
			ex.printStackTrace();
		}
		
		return null;
	}
	
	// Bắt đầu chạy máy chủ
	public void run(){
		SSLContext sslContext = this.createSSLContext();
		
		try{
			// Tạo nhà máy socket server
			SSLServerSocketFactory sslServerSocketFactory = sslContext.getServerSocketFactory();
			
			// Tạo socket server
			SSLServerSocket sslServerSocket = (SSLServerSocket) sslServerSocketFactory.createServerSocket(this.port);
			
			System.out.println("Máy chủ SSL đã bắt đầu");
			while(!isServerDone){
				SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept();
				
				// Bắt đầu luồng máy chủ
				new ServerThread(sslSocket).start();
			}
		} catch (Exception ex){
			ex.printStackTrace();
		}
	}
	
	// Luồng xử lý socket từ máy khách
	static class ServerThread extends Thread {
		private SSLSocket sslSocket = null;
		
		ServerThread(SSLSocket sslSocket){
			this.sslSocket = sslSocket;
		}
		
		public void run(){
			sslSocket.setEnabledCipherSuites(sslSocket.getSupportedCipherSuites());
			
			try{
				// Bắt đầu trao đổi khóa
				sslSocket.startHandshake();
				
				// Lấy phiên sau khi kết nối được thiết lập
				SSLSession sslSession = sslSocket.getSession();
				
				System.out.println("SSLSession :");
				System.out.println("\tGiao thức : "+sslSession.getProtocol());
				System.out.println("\tBộ mã hóa : "+sslSession.getCipherSuite());
				
				// Bắt đầu xử lý nội dung ứng dụng
				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("Đầu vào : "+line);
					
					if(line.trim().isEmpty()){
						break;
					}
				}
				
				// Ghi dữ liệu
				printWriter.print("HTTP/1.1 200\r\n");
				printWriter.flush();
				
				sslSocket.close();
			} catch (Exception ex) {
				ex.printStackTrace();
			}
		}
	}
}

Đầu tiên, nó sẽ thiết lập máy chủ SSL với kho khóa và kho tin cậy chính xác được cấu hình. Sau đó, nó sẽ bắt đầu lắng nghe kết nối máy khách. Khi nhận được kết nối máy khách, một ServerThread mới sẽ được tạo để xử lý kết nối máy khách. 

Trong ServerThread, đầu tiên nó sẽ bắt đầu thực hiện trao đổi khóa với máy khách và khi trao đổi khóa hoàn tất thành công, dữ liệu ứng dụng có thể được trao đổi giữa máy chủ và máy khách. Khi máy chủ nhận được dữ liệu ứng dụng từ máy khách, nó sẽ gửi mã phản hồi 200 (OK) cho máy khách.

Máy khách HTTPS

Máy khách HTTPS tương tự như máy chủ HTTPS được tạo ở trên,

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;
	}
	
	// Tạo và khởi tạo SSLContext
	private SSLContext createSSLContext(){
		try{
			KeyStore keyStore = KeyStore.getInstance("JKS");
			keyStore.load(new FileInputStream("test.jks"),"passphrase".toCharArray());
			
			// Tạo quản lý khóa
			KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
			keyManagerFactory.init(keyStore, "passphrase".toCharArray());
			KeyManager[] km = keyManagerFactory.getKeyManagers();
			
			// Tạo quản lý tin cậy
			TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("SunX509");
			trustManagerFactory.init(keyStore);
			TrustManager[] tm = trustManagerFactory.getTrustManagers();
			
			// Khởi tạo SSLContext
			SSLContext sslContext = SSLContext.getInstance("TLSv1");
			sslContext.init(km,  tm, null);
			
			return sslContext;
		} catch (Exception ex){
			ex.printStackTrace();
		}
		
		return null;
	}
	
	// Bắt đầu chạy máy chủ
	public void run(){
		SSLContext sslContext = this.createSSLContext();
		
		try{
			// Tạo nhà máy socket
			SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
			
			// Tạo socket
			SSLSocket sslSocket = (SSLSocket) sslSocketFactory.createSocket(this.host, this.port);
			
			System.out.println("Máy khách SSL đã bắt đầu");
			new ClientThread(sslSocket).start();
		} catch (Exception ex){
			ex.printStackTrace();
		}
	}
	
	// Luồng xử lý socket đến máy chủ
	static class ClientThread extends Thread {
		private SSLSocket sslSocket = null;
		
		ClientThread(SSLSocket sslSocket){
			this.sslSocket = sslSocket;
		}
		
		public void run(){
			sslSocket.setEnabledCipherSuites(sslSocket.getSupportedCipherSuites());
			
			try{
				// Bắt đầu trao đổi khóa
				sslSocket.startHandshake();
				
				// Lấy phiên sau khi kết nối được thiết lập
				SSLSession sslSession = sslSocket.getSession();
				
				System.out.println("SSLSession :");
				System.out.println("\tGiao thức : "+sslSession.getProtocol());
				System.out.println("\tBộ mã hóa : "+sslSession.getCipherSuite());
				
				// Bắt đầu xử lý nội dung ứng dụng
				InputStream inputStream = sslSocket.getInputStream();
				OutputStream outputStream = sslSocket.getOutputStream();
				
				BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
				PrintWriter printWriter = new PrintWriter(new OutputStreamWriter(outputStream));
				
				// Ghi dữ liệu
				printWriter.println("Hello server");
				printWriter.println();
				printWriter.flush();
				
				String line = null;
				while((line = bufferedReader.readLine()) != null){
					System.out.println("Đầu vào : "+line);
					
					if(line.trim().equals("HTTP/1.1 200\r\n")){
						break;
					}
				}
				
				sslSocket.close();
			} catch (Exception ex) {
				ex.printStackTrace();
			}
		}
	}
}

Đầu tiên, nó sẽ thiết lập máy khách SSL với kho khóa và kho tin cậy chính xác. Sau đó, nó sẽ kết nối với máy chủ và một ClientThread mới sẽ được tạo để xử lý kết nối SSL.

Trong ClientThread, điều đầu tiên là bật các bộ mã hóa mà bạn muốn sử dụng. Và sau đó máy khách bắt đầu bắt đầu trao đổi khóa và gửi dữ liệu ứng dụng sau khi trao đổi khóa hoàn tất.

Một máy khách HTTPS khác

Đây là một máy khách HTTPS khác chỉ sử dụng HttpsURLConnection đơn giản để đóng gói tất cả các chi tiết như trao đổi khóa.

import java.net.URL;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLSession;

public class HTTPSClient {
	// Tắt xác minh tên máy chủ cho mục đích trình diễn
	static {
		HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
			@Override
            public boolean verify(String s, SSLSession sslSession) {
                return true;
            }
        });
	}
	
	public static void main(String[] args){
		// Khởi tạo cấu hình
		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("KẾT QUẢ : "+client.getResponseCode());
		} catch (Exception ex){
			ex.printStackTrace();
		}
	}
}

Ở đây, vì mục đích trình diễn, chúng tôi đã tắt thủ công việc xác minh tên máy chủ. Trong ứng dụng thực tế, điều này cần được xử lý cẩn thận vì điều này gây ra nguy cơ vi phạm bảo mật. 

Ngoài ra, chúng ta sử dụng các thuộc tính hệ thống để cấu hình kho tin cậy được máy khách sử dụng để xác minh chứng chỉ máy chủ.

Phần trên trình bày một ứng dụng máy chủ và máy khách HTTPS đơn giản nhưng đầy đủ. Bạn có thể tìm hiểu thêm về mô hình kết nối SSL trong Java từ Oracle.

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

How GC works