You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 13:02:30