调用openssl api函数C代码生成证书

概述

因工作需要用到很多新证书,但是我想更灵活处理证书,而不是用openssl命令行生成证书。网上一堆的命令行操作,我找了很久没有发现用代码生成证书的。
基于此,本人利用端午放假,花了一整天的时间,翻遍了openssl源码,找相关函数,不断地试错,终于生成证书并测试好。

软件环境

openssl:版本1.1.1k,编译好windows x86静态库
visual stdio:版本2010
Keystore Explorer:版本5.3.0,用于查看确认证书的

证书组成

如图Keystore Explorer查看的证书
在这里插入图片描述
除了最下面这一行Fingerprint,其余的都是证书里面的部分,即为:
(1)、版本
(2)、颁发者
(3)、主题者
(4)、证书序列号
(5)、有效期开始
(6)、有效期结束
(7)、公钥
(8)、签名算法及签名数据
除了签名算法及签名数据不是必须的,其余的要素都要。但是证书签名在发布出去的时候基本上要有。即生成证书之后未签名,然后拿着未签名证书去找另外一个ca签名。

关键生成证书代码


int gen_cer_cert(char* pemFileName, const char* cerPubE, const char* cerPubN, unsigned long long cerId,
	int cerFromTimeYear, int cerFromTimeMonth, int cerFromTimeDay, int cerFromTimeHour, int cerFromTimeMin, int cerFromTimeSec,
	int cerToTimeYear, int cerToTimeMonth, int cerToTimeDay, int cerToTimeHour, int cerToTimeMin, int cerToTimeSec,
	char* issuerCN, char* issuerOU, char* issuerO, char* issuerL, char* issuerST, char* issuerC,
	char* subjectCN, char* subjectOU, char* subjectO, char* subjectL, char* subjectST, char* subjectC,
	char* cerSignE, char* cerSignN, char* cerSignD)
{
	EVP_PKEY* pPubKey = NULL;
	EVP_PKEY* pSignKey = NULL;
	RSA* rsaPubCtx = NULL;
	RSA* rsaSignCtx = NULL;
	BIGNUM* bigE = NULL;
	BIGNUM* bigN = NULL;
	BIGNUM* bigD = NULL;
	int re = 0;
	X509* x509 = NULL;
	ASN1_INTEGER* asnInteger = NULL;
	ASN1_TIME* ascTime = NULL;
	char buf[32];
	X509_NAME* x509Name = NULL;
	FILE* pfile = NULL;

	x509 = X509_new();	

	//X509_set_version(x509, 1);

	rsaPubCtx = RSA_new();
	pPubKey = EVP_PKEY_new();

	bigE = BN_new();
	bigN = BN_new();

	BN_hex2bn(&bigE, cerPubE);
	BN_hex2bn(&bigN, cerPubN);

	RSA_set0_key(rsaPubCtx, bigN, bigE, NULL);

	EVP_PKEY_set1_RSA(pPubKey, rsaPubCtx);
	X509_set_pubkey(x509, pPubKey);	

	asnInteger = ASN1_INTEGER_new();
	ASN1_INTEGER_set_uint64(asnInteger, cerId);
	X509_set_serialNumber(x509, asnInteger); /* from openssl source,ASN1_INTEGER asnInteger copy,should free asnInteger after */
	ASN1_INTEGER_free(asnInteger);

	ascTime = ASN1_TIME_new();
	//ASN1_TIME_set_string(ascTime, "20220604184500Z");
	sprintf(buf, "%04d%02d%02d%02d%02d%02dZ",
		cerFromTimeYear, cerFromTimeMonth, cerFromTimeDay,
		cerFromTimeHour, cerFromTimeMin, cerFromTimeSec);
	ASN1_TIME_set_string(ascTime, buf);
	X509_set1_notBefore(x509, ascTime);
	ASN1_TIME_free(ascTime);

	ascTime = ASN1_TIME_new();
	//ASN1_TIME_set_string(ascTime, "20220605164508Z");
	sprintf(buf, "%04d%02d%02d%02d%02d%02dZ",
		cerToTimeYear, cerToTimeMonth, cerToTimeDay,
		cerToTimeHour, cerToTimeMin, cerToTimeSec);
	ASN1_TIME_set_string(ascTime, buf);
	X509_set1_notAfter(x509, ascTime);
	ASN1_TIME_free(ascTime);

	x509Name = X509_get_issuer_name(x509);

	X509_NAME_add_entry_by_txt(x509Name, "CN", MBSTRING_ASC, (unsigned char*)issuerCN, -1, -1, 0);
	X509_NAME_add_entry_by_txt(x509Name, "OU", MBSTRING_ASC, (unsigned char*)issuerOU, -1, -1, 0);
	X509_NAME_add_entry_by_txt(x509Name, "O", MBSTRING_ASC, (unsigned char*)issuerO, -1, -1, 0);
	X509_NAME_add_entry_by_txt(x509Name, "L", MBSTRING_ASC, (unsigned char*)issuerL, -1, -1, 0);
	X509_NAME_add_entry_by_txt(x509Name, "ST", MBSTRING_ASC, (unsigned char*)issuerST, -1, -1, 0);
	X509_NAME_add_entry_by_txt(x509Name, "C", MBSTRING_ASC, (unsigned char*)issuerC, -1, -1, 0);

	x509Name = X509_get_subject_name(x509);
	X509_NAME_add_entry_by_txt(x509Name, "CN", MBSTRING_ASC, (unsigned char*)subjectCN, -1, -1, 0);
	X509_NAME_add_entry_by_txt(x509Name, "OU", MBSTRING_ASC, (unsigned char*)subjectOU, -1, -1, 0);
	X509_NAME_add_entry_by_txt(x509Name, "O", MBSTRING_ASC, (unsigned char*)subjectO, -1, -1, 0);
	X509_NAME_add_entry_by_txt(x509Name, "L", MBSTRING_ASC, (unsigned char*)subjectL, -1, -1, 0);
	X509_NAME_add_entry_by_txt(x509Name, "ST", MBSTRING_ASC, (unsigned char*)subjectST, -1, -1, 0);
	X509_NAME_add_entry_by_txt(x509Name, "C", MBSTRING_ASC, (unsigned char*)subjectC, -1, -1, 0);

	rsaSignCtx = RSA_new();
	pSignKey = EVP_PKEY_new();

	bigE = BN_new();
	bigN = BN_new();
	bigD = BN_new();

	BN_hex2bn(&bigE, cerSignE);
	BN_hex2bn(&bigN, cerSignN);
	BN_hex2bn(&bigD, cerSignD);

	RSA_set0_key(rsaSignCtx, bigN, bigE, bigD);

	EVP_PKEY_set1_RSA(pSignKey, rsaSignCtx);
	X509_sign(x509, pSignKey, EVP_md5());

	pfile = fopen(pemFileName, "w");
	PEM_write_X509(pfile, x509);
	fclose(pfile);

EXIT_FUN:
	if (rsaSignCtx != NULL) {
		RSA_free(rsaSignCtx);
		rsaSignCtx = NULL;
	}
	if (rsaPubCtx != NULL) {
		RSA_free(rsaPubCtx);
		rsaPubCtx = NULL;
	}
	if (pPubKey != NULL) {
		EVP_PKEY_free(pPubKey);
		pPubKey = NULL;
	}
	if (pSignKey != NULL) {
		EVP_PKEY_free(pSignKey);
		pSignKey = NULL;
	}
	if (x509 != NULL) {
		X509_free(x509);
		x509 = NULL;
	}
	return re;
}

