当前位置: 首页 > news >正文

[密码学实战]Java实现TLS 1.2单向认证

一、结果验证

1.1 代码运行客户端

在这里插入图片描述

1.2 代码运行服务端

在这里插入图片描述

1.3 浏览器访问

在这里插入图片描述

二、TLS单向认证核心原理

2.1 TLS协议分层架构

TLS协议栈
握手协议
记录协议
告警协议
密钥协商
身份验证
数据加密
数据完整性校验

2.2 TLS 1.3单向认证握手流程

Client Server ClientHello (支持密码套件列表) ServerHello (选定密码套件) + 证书 + 证书验证 Finished Finished 服务端身份验证完成,开始加密通信 Client Server

2.3 单向认证技术优势

  • 性能优化:减少证书交换次数,提升握手速度
  • 部署简单:只需服务端维护证书体系
  • 兼容性强:适配浏览器等通用客户端

三、单向认证典型应用场景

3.1 HTTPS网站建设

  • 电商平台用户数据保护
  • 企业官网信息加密传输

3.2 API接口安全

  • 移动APP与后端服务通信
  • 第三方系统数据对接

3.3 云服务访问控制

  • 云主机SSH加密连接
  • 对象存储服务安全传输

四、Java实现TLS单向认证完整代码

4.1 证书生成

# 生成密钥对,包含有效期365天,-ext 设置域名

keytool -genkeypair -alias mycert -keyalg RSA -keysize 2048 -keystore keystore.jks -validity 365 -ext SAN=dns:www.test.com,ip:192.168.231.1

# 生成证书请求

keytool -certreq -alias mycert -keystore keystore.jks -file server.csr

# 生成自签名证书

keytool -gencert -alias mycert -keystore keystore.jks -infile server.csr -outfile server.crt -validity 365

在这里插入图片描述

4.2 证书导入

在这里插入图片描述

4.3 服务端实现

package wh.com;

import javax.net.ssl.*;
import java.io.*;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import static java.util.concurrent.Executors.newFixedThreadPool;

public class SSLServer {
    // 配置常量
    private static final int SERVER_PORT = 12345;
    private static final String KEYSTORE_PATH = "keystore.jks";
    private static final char[] KEYSTORE_PASSWORD = "123456".toCharArray();
    private static final char[] KEY_PASSWORD = "123456".toCharArray();
    private static final String[] ENABLED_PROTOCOLS = {"TLSv1.2"};
    private static final String[] ENABLED_CIPHERS = {


            "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384"
    };

    public static void main(String[] args) {
        ExecutorService executor =newFixedThreadPool(10);

        try (SSLServerSocket serverSocket = createSSLServerSocket()) {
            System.out.println("SSL服务器已启动,监听端口: " + SERVER_PORT);
            while (!Thread.currentThread().isInterrupted()) {
                SSLSocket clientSocket = (SSLSocket) serverSocket.accept();
                executor.submit(() -> handleClient(clientSocket));
            }
        } catch (Exception e) {
            handleFatalError("服务器启动失败", e);
        } finally {
            executor.shutdown();
        }
    }

    private static SSLServerSocket createSSLServerSocket() throws Exception {
        SSLContext sslContext = createSSLContext();
        SSLServerSocketFactory factory = sslContext.getServerSocketFactory();
        SSLServerSocket serverSocket = (SSLServerSocket) factory.createServerSocket(SERVER_PORT);

        // 安全配置
        serverSocket.setEnabledProtocols(ENABLED_PROTOCOLS);
        serverSocket.setEnabledCipherSuites(ENABLED_CIPHERS);
        serverSocket.setNeedClientAuth(false); // 禁用客户端证书验证

        return serverSocket;
    }

    private static SSLContext createSSLContext() throws Exception {
        SSLContext context = SSLContext.getInstance("TLS");
        context.init(
                createKeyManagers(),
                createTrustManagers(),
                new SecureRandom()
        );
        return context;
    }

    private static KeyManager[] createKeyManagers() throws Exception {
        KeyStore ks = loadKeyStore();
        KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
        kmf.init(ks, KEY_PASSWORD);
        return kmf.getKeyManagers();
    }

    private static TrustManager[] createTrustManagers() throws Exception {
        // 生产环境应使用正式信任库,此处示例加载相同密钥库
        KeyStore ts = loadKeyStore();
        TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
        tmf.init(ts);
        return tmf.getTrustManagers();
    }

    private static KeyStore loadKeyStore() throws Exception {
        try (InputStream is = getResourceStream(KEYSTORE_PATH)) {
            KeyStore ks = KeyStore.getInstance("JKS");
            ks.load(is, KEYSTORE_PASSWORD);
            return ks;
        }
    }

    private static InputStream getResourceStream(String path) throws FileNotFoundException {
        InputStream is = SSLServer.class.getClassLoader().getResourceAsStream(path);
        if (is == null) {
            throw new FileNotFoundException("密钥库文件未找到: " + path);
        }
        return is;
    }

