Linux下cpprestsdk双向TLS配置遇SSL握手错误求助
我在Linux环境下使用cpprestsdk实现双向TLS,服务器部署在localhost。已生成自签名证书,通过以下OpenSSL命令测试正常:
openssl s_server -accept 4433 -cert server.crt -key server.key -CAfile ca.crt -Verify 1 openssl s_client -connect localhost:4433 -cert client1.crt -key client1.key -CAfile ca.crt -debug
但使用cpprestsdk发起请求时出现Error in SSL handshake错误,以下是相关配置,请求排查问题:
证书生成命令
# CA证书 openssl ecparam -name prime256v1 -genkey -noout -out ca.key openssl req -new -x509 -sha256 -key ca.key -out ca.crt # 服务器私钥 openssl ecparam -name prime256v1 -genkey -noout -out server.key # 服务器签名请求 openssl req -new -sha256 -key server.key -out server.csr -addext "subjectAltName=DNS:localhost,IP:0.0.0.0,IP:127.0.0.0" # 服务器证书 openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 1000 -sha256 -extfile server-extensions.txt # 客户端私钥 openssl ecparam -name prime256v1 -genkey -noout -out client1.key # 客户端签名请求 openssl req -new -sha256 -key client1.key -out client1.csr # 客户端证书 openssl x509 -req -in client1.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client1.crt -days 1000 -sha256 -extfile client-extensions.txt
cpprest客户端配置
web::http::client::http_client_config config; config.set_validate_certificates(true); config.set_ssl_context_callback([path = settings.certificate_directory.value()](boost::asio::ssl::context& ctx) { ctx.set_password_callback([](std::size_t max_length, boost::asio::ssl::context::password_purpose purpose) { return "0000"; }); ctx.set_options(boost::asio::ssl::context::default_workarounds | boost::asio::ssl::context::tlsv13); ctx.use_certificate_file(path / "client1.crt", boost::asio::ssl::context::pem); ctx.use_private_key_file(path / "client1.key", boost::asio::ssl::context::pem); ctx.load_verify_file(path / "ca.crt"); ctx.set_verify_mode(boost::asio::ssl::verify_peer); });
cpprest服务器配置
web::http::experimental::listener::http_listener_config config; config.set_ssl_context_callback([path = m_settings.certificates_path.value()](boost::asio::ssl::context& ctx) { ctx.set_password_callback([](std::size_t max_length, boost::asio::ssl::context::password_purpose purpose) { return "0000"; }); ctx.set_options(boost::asio::ssl::context::default_workarounds | boost::asio::ssl::context::tlsv13); ctx.use_certificate_file(path / "server.crt", boost::asio::ssl::context::pem); ctx.use_private_key_file(path / "server.key", boost::asio::ssl::context::pem); ctx.load_verify_file(path / "ca.crt"); ctx.set_verify_mode(boost::asio::ssl::verify_peer | boost::asio::ssl::verify_fail_if_no_peer_cert });
客户端请求代码
// 类成员: std::unique_ptr<web::http::client::http_client> m_client // 简化后的请求代码 void synchronous_request(const std::string& uri, web::http::method method, std::string body) { auto msg = web::http::http_request(method); msg.set_request_uri(uri); msg.set_body(std::move(body)); return m_client->request(std::move(msg), pplx::cancellation_token::none()).get(); }
证书扩展文件
server-extensions.txt
extendedKeyUsage=serverAuth
client-extensions.txt
extendedKeyUsage=clientAuth
排查要点
服务器配置语法错误
服务器SSL上下文配置的最后一行ctx.set_verify_mode缺少闭合括号和分号,正确写法应为:ctx.set_verify_mode(boost::asio::ssl::verify_peer | boost::asio::ssl::verify_fail_if_no_peer_cert);语法错误会导致SSL上下文初始化失败,直接引发握手错误。
服务器证书SubjectAltName错误
生成服务器CSR时指定的IP:127.0.0.0是网段地址,而非客户端实际连接的127.0.0.1。客户端验证服务器证书时,会检查SAN是否包含实际连接的IP/域名,不匹配会触发握手失败。重新生成服务器CSR,将SAN改为:-addext "subjectAltName=DNS:localhost,IP:0.0.0.0,IP:127.0.0.1"TLS版本兼容性问题
当前配置仅启用了TLS 1.3,若cpprestsdk依赖的Boost.Asio或OpenSSL版本对TLS 1.3支持不完善,可能导致握手协商失败。建议添加兼容版本,修改为:ctx.set_options(boost::asio::ssl::context::default_workarounds | boost::asio::ssl::context::tlsv12 | boost::asio::ssl::context::tlsv13);证书路径与权限问题
Linux环境下需确保程序能读取证书文件,检查证书路径是否正确(避免Windows风格的C:\路径),同时确保文件权限为可读(如chmod 644 *.crt *.key)。密钥密码回调冗余
若生成密钥时未设置密码,密码回调函数无需添加,多余的回调可能干扰SSL上下文初始化。可移除ctx.set_password_callback相关代码,或确认密钥确实设置了密码"0000"。客户端验证模式补充
客户端可显式设置验证深度,确保能正确验证服务器证书链:ctx.set_verify_depth(1);
内容的提问来源于stack exchange,提问作者Aedoro

