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

调用OpenSSL的X509_verify_cert时出现无效证书验证上下文错误排查

问题描述

在TLS服务器中尝试使用CRL验证对等证书,但调用X509_verify_cert时出现“invalid certificate verification context”错误,相关代码如下:

OpenSSL_add_all_algorithms();
ERR_load_crypto_strings();
FILE *fp = fopen(crlPath.c_str(), "r");
crl = PEM_read_X509_CRL(fp, NULL, NULL, NULL);
store = X509_STORE_new();
X509_STORE_load_file(store, caCertPath.c_str());
X509_STORE_add_crl(store, crl);

const SSL_METHOD *method;
SSL_CTX *sslCtx;
method = TLS_server_method()); // 存在语法错误:多了一个右括号
sslCtx = SSL_CTX_new(method);

SSL_CTX_use_certificate_file(sslCtx, serverCertPath.c_str(), SSL_FILETYPE_PEM);
SSL_CTX_use_PrivateKey_file(sslCtx, serverKeyPath.c_str(), SSL_FILETYPE_PEM);

struct sockaddr_in cli;
len = sizeof(cli);
int conn_fd = accept(serv_fd, (struct sockaddr*)&cli, &len);

ssl = SSL_new(sslCtx);
SSL_set_fd(ssl, conn_fd);

SSL_accept(ssl);
X509 * peerCert = SSL_get1_peer_certificate(ssl);
storeCtx = X509_STORE_CTX_new();
X509_STORE_CTX_init(storeCtx, store, peerCert, NULL);
int res = 0;
if((res = X509_verify_cert(storeCtx)) <= 0)
{
  const char * charErr = X509_verify_cert_error_string(X509_STORE_CTX_get_error(storeCtx));
  printf("%s\n", charErr);
}
错误原因及解决方法

导致该错误的核心问题集中在验证流程配置缺失和代码语法错误上:

  • 未强制客户端提供证书:默认SSL_CTX不会要求客户端提交证书,SSL_get1_peer_certificate可能返回NULL,用空证书初始化X509_STORE_CTX会直接导致验证上下文无效。
  • 手动验证缺少关键参数:X509_STORE_CTX_init的第四个参数是对等证书的中间证书链,传NULL会导致无法构建完整的信任链,验证上下文不合法。
  • 未启用CRL检查标志:仅将CRL添加到X509_STORE不够,默认X509_STORE不会自动检查CRL,必须设置对应标志才能启用该验证逻辑。
  • 语法错误导致初始化失败:TLS_server_method()后多了一个右括号,会导致编译异常或运行时SSL_CTX初始化失败,后续验证流程自然出错。

修复步骤

  1. 修正语法错误
    移除多余的右括号:
method = TLS_server_method();
  1. 启用客户端证书强制验证
    在创建SSL_CTX后添加配置,让服务器要求客户端提供证书并自动验证:
// 强制客户端提供证书,无证书则握手失败
SSL_CTX_set_verify(sslCtx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
// 将自定义的X509_STORE关联到SSL_CTX,让SSL层自动处理验证
SSL_CTX_set_cert_store(sslCtx, store);
  1. 启用CRL检查
    在添加CRL到X509_STORE后,设置验证标志:
// 启用全证书链的CRL检查,也可使用X509_V_FLAG_CRL_CHECK仅检查终端证书
long flags = X509_V_FLAG_CRL_CHECK_ALL;
X509_STORE_set_flags(store, flags);
  1. 移除手动验证逻辑
    不需要手动调用X509_verify_cert,SSL_accept会自动完成证书验证(包括CRL检查)。若需自定义验证逻辑,可通过SSL_CTX_set_verify_callback设置回调函数。

修复后的核心代码片段:

OpenSSL_add_all_algorithms();
ERR_load_crypto_strings();
FILE *fp = fopen(crlPath.c_str(), "r");
crl = PEM_read_X509_CRL(fp, NULL, NULL, NULL);
fclose(fp); // 记得关闭文件句柄
store = X509_STORE_new();
X509_STORE_load_file(store, caCertPath.c_str());
X509_STORE_add_crl(store, crl);
// 启用CRL全链检查
X509_STORE_set_flags(store, X509_V_FLAG_CRL_CHECK_ALL);

const SSL_METHOD *method;
SSL_CTX *sslCtx;
method = TLS_server_method();
sslCtx = SSL_CTX_new(method);

// 配置服务器证书和密钥
SSL_CTX_use_certificate_file(sslCtx, serverCertPath.c_str(), SSL_FILETYPE_PEM);
SSL_CTX_use_PrivateKey_file(sslCtx, serverKeyPath.c_str(), SSL_FILETYPE_PEM);
// 启用客户端证书验证并关联自定义store
SSL_CTX_set_verify(sslCtx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
SSL_CTX_set_cert_store(sslCtx, store);

struct sockaddr_in cli;
socklen_t len = sizeof(cli);
int conn_fd = accept(serv_fd, (struct sockaddr*)&cli, &len);

ssl = SSL_new(sslCtx);
SSL_set_fd(ssl, conn_fd);

// SSL_accept自动完成证书验证,包括CRL检查
int acceptRes = SSL_accept(ssl);
if (acceptRes <= 0) {
    int err = SSL_get_error(ssl, acceptRes);
    printf("SSL握手失败: %s\n", ERR_error_string(err, NULL));
} else {
    printf("客户端证书验证通过\n");
    X509 * peerCert = SSL_get1_peer_certificate(ssl);
    // 处理对等证书逻辑...
    X509_free(peerCert); // 释放证书资源
}

// 清理资源
SSL_shutdown(ssl);
SSL_free(ssl);
close(conn_fd);
SSL_CTX_free(sslCtx);
X509_STORE_free(store);
X509_CRL_free(crl);
ERR_free_strings();

内容的提问来源于stack exchange,提问作者Thundercleez

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 01:43:17