测试代码

#pragma comment(lib, "Crypt32.lib")
#pragma comment(lib, "compile_win_x86_lib\\lib\\libcrypto.lib")
#pragma comment(lib, "compile_win_x86_lib\\lib\\libssl.lib")
#include "openssl/rsa.h"
#include "openssl/pem.h"
#include "openssl/x509v3.h"
void _tmain(int argc, _TCHAR* argv[])
{
	//test_cer();
	//test_pri_key();
	//test_cert_new();
	//test_save_pub();
	const char* pubE = "010001";
	const char* pubN = "C950B92F22236BA7CD6DECFD8280917DCFD36FBECD1B2782728C1AD90E3986BEB262C2E7E5CB8145E6CFDF9518C57E0C40737E76E42368B4EBD89229CF294A56976D647E706F1CE1250354816BF7E77270FC969B77C41F0B1B18C954A8B6163AD8E4C057F629F4E9A0B2D7FBDBB0A0004A94657A41C345C605C399E5BF726F33CE7BF2FEDBEABA7A934858A49DF17AE3F08753495852AC0491D5951A24913E1CF645E1EC938BAB3634E335F92AF8F147319E716CB906B7D4CF58EDD33E3CD857CAB81F98697E0F8B7265C5F441B0C46F0A20635E14336D9B047701E1A4AB29742EB739631B3CE089C9A48456897C05C5EC58A44B6181CED01423366C924C83B5";

	const char* signE = "010001";
	const char* signN = "DA51CA066FF0C04959AEDBBEBF61B315017DE5CBC57B964A7B884064F41DE15F53B7F13A9139C7D3CB6120D072E20D23BEC8EA237DD3AE76E411FCF250B1F18E9F883C33FA0FD8A5E488F1A7C538DDE94EC45FA9A467EA96871910144D68A20F6B79C0C8195F0772EDCCC84343C9460746315FF47C3AB3FC2222E1F51AA41AF6DBA67951272C7E1337FA5AC124C8ADA84CF3A34CB9FC625CB6E3199BD4EB68E851422914465323D78CD92E0DCC06397D1419DD0EB7FB4D61B2B0D997BB7BA43C9E3B0B956F9D9FC64013B317D8FD6C243FCCD2EB1E59B857D25FB1BE66440EE90E07115CF94BAD18F6790E6FE00520FF8BBBCB02187A7F2BDDDA1D92D5D6BCA9";
	const char* signD = "9B270B0B1E1C9B9AC54896546E1051C3B8E6AF1BECC1D2E225FE83BCFB1C0D21C10E753A29214619076D84737F6194F2F55035CD794BA9AE46915E111B360AB4503DF28301D0A727E64D4867F1AA6352BE2EA2C99DD4580C1800C0C39CEBA2589109DAEE99D1462AF042DB83AA35ED359835D8BA985AE3B468D21F812028C61B9EA12BBF194BCA5BA9A2433A1193CA9BD5B515AA2363F5E486BBA5D9C9B0DE1AC22984739E9831B8AD2D193C1161A5485C9907D4BD1F5F93B1B7ABC94FDD834CFAD135330F1E30468AFA6BFD0F0CEB7097D1111329600EFE5BC83493B4A5D9FB2B483B27BD232FD0E310B0AAE1998479B05E5E65D120C703D5E108A36E5F3201";

	gen_cer_cert("test123.pem", pubE, pubN, 1234567890123456, 
		2022, 6, 3, 18, 0, 0,
		2023, 6, 5, 18, 0, 0,
		"1", "2", "3", "4", "5", "AU",
		"a", "b", "c", "d", "e", "DK",
		(char*)signE, (char*)signN, (char*)signD);
}

