OCSP服务器证书吊销校验:OpenSSL C API选型与mTLS代码修改
双向TLS客户端添加OCSP证书吊销校验方案
问题描述
尝试通过OCSP校验服务器的吊销证书,但无法找到合适的OCSP C API实现。能否帮忙建议如何修改现有的双向TLS(mTLS)代码,以实现基于OCSP的服务器吊销证书校验?
实现方案
基于OpenSSL原生OCSP API,可在客户端完成SSL握手后,主动向CA的OCSP服务器查询服务器证书的吊销状态。以下是具体修改步骤和代码:
1. 添加必要头文件
在客户端代码顶部补充OCSP相关依赖头文件:
#include <openssl/ocsp.h> #include <openssl/x509v3.h>
2. 实现OCSP校验核心函数
添加独立函数负责提取服务器证书、构造OCSP请求、发送查询并验证结果:
static int verify_server_cert_via_ocsp(SSL *ssl) { OCSP_REQ *req = NULL; OCSP_RESP *resp = NULL; BIO *ocsp_bio = NULL; X509 *server_cert = NULL; X509 *issuer_cert = NULL; STACK_OF(X509) *chain = NULL; OCSP_CERTID *cid = NULL; int rc = -1; int resp_status; OCSP_SINGLERESP *single_resp = NULL; int cert_status; // 获取服务器证书 server_cert = SSL_get_peer_certificate(ssl); if (!server_cert) { fprintf(stderr, "无法获取服务器证书\n"); goto cleanup; } // 从证书链中提取颁发者CA证书 chain = SSL_get_peer_cert_chain(ssl); if (!chain || sk_X509_num(chain) < 1) { fprintf(stderr, "无法获取服务器证书链\n"); goto cleanup; } issuer_cert = sk_X509_value(chain, 0); // 构造OCSP查询所需的证书ID cid = OCSP_cert_to_id(EVP_sha1(), server_cert, issuer_cert); if (!cid) { fprintf(stderr, "无法构造OCSP证书ID\n"); goto cleanup; } // 创建OCSP请求并绑定证书ID req = OCSP_REQUEST_new(); if (!req || !OCSP_request_add0_id(req, cid)) { fprintf(stderr, "无法创建OCSP请求\n"); goto cleanup; } cid = NULL; // 所有权转移至请求对象,无需单独释放 // 从服务器证书AIA扩展中提取OCSP服务器地址 char *ocsp_url = NULL; STACK_OF(CONF_VALUE) *ai = X509_get_ext_d2i(server_cert, NID_info_access, NULL, NULL); if (ai) { CONF_VALUE *val; int i; for (i = 0; i < sk_CONF_VALUE_num(ai); i++) { val = sk_CONF_VALUE_value(ai, i); if (!strcmp(val->name, "OCSP") && val->value) { ocsp_url = val->value; break; } } sk_CONF_VALUE_pop_free(ai, CONF_VALUE_free); } if (!ocsp_url) { fprintf(stderr, "服务器证书未包含OCSP服务器地址\n"); goto cleanup; } // 建立与OCSP服务器的连接 ocsp_bio = BIO_new_connect(ocsp_url); if (!ocsp_bio || BIO_do_connect(ocsp_bio) <= 0) { fprintf(stderr, "无法连接到OCSP服务器: %s\n", ocsp_url); goto cleanup; } // 发送OCSP请求并接收响应 resp = OCSP_sendreq_bio(ocsp_bio, ocsp_url, req, 10, NULL); if (!resp) { fprintf(stderr, "OCSP请求发送失败\n"); goto cleanup; } // 验证OCSP响应全局状态 resp_status = OCSP_response_status(resp); if (resp_status != OCSP_RESPONSE_STATUS_SUCCESSFUL) { fprintf(stderr, "OCSP响应状态错误: %d\n", resp_status); goto cleanup; } // 提取单证书响应结果(仅处理单证书场景) single_resp = OCSP_response_get1_resp(resp); if (!single_resp) { fprintf(stderr, "无法解析OCSP响应\n"); goto cleanup; } // 验证响应有效性与证书ID匹配性 if (!OCSP_check_validity(single_resp, NULL, NULL, 0, 0)) { fprintf(stderr, "OCSP响应有效性验证失败\n"); goto cleanup; } if (OCSP_id_cmp(OCSP_SINGLERESP_get0_id(single_resp), OCSP_request_get0_id(req, 0)) != 0) { fprintf(stderr, "OCSP响应证书ID不匹配\n"); goto cleanup; } // 判断证书吊销状态 cert_status = OCSP_SINGLERESP_get0_status(single_resp); switch (cert_status) { case V_OCSP_CERTSTATUS_GOOD: printf("服务器证书未被吊销\n"); rc = 0; break; case V_OCSP_CERTSTATUS_REVOKED: fprintf(stderr, "服务器证书已被吊销\n"); rc = -1; break; case V_OCSP_CERTSTATUS_UNKNOWN: fprintf(stderr, "无法确定服务器证书状态\n"); rc = -1; break; default: fprintf(stderr, "未知OCSP证书状态: %d\n", cert_status); rc = -1; break; } cleanup: // 资源清理 if (req) OCSP_REQUEST_free(req); if (resp) OCSP_RESPONSE_free(resp); if (ocsp_bio) BIO_free_all(ocsp_bio); if (server_cert) X509_free(server_cert); if (cid) OCSP_CERTID_free(cid); if (single_resp) OCSP_SINGLERESP_free(single_resp); return rc; }
3. 修改客户端主逻辑
在client函数的SSL握手验证通过后,调用OCSP校验函数:
// 原代码:验证SSL握手结果 if (SSL_get_verify_result(ssl) != X509_V_OK) { fprintf(stderr, "握手验证失败\n"); goto fail2; } // 添加OCSP吊销校验逻辑 if (verify_server_cert_via_ocsp(ssl) != 0) { fprintf(stderr, "OCSP证书吊销校验失败\n"); goto fail2; }
4. 优化SSL上下文配置(可选)
将get_client_context中的过时SSL方法替换为安全的TLS方法:
// 替换原SSLv23_client_method() if (!(ctx = SSL_CTX_new(TLS_client_method()))) { fprintf(stderr, "无法创建客户端上下文\n"); return NULL; }
编译说明
编译时需链接OpenSSL的OCSP库,示例命令:
gcc client.c -o client -lssl -lcrypto -locsp
内容的提问来源于stack exchange,提问作者Dhruv Gupta
相关产品推荐
相关产品推荐

