OpenSSL:如何用C语言实现CA对CSR的签名
C语言实现OpenSSL CA对CSR的签名步骤
下面是基于OpenSSL库实现CA签名CSR的完整流程,包含关键代码片段和注意事项:
1. 加载CA证书与私钥
首先从PEM文件中读取CA的证书和对应私钥,这是签名的核心凭证:
#include <openssl/x509.h> #include <openssl/pem.h> #include <openssl/err.h> #include <time.h> // 加载CA证书 X509* load_ca_cert(const char* cert_path) { FILE* fp = fopen(cert_path, "r"); if (!fp) return NULL; X509* ca_cert = PEM_read_X509(fp, NULL, NULL, NULL); fclose(fp); return ca_cert; } // 加载CA私钥 EVP_PKEY* load_ca_privkey(const char* key_path) { FILE* fp = fopen(key_path, "r"); if (!fp) return NULL; EVP_PKEY* ca_privkey = PEM_read_PrivateKey(fp, NULL, NULL, NULL); fclose(fp); return ca_privkey; }
2. 处理程序内的CSR对象
你已在程序中生成X509_REQ* csr对象,可直接使用。推荐先验证CSR签名合法性:
// 验证CSR签名 int verify_csr(X509_REQ* csr) { EVP_PKEY* csr_pubkey = X509_REQ_get_pubkey(csr); int ret = X509_REQ_verify(csr, csr_pubkey); EVP_PKEY_free(csr_pubkey); return ret == 1; // 返回1表示验证通过 }
3. 初始化待签名的证书结构
创建新X509证书对象,填充必要字段:
X509* create_signed_cert(X509_REQ* csr, X509* ca_cert, EVP_PKEY* ca_privkey) { X509* cert = X509_new(); if (!cert) return NULL; // 设置证书版本(X509_V3对应版本3) X509_set_version(cert, X509_V3); // 设置唯一序列号(示例用时间戳,生产环境建议用自增序列或加密随机数) ASN1_INTEGER_set(X509_get_serialNumber(cert), (long)time(NULL)); // 设置有效期:起始为当前时间,结束为1年后 X509_gmtime_adj(X509_get_notBefore(cert), 0); X509_gmtime_adj(X509_get_notAfter(cert), 365*24*3600L); // 设置颁发者(与CA证书一致) X509_set_issuer_name(cert, X509_get_subject_name(ca_cert)); // 设置主体(从CSR复制) X509_set_subject_name(cert, X509_REQ_get_subject_name(csr)); // 设置公钥(从CSR提取) EVP_PKEY* csr_pubkey = X509_REQ_get_pubkey(csr); if (!X509_set_pubkey(cert, csr_pubkey)) { EVP_PKEY_free(csr_pubkey); X509_free(cert); return NULL; } EVP_PKEY_free(csr_pubkey); // 添加基本约束扩展(标记为终端实体,非CA) X509V3_CTX ctx; X509V3_set_ctx_nodb(&ctx); X509V3_set_ctx(&ctx, ca_cert, cert, csr, NULL, 0); X509_add_ext(cert, X509V3_EXT_conf_nid(NULL, &ctx, NID_basic_constraints, "critical,CA:FALSE"), -1); // 用CA私钥签名证书,算法采用SHA256withRSA if (X509_sign(cert, ca_privkey, EVP_sha256()) <= 0) { X509_free(cert); return NULL; } return cert; }
4. 保存签名后的证书
将生成的证书写入PEM文件:
int save_cert(X509* cert, const char* output_path) { FILE* fp = fopen(output_path, "w"); if (!fp) return 0; int ret = PEM_write_X509(fp, cert); fclose(fp); return ret; }
5. 主流程与错误处理
整合上述函数,添加错误打印逻辑:
int main() { // 初始化OpenSSL错误处理 ERR_load_crypto_strings(); OpenSSL_add_all_algorithms(); // 加载CA凭证 X509* ca_cert = load_ca_cert("ca.crt"); EVP_PKEY* ca_privkey = load_ca_privkey("ca.key"); if (!ca_cert || !ca_privkey) { fprintf(stderr, "加载CA证书/私钥失败\n"); ERR_print_errors_fp(stderr); goto cleanup; } // 此处替换为你程序中生成的CSR对象 X509_REQ* csr = ...; // 验证CSR if (!verify_csr(csr)) { fprintf(stderr, "CSR签名验证失败\n"); goto cleanup; } // 签名证书 X509* signed_cert = create_signed_cert(csr, ca_cert, ca_privkey); if (!signed_cert) { fprintf(stderr, "签名CSR失败\n"); ERR_print_errors_fp(stderr); goto cleanup; } // 保存证书 if (!save_cert(signed_cert, "signed.crt")) { fprintf(stderr, "保存签名证书失败\n"); goto cleanup; } cleanup: // 释放所有OpenSSL资源 X509_free(ca_cert); EVP_PKEY_free(ca_privkey); X509_REQ_free(csr); X509_free(signed_cert); ERR_free_strings(); return 0; }
注意事项
- 序列号必须唯一,避免重复使用引发安全问题,生产环境建议使用自增序列或加密安全随机数。
- 证书有效期按需调整,避免设置过长。
- 可根据需求添加更多扩展项(如密钥用法、主题备用名称等)。
- 所有OpenSSL资源使用后必须调用对应free函数释放,防止内存泄漏。
- 编译时需链接OpenSSL库,例如:
gcc your_code.c -o sign_csr -lcrypto
内容的提问来源于stack exchange,提问作者Pepepotamo
相关产品推荐
相关产品推荐

