openssl 学习之证书中提取N 和 E

本文介绍了一种从PEM和DER格式的数字证书中提取公钥N和E的方法,并提供了完整的C语言代码示例。通过使用OpenSSL库中的函数,可以方便地解析这些证书。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原文地址:https://blog.youkuaiyun.com/kkxgx/article/details/19850509

通常数字证书包含很多信息,其中N和E值即我们称为的公钥。如何从PEM 或者DER格式的证书中提出证书呢?下面给出代码实现从PEM和DER编码的证书中提出N、E。

#include <openssl/evp.h>
#include <openssl/x509.h>
#include <stdio.h>
#include <stdlib.h>
void PrintHex(unsigned char *str, unsigned int len)
{
    int i = 0;
    for(i =0;i< len; i++)
    {
        if(i%4 == 0)
	{
	   printf("0x");
	}
        printf("%02x",str[i]);
	if(i%4 == 3)
        {
           printf(" ");
        }
        if(i%16 == 15)
        {
	    printf("\n");
        }
    }
    printf("\n");
}
void GetPukfromPEM()
{
    X509 *x;
    BIO	*b;
    EVP_PKEY *k;
    RSA *rsa;
    unsigned char n[300] ={0x0};
    unsigned char e[300] ={0x0};
    unsigned int len;
    /*www.google.com1为PEM格式的数字证书,从firefox中导出 */ 	
    b=BIO_new_file("www.google.com1","r");
    x=PEM_read_bio_X509(b,NULL,NULL,NULL);
    k=X509_get_pubkey(x);
    rsa=EVP_PKEY_get1_RSA(k);
    if(rsa->n != NULL)
    {
        BN_bn2bin(rsa->n, n);
        len= BN_num_bytes(rsa->n);
        printf("N:\n");
        PrintHex(n,len);
    }
    else
    {
        printf("PEM error \n");
    }
    if(rsa->e != NULL)
    {
        BN_bn2bin(rsa->e, e);
        len= BN_num_bytes(rsa->e);  
       	printf("E:\n");
        PrintHex(e,len);
    }
    else
    {
        printf("PEM error \n");
    }
    BIO_free(b);
    X509_free(x);
}
void GetPukfromDER()
{
    X509 *x;
    FILE *fp;
    unsigned char	buf[5000],*p;
    int	ret;
    BIO	*b;
    EVP_PKEY *k;
    RSA *rsa;
    unsigned char n[300] ={0x0};
    unsigned char e[300] ={0x0};
    unsigned int len;
	/* www.google.com2为DER编码的数字证书 ,从firefox中导出
	*/
    fp=fopen("www.google.com2","rb");
    if(!fp) return ;
    len=fread(buf,1,5000,fp);
    fclose(fp);
    p=buf;
    x=X509_new();
    d2i_X509(&x,(const unsigned char **)&p,len);
    k=X509_get_pubkey(x);
    rsa=EVP_PKEY_get1_RSA(k);
    if(rsa->n != NULL)
    {
        BN_bn2bin(rsa->n, n);
        len= BN_num_bytes(rsa->n);
	printf("N:\n");
	PrintHex(n,len);
    }
    else
    {
        printf("DER error \n");
    }
    if(rsa->e != NULL)
    {
        BN_bn2bin(rsa->e, e);
        len= BN_num_bytes(rsa->e);
        printf("E:\n");
        PrintHex(e,len);
    }
    else
    {
        printf("DER error \n");
    }
    BIO_free(b);
    X509_free(x);   
}
int main()
{
	GetPukfromPEM();
	GetPukfromDER();
	return 0;
}

程序执行结果如下:

