开发Nginx模块:如何在C代码中获取客户端证书并提取CN用于认证
在Nginx自定义C模块中获取客户端证书并提取CN的解决方案
首先得确认你的Nginx基础配置已经正确开启了客户端证书验证——这是模块能拿到证书的前提:
server { listen 443 ssl; ssl_certificate /path/to/server.crt; ssl_certificate_key /path/to/server.key; ssl_client_certificate /path/to/ca.crt; # 指定信任的CA证书 ssl_verify_client optional; # 根据业务需求选on/optional/optional_no_ca ssl_verify_depth 2; # 设置证书链的验证深度 # 你的其他站点配置... }
接下来是核心的C模块代码实现步骤:
1. 获取客户端证书指针
在你的模块处理函数(比如ngx_http_xxx_handler)里,通过请求结构体逐层拿到OpenSSL的X509证书对象:
#include <ngx_http.h> #include <openssl/x509.h> #include <openssl/x509v3.h> static ngx_int_t ngx_http_my_module_handler(ngx_http_request_t *r) { ngx_ssl_connection_t *ssl_conn; X509 *client_cert; // 先检查当前连接是否启用了SSL if (r->connection->ssl == NULL) { return ngx_http_internal_server_error(r); } ssl_conn = r->connection->ssl; client_cert = ssl_conn->peer_cert; // 检查客户端是否提供了有效证书 if (client_cert == NULL) { // 这里可以根据业务逻辑处理,比如返回403或者允许无证书请求 return ngx_http_forbidden(r); } // 下一步提取CN字段...
2. 从证书中提取通用名称(CN)
借助OpenSSL的API,从证书的主题名称中提取CN字段:
X509_NAME *subject_name; char cn_buf[256] = {0}; int cn_len; subject_name = X509_get_subject_name(client_cert); if (subject_name == NULL) { return ngx_http_internal_server_error(r); } // NID_commonName是OpenSSL预定义的通用名称标识符 cn_len = X509_NAME_get_text_by_NID(subject_name, NID_commonName, cn_buf, sizeof(cn_buf)-1); if (cn_len <= 0) { // 证书中没有CN字段,按业务需求处理错误 return ngx_http_forbidden(r); } // 现在cn_buf里就是证书的CN了,你可以用它做后续的认证逻辑 ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, "Client CN: %s", cn_buf); // 你的业务逻辑代码... return ngx_http_ok(r); }
3. 编译模块的注意事项
因为用到了OpenSSL的API,编译模块时必须确保链接OpenSSL库:
- 在模块的
config文件里,添加OpenSSL相关依赖:
ngx_addon_name=ngx_http_my_module HTTP_MODULES="$HTTP_MODULES ngx_http_my_module" NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_my_module.c" # 链接OpenSSL库 CORE_LIBS="$CORE_LIBS -lssl -lcrypto"
- 编译Nginx时要带上
--with-openssl参数(如果你的Nginx不是默认编译OpenSSL的话)。
为什么不推荐用响应头的方法?
你之前找到的add-header X-Client-Cert $ssl_client_certificate方法,本质是把证书内容返回给客户端,这不仅存在证书信息泄露的风险,而且对你的认证需求来说完全绕了弯路——直接在模块内处理证书,既能避免不必要的信息暴露,又能更高效地完成认证逻辑。
内容的提问来源于stack exchange,提问作者user1918858




