X.509证书解析
结构体及部分接口说明
1、x509结构
x509_st
cert_info:证书主体信息;
sig_alg:签名算法;
signature:签名值,存放CA对该证书签名的结果;
valid:是否是合法证书,1为合法,0为未知;
references:引用次数,被引用一次则加一;
name:证书持有者信息;
ex_data:扩展数据结构,用于存放用户自定义的信息;
ex_pathlen:证书路径长度;
ex_kusage:密钥用法;
ex_xkusage:扩展密钥用法;
ex_nscert:Netscape证书类型;
skid:主体密钥标识;
akid:颁发者密钥标识;
policy_cache:各种策略缓存;
sha1_hash:存放证书的sha1摘要值;
aux:辅助信息;
2、常用接口
1)DER编码()转换为内部结构X509
X509 *d2i_X509(X509 **cert, unsigned char **d, int len);
2)获取证书版本号
#define X509_get_version(x) ASN1_INTEGER_get((x)->cert_info->version)
3)获取颁发者信息
ASN1_INTEGER *X509_get_serialNumber(X509 *x);
4)获取序列号
ASN1_INTEGER *X509_get_serialNumber(X509 *x);
5)获取证书拥有者信息
X509_NAME *X509_get_subject_name(X509 *a);
6)获取起始日期和截至日期
#define X509_get_notBefore(x) ((x)->cert_info->validity->notBefore)
#define X509_get_notAfter(x) ((x)->cert_info->validity->notAfter)
7)获取证书公钥函数
EVP_PKEY *X509_get_pubkey(X509 *x);
8)验证证书
X509_verify_cert函数用于验证X.509证书的合法性。它执行以下验证步骤:
- 验证证书的签名:对于自签名证书,会使用证书本身作为公钥进行验证;对于由其他证书签发的证书,会使用签发者的公钥进行验证。
- 验证证书的有效期:检查证书的有效期限,包括起始时间(notBefore)和结束时间(notAfter)。
- 验证证书的主题和发行者:检查证书的主题(subject)和发行者(issuer),确保它们符合规范。
- 验证证书链:如果提供了CA证书链,会检查证书是否属于正确的证书链。
- 验证证书扩展:根据需要,可以验证证书的扩展字段,如密钥用法、扩展密钥用法等。
如果所有的验证步骤都通过,X509_verify_cert函数将返回1,表示证书是合法的。否则,返回值将是0或负数,表示验证失败,并可以通过调用X509_STORE_CTX_get_error函数获取详细的错误代码。
请注意,X509_verify_cert函数只执行基本的验证步骤。如果需要更复杂的验证,例如检查证书链中的中间证书、检查证书撤销列表(CRL)等,可能需要使用其他函数和工具来完成。
测试程序
cmakelist
project(parseCertTest C)
set(CMAKE_CXX_STANDARD)
aux_source_directory(. DIR_SRCS)
include_directories(../openssl)
link_directories(../third-Part/openssl/lib)
add_executable(parseCertTest ${DIR_SRCS})
#使用到的openssl里使用到的库需要包含,使用dlsym等需要链接dl库用于运行时动态获取
target_link_libraries(parseCertTest PRIVATE crypto pthread ssl dl)
测试程序主要是提取信息,验证证书,提取公钥,需要终端打印信息可自行修改。
//
// Created by xw on 2023/7/10.
//
#include <stdio.h>
#include <openssl/x509.h>
#include <openssl/pem.h>
#include "parseCertTest.h"
void printASN1Time(ASN1_TIME* Time)
{
//BIO是一种用于进行输入/输出操作的抽象类型。它提供了一种统一的接口,可用于在不同的数据源和目标之间进行读写操作。
BIO* bio = BIO_new(BIO_s_mem());
if (bio == NULL) {
printf("Failed to create BIO\n");
return;
}
if (ASN1_TIME_print(bio, Time) <= 0) {
printf("Failed to print ASN1_TIME\n");
BIO_free(bio);
return;
}
char buf[1024];
//从bio对象中读取结果
int len = BIO_read(bio, buf, sizeof(buf) - 1);
if (len <= 0) {
printf("Failed to read BIO\n");
BIO_free(bio);
return;
}
buf[len] = '\0';
printf("ASN1_TIME: %s\n", buf);
BIO_free(bio);
}
void print_public_key_bitstr(const X509 *cert) {
const ASN1_BIT_STRING *bitstr = X509_get0_pubkey_bitstr(cert);
if (bitstr == NULL) {
printf("Failed to get public key bit string\n");
return;
}
printf("Public Key Bit String: ");
for (int i = 0; i < bitstr->length; i++) {
//16进制直接输出
printf("%02X", bitstr->data[i]);
}
printf("\n");
}
void printPublicKey(const EVP_PKEY *pubkey)
{
// BIO *bio = BIO_new_fp(stdout, BIO_NOCLOSE);
BIO* bio = BIO_new(BIO_s_mem());
if (bio == NULL) {
printf("Failed to create BIO\n");
return;
}
if (EVP_PKEY_print_public(bio, pubkey, 0, NULL) != 1) {
printf("Failed to print public key\n");
}
char buf[1024];
int len = BIO_read(bio, buf, sizeof(buf) - 1);
if (len <= 0) {
printf("Failed to read BIO\n");
BIO_free(bio);
return;
}else{
buf[len] = '\0';
//公钥的算法名称、参数和具体的公钥值(证书显示中除开前面10字节的数据)
printf("ASN1_TIME: %s\n", buf);
}
BIO_free(bio);
}
int verifyCertificate(X509* cert)
{
EVP_PKEY *pubkey = X509_get_pubkey(cert);
printPublicKey(pubkey);
print_public_key_bitstr(cert);
//创建存储和管理证书的数据结构
X509_STORE* store = X509_STORE_new();
if (store == NULL) {
printf("Failed to create X509_STORE\n");
return 0;
}
//上下文对象 X509_STORE_CTX 证书 验证链 CRL等
X509_STORE_CTX* ctx = X509_STORE_CTX_new();
if (ctx == NULL) {
printf("Failed to create X509_STORE_CTX\n");
X509_STORE_free(store);
return 0;
}
//证书添加到X509_STORE对象中
if (X509_STORE_add_cert(store, cert) != 1) {
printf("Failed to add certificate to X509_STORE\n");
X509_STORE_CTX_free(ctx);
X509_STORE_free(store);
return 0;
}
//初始化ctx对象,传入证书和验证链
if (X509_STORE_CTX_init(ctx, store, cert, NULL) != 1) {
printf("Failed to initialize X509_STORE_CTX\n");
X509_STORE_CTX_free(ctx);
X509_STORE_free(store);
return 0;
}
//验证合法性
int result = X509_verify_cert(ctx);
if (result != 1) {
printf("Certificate verification failed\n");
X509_STORE_CTX_free(ctx);
X509_STORE_free(store);
return 0;
}
X509_STORE_CTX_free(ctx);
X509_STORE_free(store);
return 1;
}
int main()
{
int res = 0;
setbuf(stdout, NULL);
FILE *cert_file = fopen("cert.txt", "r");
X509 *cert = PEM_read_X509(cert_file, NULL, NULL, NULL);
fclose(cert_file);
//颁发者信息
X509_NAME *issuer = X509_get_issuer_name(cert);
//证书拥有者信息
X509_NAME *subject = X509_get_subject_name(cert);
ASN1_INTEGER *serial = X509_get_serialNumber(cert);
//起始时间和结束时间
ASN1_TIME *not_before = X509_get_notBefore(cert);
ASN1_TIME *not_after = X509_get_notAfter(cert);
printASN1Time(not_before);
printASN1Time(not_after);
//信息转换为可打印字符串
printf("Issuer: %s\n", X509_NAME_oneline(issuer, NULL, 0));
printf("Subject: %s\n", X509_NAME_oneline(subject, NULL, 0));
res = verifyCertificate(cert);
// 释放资源
X509_free(cert);
}