记Https校验证书问题

博客围绕Java中SSLSocketFactory展开,介绍其信任管理组、协议、安全随机数等内容。重点阐述应对SSLHandshakeException的方法,主要是对X509证书管理器操作,还分别说明了Hutool - http和HttpClient针对该问题的处理步骤,如构造、配置等。
部署运行你感兴趣的模型镜像

SSLSocketFactory 描述

TrustManagers :信任管理组 一般使用 X509证书信任管理器

Protocol: 协议:参考 SSLSocketFactoryBuilder内部参数

SecureRandom:安全随机数,可以根据自定义生成的规则匹配

KeyManager:通过载入证书的方式来实现附带证书请求

TrustManager:信任证书管理器,用于管理证书内容(可以用于避开校验证书问题)

应对SSLHandshakeException

主要是针对Https网站时会校验证书,一般无强制必要,可以进行忽略处理。主要是对X509证书管理器的操作

针对X509证书操作:

针对返回证书的时候,返回为空即可认为忽略处理

X509TrustManager X0509TM = new X509TrustManager() {
   @Override  // 检查客户端证书
   public void checkClientTrusted(X509Certificate[] chain,String authType) throws CertificateException {}

   @Override // 检查服务端端证书
   public void checkServerTrusted(X509Certificate[] chain,String authType) throws CertificateException {}

   @Override  //返回受信任的X509证书数组
   public X509Certificate[] getAcceptedIssuers() {
      return null;
   }
};

Hutool-http:

主要是针对HttpRequest的SSLSocketFactory设置

1、SSLSocketFactory构造
//X0509TM 为上述参数
SSLSocketFactory build = SSLSocketFactoryBuilder.create()
               .setTrustManagers(X0509TM) //设置信任证书
               .setProtocol(SSLSocketFactoryBuilder.SSLv3)//设置协议,其实经测试写不写都可以
               .build();
2、设置入hutool工具类内
//url 为自定义请求
String responseText = HttpRequest.post(url)
      .setSSLSocketFactory(build)
      .execute().body();

HttpClient:

整体而言,我们需要如下几步:

1、创建一个 连接池Pool

2、分析为什么https会出现异常

3、分析PoolingHttpClientConnectionManager

1、SSLContent配置(生成SSLFactory)

扩展Socket并提供使用SSL或TLS协议的安全套接字

说白了是 SSLSocketFactory也是产物之一,可以参考其 getDefault()方法

public static SSLContext getSSLContext() {
    SSLContext sslContext = null;
    try {
    	//这里仅仅为了示例,使用了TLSv1.2
        sslContext = SSLContext.getInstance("TLSv1.2");
        // 忽略https校验 主要上述参数 X0509TM
        // 这里可以填入的参数 KeyManager[]、TrustManager[]、SecureRandom
        sslContext.init(null, X0509TM, null);
    } catch (Exception e) {
    	//通常为初始化失败,原因参考
        e.printStackTrace();
    }
    return sslContext;
}
2、主要针对HttpClient的构造(解决方案)

HttpClient协议获取

// 其中 getSSLContext()为上述方法
public static CloseableHttpClient getCloseableHttpsClients() {
    // 采用绕过验证的方式处理https请求
    SSLContext sslcontext = getSSLContext();
    // 设置协议http和https对应的处理socket链接工厂的对象
    Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory> create()
        .register("http", PlainConnectionSocketFactory.INSTANCE)
        .register("https", new SSLConnectionSocketFactory(sslcontext)).build();
    PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
    HttpClients.custom().setConnectionManager(connManager);
    // 创建自定义的httpsclient对象
    CloseableHttpClient client = HttpClients.custom().setConnectionManager(connManager).build();
    return client;
}
3、针对HttpClient连接池的描述
/**
*step1
**/  
进入 PoolingHttpClientConnectionManager.class 
/**
*step2
*可以显而易见发现初始化方法:getDefaultRegistry
*主要由如下方法构成:
**/   
RegistryBuilder.<ConnectionSocketFactory>create()
                .register("http", PlainConnectionSocketFactory.getSocketFactory())
                .register("https", SSLConnectionSocketFactory.getSocketFactory())
                .build();
