调用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初始化失败,后续验证流程自然出错。
修复步骤
- 修正语法错误
移除多余的右括号:
method = TLS_server_method();
- 启用客户端证书强制验证
在创建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);
- 启用CRL检查
在添加CRL到X509_STORE后,设置验证标志:
// 启用全证书链的CRL检查,也可使用X509_V_FLAG_CRL_CHECK仅检查终端证书 long flags = X509_V_FLAG_CRL_CHECK_ALL; X509_STORE_set_flags(store, flags);
- 移除手动验证逻辑
不需要手动调用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
相关产品推荐
相关产品推荐