N:
0xb073f0f2 0x04eec2a2 0x46ca342a 0xaabb6023 
0xd111761f 0x1f3ad065 0x834e9a45 0xa8437085 
0x76f01f87 0x00021f6e 0x3b1717c4 0xb5e91946 
0xa292258d 0x622ab463 0x301fb985 0xf835e116 
0x5a7649cc 0x50485339 0x5989d684 0x02fb9aec 
0x1bc751d5 0x769590d4 0x3a2ab8a6 0xde024d06 
0xfbcdeda5 0x46415f55 0x74e5ec7e 0x40dc509c 
0xb5e4355d 0x1e6820f8 0xe9dea36a 0x28bf41d2 
0xa1b3e225 0x8d0c1bca 0x3d930c18 0xaedfc5bc 
0xfdbc82ba 0x6800d716 0x32719f65 0xb511da68 
0x59d0a657 0x641bc9fe 0x98e5f5a5 0x65eae1db 
0xeef4b39d 0xb38eea87 0xae16d21e 0xa07c7c69 
0x3f291685 0x0153a76c 0xf160abdd 0xa2fc2547 
0xd432d112 0xddf74812 0xe0fc9ca2 0x7798e989 
0x99b8f838 0xf18c06c2 0x7a23366d 0x9b9dcd30 
0xc8c73417 0x1ebb7d42 0xc8abe715 0x16f673b5 
 
E:
0x010001
N:
0xb073f0f2 0x04eec2a2 0x46ca342a 0xaabb6023 
0xd111761f 0x1f3ad065 0x834e9a45 0xa8437085 
0x76f01f87 0x00021f6e 0x3b1717c4 0xb5e91946 
0xa292258d 0x622ab463 0x301fb985 0xf835e116 
0x5a7649cc 0x50485339 0x5989d684 0x02fb9aec 
0x1bc751d5 0x769590d4 0x3a2ab8a6 0xde024d06 
0xfbcdeda5 0x46415f55 0x74e5ec7e 0x40dc509c 
0xb5e4355d 0x1e6820f8 0xe9dea36a 0x28bf41d2 
0xa1b3e225 0x8d0c1bca 0x3d930c18 0xaedfc5bc 
0xfdbc82ba 0x6800d716 0x32719f65 0xb511da68 
0x59d0a657 0x641bc9fe 0x98e5f5a5 0x65eae1db 
0xeef4b39d 0xb38eea87 0xae16d21e 0xa07c7c69 
0x3f291685 0x0153a76c 0xf160abdd 0xa2fc2547 
0xd432d112 0xddf74812 0xe0fc9ca2 0x7798e989 
0x99b8f838 0xf18c06c2 0x7a23366d 0x9b9dcd30 
0xc8c73417 0x1ebb7d42 0xc8abe715 0x16f673b5 
 
E:
0x010001

 

