利用 openssl api 实现 TLS 双向认证

1. 环境

  1. openssl1.1.1g

wget https://github.com/openssl/openssl/releases/download/OpenSSL_1_1_1g/openssl-1.1.1g.tar.gz
sha256 为: ddb04774f1e32f0c49751e21b67216ac87852ceb056b75209af2443400636d46

  1. Linux 环境

2. 静态编译 openssl

tar -zxvf openssl-1.1.1g.tar.gz
cd  openssl-1.1.1g

./config -fPIC no-shared
make -j 4

3. TLS 使用到的证书生成

#!/bin/bash

# 生成 CA
openssl genrsa -out ca.key 4096
openssl req -x509 -new -nodes -key ca.key -sha256 -days 3650 -out ca.crt \
  -subj "/C=CN/ST=Beijing/L=Beijing/O=MyOrg/CN=MyCA"

# 生成服务器证书
openssl genrsa -out server.key 4096
openssl req -new -key server.key -out server.csr \
  -subj "/C=CN/ST=Beijing/L=Beijing/O=MyOrg/CN=localhost"
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial \
  -out server.crt -days 365 -sha256

# 生成客户端证书
openssl genrsa -out client.key 4096
openssl req -new -key client.key -out client.csr \
  -subj "/C=CN/ST=Beijing/L=Beijing/O=MyOrg/CN=MyClient"
openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial \
  -out client.crt -days 365 -sha256

# 清理临时文件
rm -f server.csr client.csr ca.srl

echo "证书生成完成!"

3. Linux C 代码调用 openssl api

  1. server.c
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <openssl/ssl.h>
#include <openssl/err.h>

#define PORT 4433
#define CERT_FILE "server.crt"
#define KEY_FILE "server.key"
#define CA_FILE "ca.crt"

void init_openssl()
{
   
   
    SSL_load_error_strings();
    OpenSSL_add_ssl_algorithms();
}

void cleanup_openssl()
{
   
   
    EVP_cleanup();
}

SSL_CTX *create_context()
{
   
   
    const SSL_METHOD *method;
    SSL_CTX *ctx;

    method = TLS_server_method();
    ctx = SSL_CTX_new(method);
    if (!ctx)
    {
   
   
        perror("Unable to create SSL context");
        ERR_print_errors_fp(stderr);
        exit(EXIT_FAILURE);
    }

    return ctx;
}