    private static void handleClient(SSLSocket socket) {
        try (
             BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
             BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()))) {

            // 读取请求头
            StringBuilder request = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null && !line.isEmpty()) {
                request.append(line).append("\n");
            }

            logRequest(socket, request.toString());
            sendResponse(writer, createSuccessResponse());

        } catch (SSLHandshakeException e) {
            handleHandshakeError(socket, e);
        } catch (IOException e) {
            handleIOError(socket, e);
        }
    }

    private static void logRequest(SSLSocket socket, String request) {
        System.out.printf("[%s] 收到请求:\n%s\n",
                socket.getRemoteSocketAddress(),
                request);
    }

    private static void sendResponse(BufferedWriter writer, String response) throws IOException {
        writer.write(response);
        writer.flush();
    }

    private static String createSuccessResponse() {
        return "HTTP/1.1 200 OK\r\n" +
                "Content-Type: text/plain; charset=utf-8\r\n" +
                "Connection: close\r\n" +
                "\r\n" +
                "安全连接已建立!";
    }

    private static void handleHandshakeError(SSLSocket socket, SSLHandshakeException e) {
        System.err.printf("SSL握手失败 [%s]: %s\n",
                socket.getRemoteSocketAddress(),
                e.getMessage());

        // 调试用:打印协议和密码套件支持情况
        System.out.println("支持的协议: " + Arrays.toString(socket.getEnabledProtocols()));
        System.out.println("支持的密码套件: " + Arrays.toString(socket.getEnabledCipherSuites()));
    }

    private static void handleIOError(SSLSocket socket, IOException e) {
        System.err.printf("客户端通信异常 [%s]: %s\n",
                socket.getRemoteSocketAddress(),
                e.getMessage());
    }

    private static void handleFatalError(String message, Throwable t) {
        System.err.println(message + ": " + t.getMessage());
        t.printStackTrace();
        System.exit(1);
    }
}

4.4 客户端实现

package wh.com;

import javax.net.ssl.*;
import java.io.*;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.util.Arrays;

public class SSLClient {
    private static final String SERVER_HOST = "127.0.0.1";
    private static final int SERVER_PORT = 12345;
    private static final String TRUSTSTORE_PATH = "keystore.jks";
    private static final char[] TRUSTSTORE_PASSWORD = "123456".toCharArray();
    private static final String[] ENABLED_PROTOCOLS = {"TLSv1.2"};
    private static final String[] ENABLED_CIPHERS = {
            "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384"
    };

    public static void main(String[] args) {
        try {
            SSLSocket socket = createSSLSocket();
            configureSocket(socket);
            communicateWithServer(socket);
        } catch (Exception e) {
            handleError("客户端运行异常", e);
        }
    }

    private static SSLSocket createSSLSocket() throws Exception {
        SSLContext context = createSSLContext();
        SSLSocketFactory factory = context.getSocketFactory();
        return (SSLSocket) factory.createSocket(SERVER_HOST, SERVER_PORT);
    }

    private static SSLContext createSSLContext() throws Exception {
        SSLContext context = SSLContext.getInstance("TLS");
        context.init(null, createTrustManagers(), new SecureRandom());
        return context;
    }

    private static TrustManager[] createTrustManagers() throws Exception {
        TrustManagerFactory tmf = TrustManagerFactory.getInstance(
                TrustManagerFactory.getDefaultAlgorithm());
        tmf.init(loadTrustStore());
        return tmf.getTrustManagers();
    }

    private static KeyStore loadTrustStore() throws Exception {
        try (InputStream is = getResourceStream(TRUSTSTORE_PATH)) {
            KeyStore ks = KeyStore.getInstance("JKS");
            ks.load(is, TRUSTSTORE_PASSWORD);
            return ks;
        }
    }

    private static InputStream getResourceStream(String path) throws FileNotFoundException {
        InputStream is = SSLClient.class.getClassLoader().getResourceAsStream(path);
        if (is == null) {
            throw new FileNotFoundException("未找到信任库文件: " + path);
        }
        return is;
    }

    private static void configureSocket(SSLSocket socket) {
        socket.setEnabledProtocols(ENABLED_PROTOCOLS);
        socket.setEnabledCipherSuites(ENABLED_CIPHERS);
        socket.setUseClientMode(true);
    }