<think>好的,用户之前问过关于C++操作智能卡的问题,现在又想知道如何用C++OpenSSL读取证书信息。我需要先理解用户的需求。可能用户是在处理与安全相关的项目,比如需要处理X.509证书,比如验证服务器证书、读取客户端证书信息,或者处理数字签名等。 用户可能已经对C++有一定的了解,现在需要结合OpenSSL库来实现读取证书的功能。他们可能不太清楚如何正确使用OpenSSL的API,或者需要具体的代码示例。同时,用户可能在使用过程中遇到了问题,比如编译链接错误,或者不知道如何解析证书的不同字段。 首先,我需要确认用户的操作系统环境,因为OpenSSL在不同平台上的安装链接方式可能不同。不过用户的问题没有提到具体环境,所以需要给出通用的步骤,同时可能需要说明在WindowsLinux下的不同处理方法。 接下来,要介绍OpenSSL库的基本使用,特别是X.509相关的函数。比如如何加载证书文件,解析其内容,提取主题、颁发者、有效期等信息。另外,用户可能还需要了解如何处理PEMDER格式的证书,以及如何读取证书中的扩展信息,比如主题备用名称(SAN)等。 用户可能需要示例代码来具体展示如何实现这些功能。代码需要包含必要的错误处理,比如检查文件是否成功加载,证书是否有效,内存管理是否正确(比如释放X509对象)。同时,要提醒用户注意OpenSSL的内存管理,避免内存泄漏,比如使用X509_free()来释放资源。 另外,用户可能对如何编译链接OpenSSL库有疑问,特别是对于不熟悉库依赖的新手来说,这部分需要详细说明,比如在Linux下使用-lssl -lcrypto参数,或者在Windows下配置项目属性包含库文件头文件。 还有可能用户在使用过程中遇到了特定的错误,比如证书格式不正确,或者路径问题导致文件无法打开。因此,在代码示例中加入错误处理的步骤是必要的,比如检查文件打开是否成功,证书解析是否正确,并输出相应的错误信息。 最后,用户可能需要进一步的功能,比如验证证书链、检查证书吊销状态(CRL/OCSP),或者与智能卡结合使用(比如之前的IC卡操作),但当前问题主要是读取证书信息,所以需要集中在基础部分,同时可以提到扩展的可能性,供用户参考。 总结来说,回答需要包括以下几个部分: 1. 准备工作:安装OpenSSL库,配置开发环境。 2. 代码步骤:读取证书文件,解析为X509对象,提取各项信息。 3. 示例代码:具体的C++代码,包含错误处理内存管理。 4. 编译指令:如何在不同的平台上编译代码。 5. 扩展说明:如何处理其他格式的证书,以及进一步操作的提示。 需要确保代码正确无误,特别是OpenSSL函数的正确使用,比如使用BIO_new_file读取文件,PEM_read_bio_X509解析PEM格式,X509_get_subject_name获取主题名称等。同时,注意字符编码问题,比如X509_NAME_print_ex的输出格式,可能需要处理UTF8或其他编码。 此外,用户可能需要了解如何获取证书的指纹(比如SHA1或SHA256哈希),或者公钥信息,这些可以作为扩展内容在示例代码中补充,或者简要提及。 最后,要提醒用户在使用OpenSSL时注意版本兼容性,比如1.1.13.0版本之间的API变化,确保代码示例适用于用户所使用的版本。</think>以下是如何在C++中使用OpenSSL库读取证书(如X.509证书)的详细指南,包含代码实现关键步骤说明: --- ### 一、准备工作 1. **安装OpenSSL库**: ```bash # Linux (Debian/Ubuntu) sudo apt-get install libssl-dev # Windows # 从 https://slproweb.com/products/Win32OpenSSL.html 下载预编译包 ``` 2. **项目编译参数**: ```bash g++ -o cert_reader cert_reader.cpp -lssl -lcrypto # Linux ``` --- ### 二、核心代码实现 #### 1. 加载证书文件(支持PEM/DER格式) ```cpp #include <openssl/x509.h> #include <openssl/pem.h> #include <iostream> X509* load_certificate(const char* cert_path) { BIO* cert_bio = BIO_new_file(cert_path, "r"); if (!cert_bio) { std::cerr << "无法打开证书文件" << std::endl; return nullptr; } X509* cert = PEM_read_bio_X509(cert_bio, nullptr, nullptr, nullptr); if (!cert) { // 尝试读取DER格式 BIO_reset(cert_bio); cert = d2i_X509_bio(cert_bio, nullptr); } BIO_free(cert_bio); return cert; } ``` #### 2. 解析证书基础信息 ```cpp void print_cert_info(X509* cert) { // 获取证书版本 long version = X509_get_version(cert); std::cout << "证书版本: v" << version + 1 << std::endl; // 获取序列号 ASN1_INTEGER* serial = X509_get_serialNumber(cert); BIGNUM* bn = ASN1_INTEGER_to_BN(serial, nullptr); char* serial_hex = BN_bn2hex(bn); std::cout << "序列号: " << serial_hex << std::endl; OPENSSL_free(serial_hex); BN_free(bn); // 获取颁发者/主体信息 auto print_name = [](const char* label, X509_NAME* name) { BIO* bio = BIO_new(BIO_s_mem()); X509_NAME_print_ex(bio, name, 0, XN_FLAG_RFC2253); char buffer[1024]; int len = BIO_read(bio, buffer, sizeof(buffer)-1); buffer[len] = '\0'; std::cout << label << ": " << buffer << std::endl; BIO_free(bio); }; print_name("颁发者", X509_get_issuer_name(cert)); print_name("主体", X509_get_subject_name(cert)); } ``` #### 3. 解析有效期 ```cpp void print_validity(X509* cert) { ASN1_TIME* not_before = X509_get_notBefore(cert); ASN1_TIME* not_after = X509_get_notAfter(cert); auto convert_time = [](ASN1_TIME* tm) -> std::string { BIO* bio = BIO_new(BIO_s_mem()); ASN1_TIME_print(bio, tm); char buffer[128]; int len = BIO_read(bio, buffer, 127); BIO_free(bio); return std::string(buffer, len); }; std::cout << "有效期从: " << convert_time(not_before) << "\n" << "到 : " << convert_time(not_after) << std::endl; } ``` #### 4. 完整调用示例 ```cpp int main() { OpenSSL_add_all_algorithms(); // 初始化OpenSSL算法 X509* cert = load_certificate("example.pem"); if (!cert) { std::cerr << "证书加载失败" << std::endl; return 1; } print_cert_info(cert); print_validity(cert); X509_free(cert); EVP_cleanup(); // 清理OpenSSL资源 return 0; } ``` --- ### 三、高级功能扩展 #### 1. 读取扩展信息(如SAN) ```cpp void print_san(X509* cert) { STACK_OF(GENERAL_NAME)* sans = static_cast<STACK_OF(GENERAL_NAME)*>( X509_get_ext_d2i(cert, NID_subject_alt_name, nullptr, nullptr)); if (sans) { int count = sk_GENERAL_NAME_num(sans); for (int i=0; i<count; ++i) { GENERAL_NAME* san = sk_GENERAL_NAME_value(sans, i); if (san->type == GEN_DNS) { unsigned char* dns; ASN1_STRING_to_UTF8(&dns, san->d.dNSName); std::cout << "SAN DNS: " << dns << std::endl; OPENSSL_free(dns); } } sk_GENERAL_NAME_pop_free(sans, GENERAL_NAME_free); } } ``` #### 2. 提取公钥信息 ```cpp #include <openssl/rsa.h> void print_public_key(X509* cert) { EVP_PKEY* pkey = X509_get_pubkey(cert); if (!pkey) return; if (EVP_PKEY_id(pkey) == EVP_PKEY_RSA) { RSA* rsa = EVP_PKEY_get1_RSA(pkey); const BIGNUM* n, *e; RSA_get0_key(rsa, &n, &e, nullptr); char* n_hex = BN_bn2hex(n); char* e_hex = BN_bn2hex(e); std::cout << "RSA公钥模数(n):\n" << n_hex << "\n指数(e): " << e_hex << std::endl; OPENSSL_free(n_hex); OPENSSL_free(e_hex); RSA_free(rsa); } EVP_PKEY_free(pkey); } ``` --- ### 四、编译运行示例 ```bash # Linux编译 g++ -o cert_reader cert_reader.cpp -lssl -lcrypto # 运行 ./cert_reader example.pem # 示例输出 证书版本: v3 序列号: 12A456B7F891C 颁发者: CN=CA Root, OU=Security, O=MyOrg 主体: CN=server.example.com, OU=IT 有效期从: Jan 1 00:00:00 2023 GMT 到 : Dec 31 23:59:59 2024 GMT ``` --- ### 五、关键注意事项 1. **内存管理**: - 必须调用`X509_free()`释放证书对象 - OpenSSL返回的字符串需用`OPENSSL_free()`释放 2. **错误处理**: ```cpp ERR_load_crypto_strings(); // 初始化错误信息 unsigned long err = ERR_get_error(); if (err) std::cerr << "OpenSSL错误: " << ERR_error_string(err, nullptr); ``` 3. **证书格式**: - PEM格式:以`-----BEGIN CERTIFICATE-----`开头 - DER格式:二进制格式,需使用`d2i_X509_bio()` 通过上述代码,可以完整实现证书信息的解析与展示。实际开发中建议封装为`Certificate`类进行资源管理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值