当前位置: 首页 > 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.lryc.cn/news/578074.html

相关文章:

  • Redis初识第七期---ZSet的命令和应用场景
  • VRR(可变刷新率)和QMS(快速媒体切换)
  • 集群【运维】麒麟V10挂载本地yum源
  • OpenCV计算机视觉实战(14)——直方图均衡化
  • 【期末分布式】分布式的期末考试资料大题整理
  • UI前端大数据处理挑战与对策:保障数据安全与隐私
  • 【知识】RPC和gRPC
  • Reactor操作符的共享与复用
  • Excel数据匹配合并工具
  • Linux 系统管理:自动化运维与容器化部署
  • 2025年数字信号、计算机通信与软件工程国际会议(DSCCSE 2025)
  • postman接口测试全部流程
  • Git 简介安装教程
  • [附源码+数据库+毕业论文]基于Spring+MyBatis+MySQL+Maven+jsp实现的校园服务平台管理系统,推荐!
  • Fiddler中文版抓包工具如何帮助前端开发者高效调试
  • 我的第一个开源项目:用Python搭建轻量级静态网页服务器—— 零基础也能实现的Web开发初体验
  • 鸿蒙应用开发:ArkTS中接口的声明和使用
  • SQL优化(插入、主键、order by、group by)
  • 关于 java:8. Java 内存模型与 JVM 基础
  • ClickHouse 部署
  • RK3568平台开发系列讲解:WIFI的调试手段
  • 重构老项目不再“踩雷”:飞算JavaAI的本地化智能合并实战
  • 企业自建云概念解读|私有云、专有云、混合云、分布式云、企业云
  • Windows桌面上的「了解此图片」怎么弄掉?
  • Tailwind CSS 配置正确,也没有报错,但是样式没有生效(解决~)
  • 如何用废弃电脑变成服务器搭建web网站(公网访问零成本)
  • 成像光谱遥感技术中的AI革命:ChatGPT在遥感领域中的应用
  • 【MySQL基础】MySQL索引全面解析:从原理到实践
  • vscode ssh远程连接ubuntu20失败的解决方法
  • 第9篇:Gin配置管理-Viper的实战使用