void configure_context(SSL_CTX *ctx)
{
   
   
    /* 设置服务器证书 */
    if (SSL_CTX_use_certificate_file(ctx, CERT_FILE, SSL_FILETYPE_PEM) <= 0)
    {
   
   
        ERR_print_errors_fp(stderr);
        exit(EXIT_FAILURE);
    }

    /* 设置服务器私钥 */
    if (SSL_CTX_use_PrivateKey_file(ctx, KEY_FILE, SSL_FILETYPE_PEM) <= 0)
    {
   
   
        ERR_print_errors_fp(stderr);
        exit(EXIT_FAILURE);
    }

    /* 验证私钥是否匹配证书 */
    if (!SSL_CTX_check_private_key(ctx))
    {
   
   
        fprintf(stderr, "Private key does not match the public certificate\n");
        exit(EXIT_FAILURE);
    }

    /* 设置信任的CA证书 */
    if (SSL_CTX_load_verify_locations(ctx, CA_FILE, NULL) != 1)
    {
   
   
        ERR_print_errors_fp(std
<think>嗯,用户想了解如何使用Java实现TLS双向认证。首先,我得回忆一下TLS双向认证的基本概念。双向认证,也就是客户端和服务器端都要验证对方的证书,确保双方的身份都是可信的。这和单向认证不同,单向认证只需要客户端验证服务器的证书,而服务器不验证客户端的。 那Java里怎么实现这个呢?我记得Java的SSL/TLS实现主要是通过JSSE(Java Secure Socket Extension)来完成的。双向认证的话,需要在服务器和客户端都配置信任库和密钥库。信任库用来存放可信任的证书,而密钥库则存放自己的私钥和证书。 首先,可能需要生成证书。通常可以用keytool来生成自签名证书,或者用OpenSSL。不过用户可能更熟悉keytool,因为它是Java自带的工具。生成服务器端和客户端的密钥对和证书,然后导出证书,互相导入到对方的信任库中。 接下来,服务器端的代码需要设置SSLContext,指定密钥库和信任库的位置和密码。然后创建SSLServerSocket,设置需要客户端认证。比如,在服务器端代码中,使用System.setProperty来指定javax.net.ssl.keyStore和trustStore,这可能是个方法,或者更好的做法是在代码中通过SSLContext来配置,这样更灵活。 客户端同样需要配置SSLContext,指定自己的密钥库和信任库。然后创建SSLSocket连接到服务器,进行握手。这时候,双方都会验证对方的证书是否可信。 不过要注意,证书的生成是否正确,比如密钥库的类型,默认是JKS,但现在可能更多使用PKCS12。所以在生成的时候需要指定-storetype参数。另外,证书的CN(Common Name)或者SAN(Subject Alternative Name)可能需要正确设置,否则可能导致验证失败。 可能遇到的问题包括证书路径找不到,密钥库密码错误,或者证书没有正确导入到信任库中。这时候需要检查路径是否正确,密码是否正确,以及是否确实导入了对方的证书。 另外,示例代码可能需要处理异常,比如SSLHandshakeException,这时候需要查看异常信息,确定是证书的问题还是其他问题。调试的时候可以启用SSL调试参数,比如-Djavax.net.debug=all,这样可以更详细地查看握手过程。 用户可能还需要了解双向认证的应用场景,比如内部系统之间的安全通信,或者对安全性要求较高的API接口。同时,可能需要注意证书的有效期,定期更新证书,以及密钥库的安全存储问题。 总结一下,实现步骤大概是:生成服务器和客户端的密钥对和证书;配置双方的信任库和密钥库;编写服务器端和客户端代码,设置SSLContext,并启用客户端认证。然后测试连接,确保握手成功。过程中要注意证书的生成和导入是否正确,路径和密码的设置,以及可能的异常处理。</think>Java实现TLS双向认证需要以下核心步骤和代码实现(示例基于Java 11+): 一、证书准备(需提前生成) ```bash # 生成服务端密钥库 keytool -genkeypair -alias server -keyalg RSA -keysize 2048 \ -validity 365 -storetype PKCS12 -keystore server.p12 \ -dname "CN=server" -storepass 123456 # 生成客户端密钥库 keytool -genkeypair -alias client -keyalg RSA -keysize 2048 \ -validity 365 -storetype PKCS12 -keystore client.p12 \ -dname "CN=client" -storepass 123456 # 导出证书并互相导入信任库 keytool -exportcert -alias server -keystore server.p12 -file server.cer -storepass 123456 keytool -exportcert -alias client -keystore client.p12 -file client.cer -storepass 123456 keytool -importcert -alias client -file client.cer -keystore server_trust.p12 -storetype PKCS12 -storepass 123456 keytool -importcert -alias server -file server.cer -keystore client_trust.p12 -storetype PKCS12 -storepass 123456 ``` 二、服务端实现 ```java SSLServerSocketFactory factory = SSLContextBuilder .create() .setKeyStore(KeyStore.getInstance("PKCS12"), "123456", "server.p12") .setTrustStore(KeyStore.getInstance("PKCS12"), "123456", "server_trust.p12") .build() .getServerSocketFactory(); try (SSLServerSocket serverSocket = (SSLServerSocket) factory.createServerSocket(8443)) { serverSocket.setNeedClientAuth(true); // 关键设置:要求客户端认证 System.out.println("服务端已启动..."); while (true) { try (SSLSocket socket = (SSLSocket) serverSocket.accept()) { // 处理通信逻辑 } } } ``` 三、客户端实现 ```java SSLSocketFactory factory = SSLContextBuilder .create() .setKeyStore(KeyStore.getInstance("PKCS12"), "123456", "client.p12") .setTrustStore(KeyStore.getInstance("PKCS12"), "123456", "client_trust.p12") .build() .getSocketFactory(); try (SSLSocket socket = (SSLSocket) factory.createSocket("localhost", 8443)) { socket.startHandshake(); // 显式触发握手 System.out.println("成功建立双向认证连接"); // 处理通信逻辑 } ``` 四、关键配置说明 1. 双向认证核心设置: - 服务端:`serverSocket.setNeedClientAuth(true)` - 客户端:必须提供有效客户端证书 2. 证书验证流程: $$ 客户端验证 \rightarrow 服务端证书有效性 $$ $$ 服务端验证 \rightarrow 客户端证书有效性 $$ 五、常见问题排查 1. 证书链验证失败: - 检查信任库是否包含对方根证书 - 验证证书有效期:`keytool -list -v -keystore xxx.p12` 2. 调试模式启动: ```java System.setProperty("javax.net.debug", "ssl:handshake"); ``` 3. 协议版本控制: ```java socket.setEnabledProtocols(new String[]{"TLSv1.3"}); ``` 该实现适用于以下场景: - 金融交易系统 - 物联网设备安全通信 - 微服务间敏感数据传输 - 企业级API网关验证 建议配合使用: 1. 证书自动轮换机制 2. CRL/OCSP证书吊销检查 3. 密钥库硬件加密(HSM)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值