    private static void communicateWithServer(SSLSocket socket) {
        try  {
            try (BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()))) {
                try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()))) {

                    sendRequest(writer, "你好服务端 from client");
                    String response = readResponse(reader);
                    handleResponse(response);

                }
            }
        } catch (SSLHandshakeException e) {
            handleError("SSL握手失败", e);
        } catch (IOException e) {
            handleError("通信异常", e);
        }
    }

    private static void sendRequest(BufferedWriter writer, String message) throws IOException {
        writer.write(message);
        writer.newLine();
        writer.newLine();  // 结束标记
        writer.flush();
        System.out.println("已发送请求: " + message);
    }

    private static String readResponse(BufferedReader reader) throws IOException {
        StringBuilder response = new StringBuilder();
        String line;
        while ((line = reader.readLine()) != null && !line.isEmpty()) {
            response.append(line).append("\n");
        }
        return response.toString().trim();
    }

    private static void handleResponse(String response) {
        if (!response.isEmpty()) {
            System.out.println("收到服务器响应:\n" + response);
        } else {
            System.out.println("收到空响应");
        }
    }

    private static void handleError(String message, Throwable t) {
        System.err.println(message + ": " + t.getMessage());
        if (t instanceof SSLHandshakeException) {
            System.err.println("可能原因: 证书验证失败或协议不匹配");
        }
        t.printStackTrace();
    }
}

五、关键配置解析

5.1 服务端关键配置项

配置项说明推荐值
keystore服务端证书存储文件server.jks
keyalg密钥算法RSA/EC
sslProtocol启用协议版本TLSv1.2
cipherSuites加密套件TLS_AES_256_GCM_SHA384

5.2 客户端信任库配置

// 自定义信任管理器(可选)
TrustManager[] trustManagers = new TrustManager[] {
    new X509TrustManager() {
        public void checkClientTrusted(X509Certificate[] chain, String authType) {}
        public void checkServerTrusted(X509Certificate[] chain, String authType) {
            // 自定义证书验证逻辑
            if (!chain[0].getSubjectDN().getName().contains("example.com")) {
                throw new CertificateException("Invalid server certificate");
            }
        }
        public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; }
    }
};

六、常见问题与解决方案

6.1 证书链不完整

  • 现象PKIX path building failed
  • 解决方案
    # 导出完整证书链
    keytool -importcert -trustcacerts -alias rootca \
      -file root.cer -keystore client_truststore.jks
    

6.2 主机名验证失败

  • 现象java.security.cert.CertificateException: No subject alternative names
  • 解决方案
    // 创建自定义主机名验证器
    SSLParameters params = new SSLParameters();
    params.setEndpointIdentificationAlgorithm("HTTPS");
    socket.setSSLParameters(params);
    

6.3 性能调优建议

  1. 会话票证:启用TLS会话恢复
    SSLContext.setDefault(SSLContext.getInstance("TLS"));
    
  2. OCSP装订:减少证书验证延迟
  3. HTTP/2支持:提升传输效率

七、扩展应用场景

7.1 Spring Boot配置HTTPS

# application.properties
server.port=8443
server.ssl.key-store=classpath:server.jks
server.ssl.key-store-password=123456
server.ssl.key-alias=server
server.ssl.enabled-protocols=TLSv1.2

7.2 Nginx代理配置

server {
    listen 443 ssl;
    ssl_protocols TLSv1.3 TLSv1.2;
    ssl_certificate /path/to/server.crt;
    ssl_certificate_key /path/to/server.key;
    
    location / {
        proxy_pass http://localhost:8080;
    }
}

通过本文,您已掌握Java实现TLS单向认证的核心技术。建议在生产环境中使用权威CA签发的证书,并定期更新密钥,以保障系统安全性。

相关文章:

  • k8s面试题总结(九)
  • CentOS7 安装Redis 6.2.6 详细教程
  • 安卓内存泄露之DMA-BUF异常增长:Android Studio镜像引起DMA内存泄露
  • Python----数据分析(Matplotlib三:绘图二:箱图,散点图,饼图,热力图,3D图)
  • c++新特性之 左右值 lambda 以及“for”
  • qt-C++笔记之ubuntu22.04源码安装Qt6.8.2
  • AF3 _correct_post_merged_feats函数解读
  • 解决VSCode鼠标光标指针消失
  • 分布式锁实现方案对比与最佳实践
  • 【计网】数据链路层
  • Glide图片加载优化全攻略:从缓存到性能调优
  • python官方文档阅读整理(一)
  • 2024最新版Java面试题及答案,【来自于各大厂】
  • 【ORACLE】char类型和sql优化器发生的“错误”反应
  • 【工具推荐】在线提取PDF、文档、图片、论文中的公式
  • 数字万用表的使用教程
  • 学习 Wireshark 分析 Android Netlog
  • 什么是SElinux?
  • MongoDB Chunks核心概念与机制
  • 【前端】HTML 备忘清单(超级详细!)
  • 姑苏网站制作/友情链接发布平台
  • 反钓鱼网站建设期/网址搜索引擎入口
  • mugeda做网站/优就业seo课程学多久
  • 呼伦贝尔网站建设维护/贵州seo技术培训
  • wordpress 过于肿肿/唐山百度seo公司
  • 江门网站制作培训/2023推广平台