OpenSSL本地CA存储证书验证失败:C代码实现求助
问题描述
我希望理解CA存储(CA store)的概念,并在不影响系统全局的前提下测试自定义证书链。我在本地主目录下创建了ca_store目录,其中包含根CA证书root_ca.crt与中间CA证书intermediate_ca.crt:
$ ls /home/my/ca_store root_ca.crt intermediate_ca.crt
我同时拥有一份服务器证书server.crt,使用OpenSSL命令可成功验证该证书,但使用以下C代码基于CA存储概念实现验证时却失败了:
SSL_library_init(); OpenSSL_add_all_algorithms(); SSL_CTX* ctx = SSL_CTX_new(TLS_server_method()); SSL_CTX_load_verify_locations(ctx, NULL, "/home/my/ca_store"); BIO* bio = BIO_new_file("server.crt", "rb"); X509* cert = X509_new(); PEM_read_bio_X509(bio, &cert, NULL, NULL); X509_STORE_CTX* store_ctx = X509_STORE_CTX_new(); X509_STORE_CTX_init(store_ctx, SSL_CTX_get_cert_store(ctx), cert, NULL); int ret = X509_verify_cert(store_ctx); if (ret != 1) { int error = X509_STORE_CTX_get_error(store_ctx); const char *error_str = X509_verify_cert_error_string(error); printf("Certificate verification failed. Error code: %d, Error message: %s\n", error, error_str); ERR_print_errors_fp(stdout); } else { printf("Certificate verification successful\n"); }
代码执行输出如下:
Certificate verification failed. Error code: 20, Error message: unable to get local issuer certificate
请问如何修改上述代码以实现正确的证书验证?
解决方案
问题根源
错误的核心原因有两个:
- OpenSSL通过目录加载CA证书时,要求证书文件名必须符合哈希命名规则(即证书主题哈希值加
.0/.1后缀),否则无法自动检索到对应的签发者证书 - 服务器证书由中间CA签发时,验证过程需要明确将中间CA纳入信任链,原代码仅加载目录但未确保中间CA被加入验证上下文
修改方案
方案1:手动加载目录内所有CA证书(无需修改证书文件名)
直接读取目录下的所有PEM格式证书,手动添加到X509_STORE中,避免依赖哈希文件名规则:
#include <stdio.h> #include <openssl/ssl.h> #include <openssl/x509.h> #include <openssl/bio.h> #include <dirent.h> #include <string.h> // 辅助函数:批量加载目录内的CA证书到X509_STORE int load_certs_from_dir(X509_STORE *store, const char *dir_path) { DIR *dir = opendir(dir_path); if (!dir) { perror("Failed to open CA directory"); return 0; } struct dirent *entry; while ((entry = readdir(dir)) != NULL) { // 跳过目录本身和上级目录,只处理普通文件 if (entry->d_type != DT_REG || strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) { continue; } char file_path[256]; snprintf(file_path, sizeof(file_path), "%s/%s", dir_path, entry->d_name); BIO *bio = BIO_new_file(file_path, "rb"); if (!bio) { fprintf(stderr, "Failed to open cert file: %s\n", file_path); continue; } X509 *cert = PEM_read_bio_X509(bio, NULL, NULL, NULL); if (cert) { if (!X509_STORE_add_cert(store, cert)) { fprintf(stderr, "Failed to add cert %s to store\n", entry->d_name); } X509_free(cert); } else { fprintf(stderr, "Failed to parse cert %s\n", entry->d_name); } BIO_free(bio); } closedir(dir); return 1; } int main() { SSL_library_init(); OpenSSL_add_all_algorithms(); SSL_CTX* ctx = SSL_CTX_new(TLS_server_method()); if (!ctx) { fprintf(stderr, "Failed to create SSL_CTX\n"); return 1; } // 获取SSL上下文的CA存储,手动加载目录内证书 X509_STORE *store = SSL_CTX_get_cert_store(ctx); if (!load_certs_from_dir(store, "/home/my/ca_store")) { fprintf(stderr, "Failed to load CA certificates\n"); SSL_CTX_free(ctx); return 1; } // 读取服务器证书 BIO* bio = BIO_new_file("server.crt", "rb"); if (!bio) { perror("Failed to open server.crt"); SSL_CTX_free(ctx); return 1; } X509* cert = PEM_read_bio_X509(bio, NULL, NULL, NULL); BIO_free(bio); if (!cert) { fprintf(stderr, "Failed to parse server.crt\n"); SSL_CTX_free(ctx); return 1; } // 初始化验证上下文 X509_STORE_CTX* store_ctx = X509_STORE_CTX_new(); if (!X509_STORE_CTX_init(store_ctx, store, cert, NULL)) { fprintf(stderr, "Failed to initialize verification context\n"); X509_free(cert); SSL_CTX_free(ctx); X509_STORE_CTX_free(store_ctx); return 1; } // 执行证书验证 int ret = X509_verify_cert(store_ctx); if (ret != 1) { int error = X509_STORE_CTX_get_error(store_ctx); const char *error_str = X509_verify_cert_error_string(error); printf("Certificate verification failed. Error code: %d, Error message: %s\n", error, error_str); ERR_print_errors_fp(stdout); } else { printf("Certificate verification successful\n"); } // 释放资源 X509_STORE_CTX_free(store_ctx); X509_free(cert); SSL_CTX_free(ctx); return 0; }
方案2:给CA证书生成哈希文件名(无需修改代码)
使用OpenSSL命令生成符合要求的哈希链接,让原代码能自动找到签发者证书:
cd /home/my/ca_store # 生成根CA的哈希链接 openssl x509 -hash -noout -in root_ca.crt ln -s root_ca.crt $(openssl x509 -hash -noout -in root_ca.crt).0 # 生成中间CA的哈希链接 openssl x509 -hash -noout -in intermediate_ca.crt ln -s intermediate_ca.crt $(openssl x509 -hash -noout -in intermediate_ca.crt).0
执行完上述命令后,原代码即可正常完成证书验证。
CA存储概念补充
CA存储是OpenSSL中用于管理信任根的核心容器(对应X509_STORE结构体),它存储了所有被系统/应用信任的CA证书。证书验证时,OpenSSL会从待验证证书的签发者字段出发,在CA存储中逐层查找匹配的证书,直到回溯到已信任的根CA,以此确认证书的合法性。
内容的提问来源于stack exchange,提问作者user180574
相关产品推荐
相关产品推荐