//step3:
/**
*http并未引发标题问题,我们主要考虑https则,进入SSLConnectionSocketFactory.getSocketFactory()
* 又回到了SSLSocketFactory的这个套接字类
**/  
//虽然有5个实现类,但我就涉及使用的两个,如下:
//1、创建和初始化普通(未加密)套接字的默认工厂类,仅仅简单连接socket,应对http即可
PlainConnectionSocketFactory
//2、附带套接字加密与主机名称校验,我们需要修改的就是这个部分
SSLConnectionSocketFactory

//step4:
/**
*进入SSLConnectionSocketFactory,我们可以通过默认构造类来获取到两个信息
* 1、创建默认的SSLContext --> SSLContexts.createDefault()
*  点进去我们可以发现 sslContext.init这个方法
*  延伸:engineInit 可以基本确定为 SSLContextImpl实现类  
*  我们需要找什么呢,就是要往下挖:unable to find valid certification path to requested target
**/      
    
/**
*step5:
* 排查分析
**/
    

//排除部分1:    
//方法中这部分可以main测试,基本可以排除    
TrustManagerFactory var4 =  TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
var4.init((KeyStore)null);
var2 = var4.getTrustManagers(); 

//剩余部分2:
//由方法中得出,其实也不是在这个部分
this.trustManager = this.chooseTrustManager(var2);

/**
*step6:
* 运行时出错,其实也是的,主要是在工厂类开始建立连接的时候。
* 上面主要是证明不是在配置时出错,同时也说明了配置的地方
**/
SSLSocketFactory 创建连接时 startHandshake() 内部调用访问https连接时,会根据证书进行调用验证。通常会从java环境中根据keytool读取证书,若我们么有配置,则会出现以上异常。通常不是很强要求下,我们直接采取 上述 2中的方法忽略即可,否则继续往获取相关答案:
https://www.cnblogs.com/fron/p/https-20170111.html
    

关于自建证书与证书方面,我找到我看到的一个比较好的文章:https://blog.youkuaiyun.com/do_bset_yourself/article/details/78159697 属于都市桃源17年的文章。

您可能感兴趣的与本文相关的镜像

Llama Factory

Llama Factory

模型微调
LLama-Factory

LLaMA Factory 是一个简单易用且高效的大型语言模型(Large Language Model)训练与微调平台。通过 LLaMA Factory,可以在无需编写任何代码的前提下,在本地完成上百种预训练模型的微调

