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

C++ Curl使用Let's Encrypt证书连接失败:CA证书验证异常

问题:Curl无法验证Let's Encrypt证书,报错Peer certificate cannot be authenticated with given CA certificates

我有一台装了Let's Encrypt SSL证书的服务器,当前生效的证书文件包括:cert.pem、privkey.pem、fullchain.pem、chain.pem。

我的Curl代码里的证书相关配置是这样的:

curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 1);
curl_easy_setopt(curl, CURLOPT_SSLCERTTYPE, "PEM");
curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, *sslctx);

对应的sslctx函数如下,现在纠结mypem应该填哪个PEM文件的内容:

static CURLcode sslctx(CURL *curl, void *sslctx, void *parm) {
    X509_STORE *store;
    X509 *cert = NULL;
    BIO *bio;
    const char *mypem = // 此处应填入哪个PEM文件内容?
    "-----BEGIN CERTIFICATE-----\n"
    "XXXXXX WHICH PEM FILE? XXXX\n"
    "-----END CERTIFICATE-----";
    bio = BIO_new_mem_buf(mypem, -1);
    PEM_read_bio_X509(bio, &cert, 0, NULL);
    if (cert == NULL) {
        cout << "Failed reading SSL-certificate" << endl;
        Exit(false);
    }
    store = SSL_CTX_get_cert_store((SSL_CTX *)sslctx);
    if (X509_STORE_add_cert(store, cert) == 0) {
        cout << "Error adding SSL-certificate" << endl;
        Exit(false);
    }
    X509_free(cert);
    BIO_free(bio);
    return CURLE_OK;
}

现在通过HTTPS连接服务器时,Curl返回错误:Peer certificate cannot be authenticated with given CA certificates。禁用CURLOPT_SSL_VERIFYPEER能成功请求,但有安全风险,不符合要求。

补充信息:
我的Apache配置里,/etc/letsencrypt/options-ssl-apache.conf包含基础安全配置,而启用的站点SSL配置/etc/apache2/sites-enabled/{site}-le-ssl.conf里指定了:

Include /etc/letsencrypt/options-ssl-apache.conf
SSLCertificateFile /etc/letsencrypt/live/{site}/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/{site}/privkey.pem

解决方案

嘿,这个问题其实很明确,我来给你拆解清楚:

1. 你需要填入的是**chain.pem**的内容

Let's Encrypt的这几个文件分工很清晰:

  • cert.pem:仅服务器自己的证书
  • privkey.pem:服务器的私钥(绝对不能暴露给客户端)
  • fullchain.pem:服务器证书 + 中间证书的完整链(服务器端用这个)
  • chain.pem:仅Let's Encrypt的中间证书(客户端验证时需要信任这个)

你的代码逻辑是往Curl的SSL上下文里添加可信任的CA证书,用来验证服务器的证书链。服务器发送的fullchain.pem里包含了中间证书,客户端需要信任这个中间证书(加上系统默认信任的根证书),才能完成整个证书链的验证。所以把chain.pem的内容填到mypem里就对了。

2. 更简单可靠的替代方案:直接指定CA证书文件

其实你完全不用自己写sslctx函数这么麻烦,Curl本身就支持直接指定CA证书文件的路径,用CURLOPT_CAINFO参数就行,代码更简洁也不容易出错:

// 直接指定chain.pem的绝对路径
curl_easy_setopt(curl, CURLOPT_CAINFO, "/etc/letsencrypt/live/{site}/chain.pem");
// 或者也可以用fullchain.pem,效果一样,因为它包含了中间证书
// curl_easy_setopt(curl, CURLOPT_CAINFO, "/etc/letsencrypt/live/{site}/fullchain.pem");

这样你甚至可以删掉CURLOPT_SSL_CTX_FUNCTION相关的配置,代码更清爽。

3. 为什么之前会报错?

如果之前你填的是cert.pem或者其他错误的文件,那相当于告诉Curl要信任服务器的证书本身,而不是用来验证它的CA证书。这样Curl无法构建有效的信任链,自然就会抛出Peer certificate cannot be authenticated的错误了。

另外提一句,现在大多数系统已经默认信任Let's Encrypt的根证书了,如果你不想指定本地的证书文件,也可以尝试直接依赖系统的CA存储,但为了兼容性(比如某些老系统),还是指定chain.pem更稳妥。


内容的提问来源于stack exchange,提问作者Thomas van Hesteren

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.11 09:16:23