当前位置: 首页 > news >正文

使用OpenSSL接口读取pem编码格式文件中的证书

一. 概述

        pem格式的全称是Private Enhance Mail(加强邮件文本格式),是一种常见的文本文件格式,用于存储和传输加密的数据,最初为了安全电子邮件而设计,后来被广泛应用在数字证书,SSL/TLS和秘钥管理。

        pem文件中,每一段文本前后都有清晰的边界标记,如下

-----BEGIN XXX-----
HKK091IJ23KJJQkjc2k
k0.......
-----END XXX-----

        解析pem文件的代码流程如下:
a. 读取pem文件中的标签获取不同的对象(证书,秘钥..)的BASE64编码的字符串
b. 使用BIO_new_mem_buf包装读取到的字符串作为一个BIO
c. 根据不同的对象(证书,秘钥)调用不同的SSL函数解析BIO,例如PEM_read_bio_X509,PEM_read_bio_PrivateKey

二. 代码

#include <iostream>
#include <fstream>
#include <vector>#include <unistd.h>
#include <string.h>#include <openssl/evp.h>
#include <openssl/ssl.h>
#include <openssl/asn1.h>
#include <openssl/x509.h>
#include <openssl/err.h>const char* CERTIFICATE_BEGIN = "-----BEGIN CERTIFICATE-----";
const char* CERTIFICATE_END = "-----END CERTIFICATE-----";const int CERTFILE_CONTENT_LINE_MAXLEN = 14000;   // 14000 byte limit
char cert_content_tmp_buf[CERTFILE_CONTENT_LINE_MAXLEN] = {0};  // store every certificate content in pem filevoid print_serial_number(X509 *cert) {ASN1_INTEGER *serial = X509_get_serialNumber(cert);if (!serial) {std::cout << "无法获取序列号" << std::endl;return;}// 将序列号转换为 BIGNUM(方便打印)BIGNUM *bn = ASN1_INTEGER_to_BN(serial, NULL);if (!bn) {std::cout << "序列号转换失败" << std::endl;return;}// 打印序列号(十进制)std::cout << "Serial Number (decimal): ";BN_print_fp(stdout, bn);std::cout << std::endl;// 打印序列号(十六进制)char *hex = BN_bn2hex(bn);std::cout << "Serial Number (hex): " << hex << std::endl;OPENSSL_free(hex);BN_free(bn);
}void extract_public_key(X509 *cert) {EVP_PKEY *pkey = X509_get_pubkey(cert);if (!pkey) {std::cout << "无法提取公钥" << std::endl;return;}// 打印公钥类型(如 RSA、ECC)int type = EVP_PKEY_id(pkey);std::cout << "Public Key Type: " << OBJ_nid2sn(type) << std::endl;// 如果是 RSA 公钥,可进一步提取模数(n)和指数(e)if (type == EVP_PKEY_RSA) {RSA *rsa = EVP_PKEY_get1_RSA(pkey);const BIGNUM *n, *e;RSA_get0_key(rsa, &n, &e, NULL);std::cout << "RSA Modulus (n): ";BN_print_fp(stdout, n);std::cout << "\nRSA Exponent (e): ";BN_print_fp(stdout, e);std::cout << std::endl;RSA_free(rsa);}EVP_PKEY_free(pkey);
}void print_validity(X509 *cert) {ASN1_TIME *not_before = X509_get_notBefore(cert);ASN1_TIME *not_after = X509_get_notAfter(cert);char* s_notBefore = (char*)(ASN1_STRING_data(not_before));char* s_notAfter = (char*)(ASN1_STRING_data(not_after));std::cout << "Valid From: " << s_notBefore;std::cout << "\nValid Until: " << s_notAfter;std::cout << std::endl;
}void print_subject(X509 *cert) {X509_NAME *subject = X509_get_subject_name(cert);if (!subject) {std::cout << "Error get subject from cert" << std::endl;} else {X509_NAME_print_ex_fp(stdout, subject, 0, XN_FLAG_ONELINE);std::cout << std::endl;}
}bool read_cert_from_file(const std::string& cert_file, std::vector<std::string>& cert_content_buf) {bool success = true;// file existif (access(cert_file.c_str(), F_OK)) {std::cout << "cert file [" << cert_file << "] not exists" << std::endl;success = false;} else {// loop read content between cert begin-end tagstd::ifstream fs(cert_file);std::string line;bool begin_find = false;int line_index = 1;  // indicate current read line numberint cert_index = 0;  // indicate which certificate in this pem fileint read_size = 0;while (std::getline(fs, line)) {if (!begin_find) {if (0 == line.compare(CERTIFICATE_BEGIN)) {if (line.length() > CERTFILE_CONTENT_LINE_MAXLEN) {std::cout << "data line " << line_index << " exceed max line length [" << CERTFILE_CONTENT_LINE_MAXLEN << "]" << std::endl;success = false;break;}memset(cert_content_tmp_buf, 0, CERTFILE_CONTENT_LINE_MAXLEN);memcpy(cert_content_tmp_buf + read_size, line.c_str(), line.length());  // append current line to cert file bufferread_size = 0;   // reset read sizeread_size += line.length();cert_content_tmp_buf[read_size] = '\n';read_size++;begin_find = true;}} else {memcpy(cert_content_tmp_buf + read_size, line.c_str(), line.length());  // append current line to cert file bufferread_size += line.length();if (0 == line.compare(CERTIFICATE_BEGIN)) {std::cout << "data line " << line_index << " has duplicated [" << CERTIFICATE_BEGIN << "]" << std::endl;success = false;break;} else{if (0 == line.compare(CERTIFICATE_END)) {// store current certificatestd::cout << "read " << (cert_index + 1) << " complete certificate content, size is " << read_size << std::endl;cert_content_buf.emplace_back(std::string(cert_content_tmp_buf, read_size));// increase cert indexcert_index++;// reset begin flagbegin_find = false;} else {cert_content_tmp_buf[read_size] = '\n';read_size++;}}}}fs.close();if (success) {std::cout << "total " << cert_index << " certificate in " << cert_file << std::endl;}}return success;
}void parse_certificate(const std::string& certificate) {X509* x509;  // store parsed x509 cert contentBIO* bio = BIO_new_mem_buf(certificate.c_str(), certificate.length());std::cout << "allocate BIO fro certificate buffer, size is " << certificate.length() << ",\n content is:\n " << certificate << std::endl;if (bio) {x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL);if (x509) {// cert serial number print_serial_number(x509);// before and after date X509_get_notBefore & X509_get_notAfterprint_validity(x509);// Subject X509_get_subject_nameprint_subject(x509);// Issuer X509_get_issuer_name// Public Key  X509_get_pubkeyextract_public_key(x509);// free x509X509_free(x509);} else {std::cout << "Read X509 certificate content from  BIO fail" << std::endl;ERR_print_errors_fp(stderr);}} else {std::cout << "allocate BIO fro certificate buffer fail" << std::endl;}
}int main(int argc, char** argv) {if (argc > 1) {std::string filePath = argv[1];std::vector<std::string> vec_cert;if (read_cert_from_file(std::string(filePath), vec_cert)) {for(auto cert : vec_cert) {std::cout << "##### parse certificate begin ######\n" << std::endl;parse_certificate(cert);std::cout << "\n##### parse certificate end ######" << std::endl;}}} else {std::cout << "usage: ./cert_read $PEM_FILE_NAME" << std::endl;}
}

