SSL_connect报错SSL_ERROR_SSL,如何排查问题根源?
我编写了一个简单的套接字程序,尝试连接一个可通过FireFox/Chrome正常访问的HTTPS服务器,代码如下:
// Register the error strings for libcrypto & libssl SSL_load_error_strings(); // Register the available ciphers and digests SSL_library_init(); // load all algos OpenSSL_add_all_algorithms(); // create context (new context for each connection) ssl_ctx_ = SSL_CTX_new(SSLv23_client_method()); if(!ssl_ctx_) { throw std::runtime_error("Can't initialize ssl context"); } // create handle ssl_h_ = SSL_new(ssl_ctx_); if(!ssl_h_) { SSL_CTX_free(ssl_ctx_); throw std::runtime_error("Can't initialize new ssl handle"); } // sd_ is a valid socket connected to a host on port 443, // i.e. www.repubblica.it:443 // bind socket if (!SSL_set_fd(ssl_h_, sd_)) { SSL_free(ssl_h_); SSL_CTX_free(ssl_ctx_); throw std::runtime_error("Can't set sd to ssl handle"); } // set blocking // this api makes the socket blocking fd_block(sd_); // perform handshake const int r = SSL_connect(ssl_h_); // r is now -1 if(1 != r) { // find out the error const int err = SSL_get_error(ssl_h_, r); SSL_free(ssl_h_); SSL_CTX_free(ssl_ctx_); throw std::runtime_error(std::string("Can't perform ssl handshake, err code: ") + ssl_err_human(err)); }
调用SSL_connect后返回值为-1,抛出错误:Exception: Can't perform ssl handshake, err code: SSL_ERROR_SSL,该错误信息不够直观。运行环境为Ubuntu 22.04及默认libssl-dev包,请问后续该如何排查问题并修复?
1. 获取详细的OpenSSL错误栈
SSL_ERROR_SSL是通用错误码,无法定位具体问题。在错误处理逻辑中添加代码打印OpenSSL内部错误信息:
// 在获取err变量后添加 char err_buf[256]; ERR_error_string_n(ERR_get_error(), err_buf, sizeof(err_buf)); // 将详细错误信息加入抛出的异常 throw std::runtime_error(std::string("Can't perform ssl handshake, err code: ") + ssl_err_human(err) + ", detail: " + err_buf);
执行后会得到类似error:14094410:SSL routines:ssl3_read_bytes:sslv3 alert handshake failure的具体错误,直接指向问题核心。
2. 更新SSL上下文创建方法
Ubuntu 22.04默认libssl为3.0版本,SSLv23_client_method()已过时,且部分现代服务器禁用了旧版SSL/TLS协议。替换为兼容更高版本的方法:
// 替换原SSL_CTX_new调用 ssl_ctx_ = SSL_CTX_new(TLS_client_method());
TLS_client_method()会自动协商服务器支持的最高TLS版本(从TLS 1.0到TLS 1.3),适配绝大多数现代HTTPS服务器。
3. 添加服务器名称指示(SNI)配置
多数现代HTTPS服务器托管多个域名,需客户端发送SNI信息才能返回对应证书,这是握手失败的常见原因。在SSL_set_fd之后、SSL_connect之前添加:
const char* target_host = "www.repubblica.it"; // 替换为实际连接的域名 if (!SSL_set_tlsext_host_name(ssl_h_, target_host)) { SSL_free(ssl_h_); SSL_CTX_free(ssl_ctx_); throw std::runtime_error("Failed to set SNI host name"); }
4. 确认套接字连接有效性
即使标注sd_为有效套接字,仍需验证:
connect调用返回值为0,确认已成功建立TCP连接- 无本地防火墙或代理拦截443端口流量
- 用
telnet www.repubblica.it 443测试基础网络连通性,能建立连接则说明网络层面无问题
5. 排查证书信任问题
若服务器证书不在系统信任库中,也会导致握手失败。可临时关闭证书验证(仅用于测试,生产环境绝对禁止):
// 在SSL_CTX_new之后添加 SSL_CTX_set_verify(ssl_ctx_, SSL_VERIFY_NONE, NULL);
若测试通过,说明是证书信任问题,需将服务器根证书添加到系统信任库,或在代码中指定自定义信任证书文件。
内容的提问来源于stack exchange,提问作者Emanuele

