cpprestsdk WebSocket客户端配置:解决TLS Handshake failed错误并实现自签名证书与CA签发证书双重验证
嘿,我之前在使用cpprestsdk开发WebSocket客户端时,也遇到过一模一样的WSS证书验证问题!刚好可以给你梳理下解决方案,既能开启证书验证保证安全,又能兼容CA签发和自签名的证书。
首先得搞清楚为什么开启validate_certificates=true会握手失败:cpprestsdk默认只会信任系统预装的根CA证书,你的自签名证书不在这个信任列表里,所以TLS握手时客户端会拒绝服务器的证书;而关闭验证相当于跳过了证书合法性检查,自然能连接,但这会带来中间人攻击的风险,绝对不能在生产环境这么用。
下面分场景给你具体的配置方法,两种情况可以结合起来,让客户端同时支持两种证书:
1. 信任CA签发的证书(包括自定义企业CA)
如果服务器用的是正规CA或内部CA签发的证书,你只需要把CA的根证书文件(.pem格式)加载到客户端的SSL信任列表里就行:
#include <cpprest/ws_client.h> #include <boost/asio/ssl.hpp> int main() { web::websockets::client::websocket_client_config config; config.set_validate_certificates(true); // 必须开启证书验证 // 配置SSL上下文,加载CA根证书 config.set_ssl_context_callback([](boost::asio::ssl::context& ctx) { try { // 加载CA根证书文件,替换成你的证书实际路径 ctx.load_verify_file("/etc/ssl/certs/your-ca-root.pem"); // 保持默认的验证模式:验证peer证书,且没有peer证书时直接失败 ctx.set_verify_mode(boost::asio::ssl::verify_peer | boost::asio::ssl::verify_fail_if_no_peer_cert); } catch (const std::exception& e) { std::cerr << "SSL上下文配置失败: " << e.what() << std::endl; } }); // 创建客户端并连接 web::websockets::client::websocket_client client(config); web::uri uri(U("wss://your-server-url")); try { client.connect(uri).wait(); std::cout << "连接成功!" << std::endl; // 后续的WebSocket消息收发操作... } catch (const std::exception& e) { std::cerr << "连接失败: " << e.what() << std::endl; } return 0; }
这样客户端就会信任该CA签发的所有合法证书,包括你的服务器证书。
2. 信任特定的自签名证书
如果服务器用的是自签名证书,你有两种安全的处理方式:
- 直接把自签名证书加载到信任列表(和加载CA证书的方法一致,只是换成自签名的.pem文件)
- 自定义验证回调,检查证书的SHA256指纹等唯一标识(更安全,避免信任其他未知的自签名证书)
比如用指纹验证的方式:
config.set_ssl_context_callback([](boost::asio::ssl::context& ctx) { try { ctx.set_verify_mode(boost::asio::ssl::verify_peer | boost::asio::ssl::verify_fail_if_no_peer_cert); // 自定义验证回调,检查证书SHA256指纹 ctx.set_verify_callback([](bool preverified, boost::asio::ssl::verify_context& ctx) { // 获取当前正在验证的证书 X509* cert = X509_STORE_CTX_get_current_cert(ctx.native_handle()); if (!cert) { return false; } // 计算证书的SHA256指纹 unsigned char md[EVP_MAX_MD_SIZE]; unsigned int md_len; X509_digest(cert, EVP_sha256(), md, &md_len); std::string fingerprint; for (unsigned int i = 0; i < md_len; ++i) { char buf[3]; sprintf(buf, "%02x", md[i]); fingerprint += buf; if (i != md_len - 1) { fingerprint += ":"; } } // 替换成你的自签名证书的实际SHA256指纹 const std::string expected_fingerprint = "AA:BB:CC:DD:EE:FF:00:11:22:33:44:55:66:77:88:99:AA:BB:CC:DD:EE:FF:00:11:22:33:44:55:66:77:88:99"; if (fingerprint == expected_fingerprint) { return true; } // 如果指纹不匹配,返回默认的验证结果(会失败) return preverified; }); } catch (const std::exception& e) { std::cerr << "SSL上下文配置失败: " << e.what() << std::endl; } });
3. 同时支持两种证书
如果你需要客户端既能信任CA签发的证书,又能信任特定的自签名证书,可以把两种方式结合:先加载CA根证书,然后在自定义回调里额外允许指定的自签名证书指纹。
关键注意事项
- 证书文件必须是PEM格式,如果是DER格式,需要先转换成PEM(可以用
openssl x509 -inform der -in cert.cer -out cert.pem命令转换)。 - 确保服务器证书的域名和你连接的URI域名完全一致,否则会触发域名不匹配的验证失败。
- 生产环境绝对不要关闭
validate_certificates,否则会暴露在中间人攻击的风险下。
内容的提问来源于stack exchange,提问作者Shyamala Devi
相关产品推荐
相关产品推荐