三. 编译

CMakeLists.txt如下

cmake_minimum_required(VERSION 3.10)

project(certRead)

add_executable(${PROJECT_NAME} main.cpp)

target_link_libraries(${PROJECT_NAME} ssl crypto)

然后cmake + make编译

四. 运行结果

其中使用的证书是在下面这个在线网站上生成的:

SSL证书生成

http://www.lqws.cn/news/580699.html

相关文章:

  • Java外观模式实现方式与测试方法
  • 链表题解——设计链表【LeetCode】
  • 大模型-分布式推理简介
  • linux 操作docker的基本命令docker仓库
  • ubuntu下免sudo执行docker
  • 自动驾驶:特斯拉 Model Y全自动驾驶交付的技术原理
  • 笨方法学python -练习6
  • 设计模式精讲 Day 19:观察者模式(Observer Pattern)
  • 设计模式-观察者模式
  • Linux 系统管理:高效运维与性能优化
  • MAC电脑,在 Docker 中已经运行过一次 Oracle 镜像后,再次运行具体操作。
  • pytorch底层原理学习--计算图
  • 【WCF】通过AOP实现基于JWT的授权与鉴权的实践
  • LG 将正式终止手机相关服务,彻底告别手机市场
  • 云手机主要是指什么?
  • static线程安全
  • 10.【C语言学习笔记】指针(二)
  • 汉字田(第十五届蓝桥杯大赛软件赛国赛)
  • 生成式人工智能实战 | 变分自编码器(Variational Auto-Encoder, VAE)
  • zxing-cpp c++版本的编译
  • 【T2I】RB: REGION AND BOUNDARY AWARE ZERO-SHOT GROUNDED TEXT-TO-IMAGE GENERATION
  • RK3588高性能处理器核心技术解析
  • 从0开始学习计算机视觉--Day06--反向传播算法
  • chatshare最新激活码分享
  • OpenCV计算机视觉实战(14)——直方图均衡化
  • Windows环境下Docker容器化的安装与设置指南
  • MySQL DATETIME 类型时间精度陷阱:一次由毫秒引发的数据“消失”之谜
  • 计算机网络第一章——计算机网络体系结构
  • Pandas5(数据清洗1)——缺失值处理、数据去重/转换/替换、离散化/分箱、检测和过滤异常值
  • 【Kafka】docker 中配置带 Kerberos 认证的 Kafka 环境(全过程)