验证及说明

这是我生成证书然后工具查看截图
在这里插入图片描述
(1)、版本,这是自动生成的,代码中我都注释了,//X509_set_version(x509, 1);
(2)、发行者和主题者,注意C表示国家名,两个字母简写,不要乱写,一定要存在,不然显示不出来。DK表示丹麦,AU表示澳大利亚。
(3)、公钥ID值,8字节整型,设置十进制值1234567890123456,你看看是不是工具上看到得十六进制数据0x462D53C8ABAC0。
(4)、时间设置,本实例设置从2022, 6, 3, 18, 0, 0,到2023, 6, 5, 18, 0, 0,这是格林威治标准时间,在工具上看到是这个时间上加8小时,因为我们北京属于东八区。
(5)、公钥,只需要传两个参数,pubE,pubN,即公钥指数和公钥模。
(6)、签名,传入的是私钥。只需要传三个参数,char* cerSignE, char* cerSignN, char* cerSignD。另外特别注意签名算法,hash算法MD5,本示例为MD5WITHRSA,这个已经在行业内认为不安全了。业内目前公认hash算法SHA256,仅需要将**X509_sign(x509, pSignKey, EVP_md5());改为X509_sign(x509, pSignKey, EVP_sha256());**或者你认为更高级别的。

  • 5
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值