使用 **OpenSSL** 进行证书校验,是确保 SSL/TLS 通信安全的重要步骤。它包括验证证书的有效性、签名链、有效期、域名匹配、吊销状态等。 下面我将详尽介绍如何使用 OpenSSL 命令行工具和 C API 实现完整的证书校验流程。 --- ## ✅ 一、使用 OpenSSL 命令行校验证书 ### 场景:你有一个服务器证书 `server.crt`,想验证它是否可信。 ### 1. 查看证书内容(基本信息) ```bash openssl x509 -in server.crt -text -noout ``` > 输出包括: - Subject(主题) - Issuer(颁发者) - Validity(有效期) - Subject Alternative Name(SAN,支持的域名) - 公钥信息 --- ### 2. 验证证书信任链(需要 CA 根证书) 假设你的根证书为 `ca.crt`,你可以这样验证: ```bash openssl verify -CAfile ca.crt server.crt ``` ✅ 输出示例: ``` server.crt: OK ``` ❌ 失败示例: ``` error 20 at 0 depth lookup: unable to get local issuer certificate ``` 说明:找不到签发该证书的 CA。 --- ### 3. 使用完整信任链验证(含中间 CA) 如果服务器证书是由中间 CA 签发的,你需要提供完整的信任链: ```bash openssl verify -CAfile trust_chain.pem server.crt ``` 其中 `trust_chain.pem` 包含: ``` -----BEGIN CERTIFICATE----- <Root CA> -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- <Intermediate CA> -----END CERTIFICATE----- ``` 或者分开指定: ```bash openssl verify -untrusted intermediate.crt -CAfile root.crt server.crt ``` > `-untrusted`:指定中间证书(非信任锚,但用于构建链) > `-CAfile`:指定可信根证书 --- ### 4. 从远程服务器获取并校验证书 #### 获取服务器证书(如 api.example.com) ```bash echo | openssl s_client -connect api.example.com:443 -servername api.example.com 2>/dev/null | \ openssl x509 > server.crt ``` #### 自动验证(推荐方式) ```bash echo | openssl s_client -connect api.example.com:443 -servername api.example.com -CAfile /etc/ssl/certs/ca-certificates.crt ``` > 如果输出中有 `Verify return code: 0 (ok)`,表示验证成功。 常见错误码: | 错误码 | 含义 | |-------|------| | 0 | OK | | 2 | 无法获取根证书(CA not trusted) | | 10 | 证书已过期 | | 62 | 域名不匹配 | --- ### 5. 强制检查域名(SNI + CN/SAN) 使用 `-servername` 参数启用 SNI,并检查 SAN 是否匹配: ```bash openssl s_client -connect google.com:443 -servername google.com -showcerts ``` OpenSSL 会自动进行主机名检查(部分版本需手动实现)。 --- ## ✅ 二、使用 OpenSSL C API 编程实现证书校验 以下是一个完整的 C 示例程序,连接到 HTTPS 服务器并执行证书校验。 ### 📦 所需头文件与库 ```c #include <openssl/ssl.h> #include <openssl/err.h> #include <openssl/x509_vfy.h> #include <stdio.h> #include <string.h> ``` 链接时加上: ```bash -lssl -lcrypto ``` --- ### 🔐 完整代码示例:OpenSSL 证书校验客户端 ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <arpa/inet.h> #include <unistd.h> #include <openssl/ssl.h> #include <openssl/err.h> #include <openssl/x509_vfy.h> // 创建 TCP 连接 int create_socket(const char *host, int port) { int sock; struct sockaddr_in addr; sock = socket(AF_INET, SOCK_STREAM, 0); addr.sin_family = AF_INET; addr.sin_port = htons(port); addr.sin_addr.s_addr = inet_addr(host); // 实际应用中应使用 getaddrinfo if (connect(sock, (struct sockaddr*)&addr, sizeof(addr)) != 0) { perror("Connect failed"); close(sock); return -1; } return sock; } // 主函数 int main(int argc, char **argv) { const char *host = "api.example.com"; const char *ip = "93.184.216.34"; // example.com IP int port = 443; SSL_CTX *ctx; SSL *ssl; int sock; X509 *cert; long res; // 初始化 OpenSSL SSL_library_init(); OpenSSL_add_all_algorithms(); SSL_load_error_strings(); // 创建上下文 ctx = SSL_CTX_new(TLS_client_method()); if (!ctx) { fprintf(stderr, "Unable to create SSL context\n"); ERR_print_errors_fp(stderr); exit(1); } // 加载系统默认 CA 证书(Linux) if (SSL_CTX_load_verify_locations(ctx, "/etc/ssl/certs/ca-certificates.crt", NULL) != 1) { fprintf(stderr, "Can't load CA file\n"); // 可尝试其他路径: // "/etc/pki/tls/certs/ca-bundle.crt" (RHEL/CentOS) // 或使用 Mozilla bundle: https://curl.se/ca/cacert.pem } // 启用严格验证 SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL); SSL_CTX_set_verify_depth(ctx, 4); // 创建 TCP 连接 sock = create_socket(ip, port); if (sock == -1) exit(1); // 创建 SSL 对象 ssl = SSL_new(ctx); SSL_set_fd(ssl, sock); SSL_set_hostflags(ssl, X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT); SSL_set1_host(ssl, host); // 设置预期主机名(用于 SAN 检查) // 执行 TLS 握手 if (SSL_connect(ssl) <= 0) { fprintf(stderr, "SSL Connect failed\n"); ERR_print_errors_fp(stderr); goto cleanup; } printf("Connected via %s\n", SSL_get_cipher(ssl)); // 获取服务器证书 cert = SSL_get_peer_certificate(ssl); if (!cert) { fprintf(stderr, "No certificate received\n"); goto cleanup; } // 手动触发验证(可选,SSL_connect 已做) res = SSL_get_verify_result(ssl); if (res != X509_V_OK) { fprintf(stderr, "Certificate verification error: %ld (%s)\n", res, X509_verify_cert_error_string(res)); X509_free(cert); goto cleanup; } printf("✅ Certificate is valid and trusted.\n"); // 提取 Common Name 和 SAN char cn[256]; X509_NAME_oneline(X509_get_subject_name(cert), cn, sizeof(cn)); printf("Subject CN: %s\n", cn); // TODO: 可添加 SAN 检查逻辑 X509_free(cert); cleanup: SSL_shutdown(ssl); close(sock); SSL_free(ssl); SSL_CTX_free(ctx); return 0; } ``` --- ### 🔍 关键 API 解释 | 函数 | 作用 | |------|------| | `SSL_CTX_load_verify_locations()` | 加载信任的 CA 证书文件或目录 | | `SSL_CTX_set_verify()` | 设置验证模式(`SSL_VERIFY_PEER` 表示必须验证) | | `SSL_set1_host()` | 设置期望的主机名,用于自动检查 SAN/CN | | `SSL_get_verify_result()` | 获取证书验证结果(0=OK) | | `X509_verify_cert_error_string()` | 将错误码转为字符串 | --- ## ✅ 三、高级校验功能 ### 1. 吊销检查(CRL) OpenSSL 默认 **不启用 CRL 或 OCSP**,需要手动实现。 #### 使用 CRL 文件校验: ```c // 加载 CRL X509_STORE *store = SSL_CTX_get_cert_store(ctx); X509_CRL *crl = load_crl("crl.pem"); X509_STORE_add_crl(store, crl); // 启用 CRL 检查 X509_STORE_set_flags(store, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL); ``` --- ### 2. OCSP 在线状态检查(略复杂) 需发送 OCSP 请求到 OCSP 响应器,解析响应判断是否被吊销。 可用命令行测试: ```bash openssl ocsp -issuer ca.crt -cert server.crt -url http://ocsp.example.com -resp_text ``` --- ### 3. 主机名验证(防止中间人攻击) 即使证书由合法 CA 签发,也必须确认其 `SAN` 或 `CN` 匹配目标域名。 使用: ```c SSL_set1_host(ssl, "api.example.com"); // 自动检查 SAN ``` 或手动提取 SAN 并比较。 --- ## ✅ 四、常见错误及解决方法 | 错误信息 | 原因 | 解决方案 | |--------|------|-----------| | `unable to get local issuer certificate` | 缺少中间 CA 或根 CA | 添加完整信任链 | | `certificate has expired` | 证书过期 | 更新证书或同步时间 | | `self-signed certificate` | 自签名证书未受信 | 手动导入证书 | | `hostname does not match` | SAN 不包含访问域名 | 使用正确域名或更新证书 | | `system lib` (`x509 lookup failure`) | CA bundle 路径错误 | 指定正确的 `-CAfile` | --- ## ✅ 五、最佳实践建议 1. ✅ 总是验证证书(不要禁用验证!) 2. ✅ 使用 `SSL_set1_host()` 启用主机名检查 3. ✅ 加载完整的信任链(包括中间 CA) 4. ✅ 定期更新 CA bundle(如 [Mozilla CA List](https://curl.se/docs/caextract.html)) 5. ✅ 在嵌入式设备上预置最小化 CA 列表 6. ✅ 证书错误日志用于调试 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值