Bootstrap

基于OpenSSL,实现C语言SM2的PKCS10的证书请求

基于OpenSSL, 实现算法的 Engine专栏

基于OpenSSL, 实现数据编码转换专栏

本专栏订阅后可查看以下文章 

基于OpenSSL,实现C语言RSA的PKCS10的证书请求

在日常工作中使用密码,就一定会接触到OpenSSL,这个算法库带给我们的东西太多太多,不管是密码算法层面,还有代码框架和使用技巧上更是值得我们去学习和专研。

在接触密码学中,就会涉及到证书,我们在虚拟网络中产生信任,就要有一个信任源,而在密码层面,信任源就是颁布证书的认证中心,从而,证书就是我们可以相信的虚拟对象。

在自己的学习过程中,可以参考网络中,自己创建一个CA中心,自己产生一个证书请求,自己用CA中心颁发一个自签名的证书,这样在验证过程中,就可以验证此证书了。这里的证书请求就是PKCS10,简称为P10。下面我将给出针对C语言产生P10的实例。

主要函数如下:

// 签名验签的详细操作请参考我的另外一篇博文
// https://blog.csdn.net/weixin_49984575/article/details/126044638
rv = lbxx_sm2_signature(prikey, prikeylen, tbs, tbslen, sig, &siglen);
rv = lbxx_sm2_verify(pubkey, pubkeylen, tbs, tbs_len, sig, siglen);
rv = pkcs10_make_req(g_public_key, g_public_key_len, g_private_key, g_private_key_len, dn, pkcs10, &p10_len);
rv = pkcs10_verify_req(pkcs10, p10_len);

具体调用方式如下,我添加了一些注释,便于理解,这里需要使用带有国密算法SM2/3/4的OpenSSL库编译,否则会报错误

/****************************************************************
 * FileName:    pkcs10_req.c
 * Author:      labixiaoxin1849
 * Date:        2022-10-27
 * Description: Generate certificate request by OpenSSL
 ****************************************************************/

#include <string.h>
#include "openssl/x509.h"
#include "openssl/evp.h"
#include "openssl/pem.h"

unsigned char g_user_id[16] = "1234567812345678";
unsigned char g_public_key[65] = {
					0x04, 
					0xaf, 0x35, 0x42, 0xf0, 0xb3, 0x61, 0x3b, 0x2f, 
					0x09, 0x5c, 0x91, 0x99, 0xb3, 0xe7, 0xbf, 0x66, 
					0xce, 0x7c, 0x36, 0xc3, 0xef, 0x98, 0x7d, 0xff, 
					0x53, 0x7a, 0x1d, 0x8d, 0x9a, 0x55, 0x18, 0x37, 
					0xbd, 0x68, 0xd5, 0x3a, 0xc0, 0x9b, 0x40, 0x37, 
					0x8f, 0x02, 0x55, 0xf6, 0xaa, 0x74, 0xd3, 0x05, 
					0xb1, 0x41, 0x0a, 0xf8, 0x28, 0x48, 0xda, 0x83, 
					0xad, 0x9a, 0xe0, 0x05, 0x74, 0x36, 0x03, 0x7a
					};
unsigned int g_public_key_len = sizeof(g_public_key);
unsigned char g_private_key[32] = {
					0x89, 0xc3, 0x8a, 0x25, 0x27, 0x8e, 0xd7, 0x44, 
					0x45, 0x8e, 0x95, 0x1b, 0xad, 0xa9, 0xc4, 0x0c, 
					0x88, 0x3a, 0xd7, 0xc8, 0xc1, 0x73, 0xbb, 0x96, 
					0x5c, 0x78, 0x1d, 0x22, 0x5d, 0xcb, 0xbe, 0x89
					};
unsigned int g_private_key_len = sizeof(g_private_key);

static void trim_string(const char *strIn, int len, char *strOut)
{
	const char *start, *end, *temp;
	char prefix_char;

	temp = strIn;
	while (*temp == ' ' || *temp == '\t') {
		++temp;
	}
	start = temp;

	if (len == -1) {
		len = strlen(strIn);
	}
	temp = strIn + len - 1;
	while (*temp == ' ' || *temp == '\t') {
		--temp;
	}
	end = temp;

	for (strIn = start; strIn <= end;) {
		if (*strIn != '\\') {
			prefix_char = *strIn;
		}
		else {
			if (prefix_char != '\\') {
				strIn++;
			}
		}

		*strOut++ = *strIn++;
	}

	*strOut = '\0';
}
static void parse_dn(const char *dn, char outs[32][1024])
{
	const char *start = dn, *end = dn;
	int inEscape = 0;     // is in escape
	int i = 0;

	while (*end) {
		if (*end == '\\') {
			inEscape = 1;
			end++;
			continue;
		}
		if (*end == ',' && inEscape) {
			end++;
			inEscape = 0;
			continue;
		}

		if (*end == ',') {
			trim_string(start, end - start, outs[i++]);
			end++; // skip the ,
			start = end;
		}
		else {
			end++;
		}
	}

	trim_string(start, end - start, outs[i++]);

	return;
}
static void parse_key_value(const char *dn, char *key, char *value)
{
	const char *ptr = dn;
	while (*ptr) {
		if (*ptr == '=') {
			trim_string(dn, ptr - dn, key);
			trim_string(dn + (ptr - dn) + 1, -1, value);
			break;
		}
		ptr++;
	}
	return;
}

int lbxx_sm2_signature(unsigned char *prikey, unsigned int prikeylen, unsigned char *in, unsigned inlen, unsigned char *out, size_t *outlen)
{
    int ret = 0;
    EVP_PKEY *pKey = NULL;
    EVP_PKEY_CTX *pkey_ctx;
	EC_KEY *pSM2 = NULL;
	pSM2 = EC_KEY_new_by_curve_name(EVP_PKEY_SM2);
	if (!pSM2) {
		return -1;
	}
	/* 从内存设置私钥 */
	ret = EC_KEY_oct2priv(pSM2, prikey, prikeylen);
	if (ret != 1) {
		EC_KEY_free(pSM2);
		return -1;
	}
	/* 创建EVP_PKEY并设置密钥 */
	pKey = EVP_PKEY_new();
	if (!pKey) {
		EC_KEY_free(pSM2);
		return -1;
	}
	if (!EVP_PKEY_set1_EC_KEY(pKey, pSM2)){
		EC_KEY_free(pSM2);
		return -1;
	}
	EC_KEY_free(pSM2);
	
	EVP_PKEY_set_alias_type(pKey, EVP_PKEY_SM2);
	pkey_ctx = EVP_PKEY_CTX_new(pKey, NULL);
	if (!pkey_ctx) {
		EVP_PKEY_free(pKey);
		return -1;
	}
	EVP_PKEY_free(pKey);
	
	if (EVP_PKEY_sign_init(pkey_ctx) <= 0) {
		EVP_PKEY_CTX_free(pkey_ctx);
		return -1;
	}
	if (EVP_PKEY_CTX_set_signature_md(pkey_ctx, EVP_get_digestbynid(NID_sm3)) <= 0){
		EVP_PKEY_CTX_free(pkey_ctx);
		return -1;
	}
	if (EVP_PKEY_sign(pkey_ctx, out, outlen, in, inlen) <= 0){
		EVP_PKEY_CTX_free(pkey_ctx);
		return -1;
	}
	EVP_PKEY_CTX_free(pkey_ctx);
	return 0;
}

int lbxx_sm2_verify(unsigned char *pubkey, unsigned int pubkeylen, unsigned char *tbs, unsigned int tbslen, unsigned char *sig, unsigned int siglen)
{
	EVP_PKEY *pKey = NULL;
	EVP_PKEY_CTX *pkey_ctx;
	EC_KEY *pSM2 = NULL;
	pSM2 = EC_KEY_new_by_curve_name(EVP_PKEY_SM2);
	if (!pSM2) {
		return -1;
	}
	/* 从内存设置公钥 */
	const unsigned char *pp = pubkey;
	if (!o2i_ECPublicKey(&pSM2, &pp, (long)pubkeylen)) {
		EC_KEY_free(pSM2);
		return -1;
	}
	/* 创建EVP_PKEY并设置密钥 */
	pKey = EVP_PKEY_new();
	if (!pKey) {
		EC_KEY_free(pSM2);
		return -1;
	}
	if (!EVP_PKEY_set1_EC_KEY(pKey, pSM2)){
		EC_KEY_free(pSM2);
		return -1;
	}
	EC_KEY_free(pSM2);
	/* 设置类型为SM2,并初始化CTX */
	EVP_PKEY_set_alias_type(pKey, EVP_PKEY_SM2);
	pkey_ctx = EVP_PKEY_CTX_new(pKey, NULL);
	if (!pkey_ctx) {
		EVP_PKEY_free(pKey);
		return -1;
	}
	EVP_PKEY_free(pKey);

	if (EVP_PKEY_verify_init(pkey_ctx) <= 0) {
		EVP_PKEY_CTX_free(pkey_ctx);
		return -1;
	}
	if (EVP_PKEY_CTX_set_signature_md(pkey_ctx, EVP_get_digestbynid(NID_sm3)) <= 0){
		EVP_PKEY_CTX_free(pkey_ctx);
		return -1;
	}
	if (EVP_PKEY_verify(pkey_ctx, sig, siglen, tbs, tbslen) <= 0){
		EVP_PKEY_CTX_free(pkey_ctx);
		return -1;
	}
	EVP_PKEY_CTX_free(pkey_ctx);
	return 0;
}

void lbxx_pkcs10_set_dn(X509_REQ *x509_req, const char *dn)
{
    int i = 0;
	char outs[32][1024] = { 0 };
	parse_dn(dn, outs);
	for (i = 0; i < 32; i++) {
		char key[128] = { 0 }, value[1024] = { 0 };
		X509_NAME *name = X509_REQ_get_subject_name(x509_req);
		int nid;

		if (outs[i][0] == 0) {
			break;
		}
		parse_key_value(outs[i], key, value);

		nid = OBJ_txt2nid(key);
		if (nid == NID_undef) {
			continue;
		}

		X509_NAME_add_entry_by_NID(name, nid, V_ASN1_UTF8STRING,
			(unsigned char*)value, (int)strlen((const char*)value), -1, 0);
	}
}

void lbxx_pkcs10_set_pubkey(X509_REQ *x509_req, unsigned char *pubkey, unsigned int pubkeyLen)
{
    X509_PUBKEY *x509PubKey = X509_REQ_get_X509_PUBKEY(x509_req);
    
    X509_PUBKEY_set0_param(x509PubKey,
            OBJ_nid2obj(NID_X9_62_id_ecPublicKey),
            V_ASN1_OBJECT, OBJ_nid2obj(NID_sm2),
            OPENSSL_memdup(pubkey, pubkeyLen),
            pubkeyLen);
}

int lbxx_pkcs10_get_pubkey(X509_REQ *x509_req, unsigned char *pubkey, unsigned int *pubkeyLen)
{
	X509_PUBKEY *x509_pubkey;
	ASN1_OBJECT *aobj;
	unsigned char *pk;
	int len;

	x509_pubkey = X509_REQ_get_X509_PUBKEY(x509_req);
	if (!x509_pubkey) {
		return -1;
	}

	X509_PUBKEY_get0_param(&aobj, (const unsigned char **)&pk, &len, NULL, x509_pubkey);
	
	if (pubkey == NULL) {
		*pubkeyLen = len;
		return 0;
	}
	if (len > (int)*pubkeyLen) {
		*pubkeyLen = len;
		return -1;
	}

	*pubkeyLen = len;
	memcpy(pubkey, pk, len);
	return 0;
}

int lbxx_pkcs10_get_tbs(X509_REQ *x509_req, unsigned char *rtn, unsigned int *rtnlen)
{
	unsigned char *tbs = NULL;
	unsigned int tbslen = 0;
	
	tbslen = i2d_re_X509_REQ_tbs(x509_req, &tbs);
	if (tbslen <= 0){
		return -1;
	}
	if (rtn == NULL) {
		*rtnlen = tbslen;
		return 0;
	}
	if (tbslen > (int)*rtnlen){
		*rtnlen = tbslen;
		return -1;
	}
	memcpy(rtn, tbs, tbslen);
	*rtnlen = tbslen;
	return 0;
}

void lbxx_pkcs10_set_sig(X509_REQ *x509_req, unsigned char *psig, unsigned int sig_len)
{
	ASN1_BIT_STRING *abit;

    X509_REQ_get0_signature(x509_req, (const ASN1_BIT_STRING **)&abit, NULL);

    ASN1_STRING_set(abit, psig, sig_len);
    abit->flags &= ~(ASN1_STRING_FLAG_BITS_LEFT | 0x07);
    abit->flags |= ASN1_STRING_FLAG_BITS_LEFT;
}

int lbxx_pkcs10_get_sig(X509_REQ *x509_req, unsigned char *psig, unsigned int *sig_len)
{
	int len = 0;
	ASN1_BIT_STRING *abit;

	X509_REQ_get0_signature(x509_req, (const ASN1_BIT_STRING **)&abit, NULL);

	len = ASN1_STRING_length(abit);
	if (psig == NULL) {
		*sig_len = len;
		return 0;
	}
	if (len > (int)*sig_len) {
		*sig_len = len;
		return -1;
	}

	*sig_len = len;
	memcpy(psig, ASN1_STRING_get0_data(abit), len);
	return 0;
}

// openssl需支持sm2、sm3
void lbxx_pkcs10_set_algor(X509_REQ *x509_req)
{
	X509_ALGOR *algor = NULL;

	X509_REQ_get0_signature(x509_req, NULL, (const X509_ALGOR **)&algor);
	X509_ALGOR_set0(algor, OBJ_nid2obj(NID_sm3withSM2Signature), V_ASN1_NULL, NULL);
}

void lbxx_pkcs10_req_der_encode(X509_REQ *x509_req, unsigned char *p10, unsigned int *p10len)
{
	*p10len = i2d_X509_REQ(x509_req, &p10);
}

int lbxx_pkcs10_req_der_decode(X509_REQ **x509_req, const unsigned char *p10, unsigned int p10len)
{
	X509_REQ *x;
	const unsigned char *kptr;
	
	kptr = p10;
	x = d2i_X509_REQ(NULL, &kptr, p10len);
	if (x == NULL) {
		return -1;
	}

	*x509_req = x;
	return 0;
}

int pkcs10_verify_req(unsigned char *pkcs10, unsigned int p10_len)
{
	int rv;
	X509_REQ *x509_req = NULL;
	unsigned char sig[1024] = { 0 };
	unsigned int  siglen = sizeof(sig);
	unsigned char tbs[8192] = { 0 };
	unsigned int  tbs_len = sizeof(tbs);
	unsigned char pubkey[1024] = { 0 };
	unsigned int  pubkeylen = sizeof(pubkey);

	lbxx_pkcs10_req_der_decode(&x509_req, pkcs10, p10_len);

	rv = lbxx_pkcs10_get_sig(x509_req, sig, &siglen);
	if (rv != 0) {
		return rv;
	}

	rv = lbxx_pkcs10_get_pubkey(x509_req, pubkey, &pubkeylen);
	if (rv != 0) {
		return rv;
	}

	rv = lbxx_pkcs10_get_tbs(x509_req, tbs, &tbs_len);
	if (rv != 0) {
		return rv;
	}

	rv = lbxx_sm2_verify(pubkey, pubkeylen, tbs, tbs_len, sig, siglen);
	if (rv != 0) {
		return rv;
	}
	return 0;
}

int pkcs10_make_req(unsigned char *pubkey, unsigned int pubkeylen, unsigned char *prikey, unsigned int prikeylen, const char *dn, unsigned char *pkcs10, unsigned int *p10_len)
{
    int rv = 0;
    X509_REQ *x509_req = NULL;
	unsigned char tbs[256] = { 0 };
    unsigned int tbslen = sizeof(tbs);
    unsigned char sig[128] = { 0 };
    // 这里一定要特别注意这个输出长度的类型,使用unsigned int后,签名结果就会错误
    size_t  siglen = sizeof(sig);
	
    x509_req = X509_REQ_new();
	if (x509_req == NULL) {
		return -1;
	}
    X509_REQ_set_version(x509_req, 0);

	lbxx_pkcs10_set_dn(x509_req, dn);
    
	lbxx_pkcs10_set_pubkey(x509_req, pubkey, pubkeylen);
	
	rv = lbxx_pkcs10_get_tbs(x509_req, tbs, &tbslen);
	if (rv != 0) {
		X509_REQ_free(x509_req);
		return -1;
	}

	rv = lbxx_sm2_signature(prikey, prikeylen, tbs, tbslen, sig, &siglen);
	if(rv != 0) {
		X509_REQ_free(x509_req);
		return -1;
    }
	
	lbxx_pkcs10_set_sig(x509_req, sig, siglen);
	lbxx_pkcs10_set_algor(x509_req);
	lbxx_pkcs10_req_der_encode(x509_req, pkcs10, p10_len);
	
    X509_REQ_free(x509_req);
    return 0;
}

int main(int argc, char *argv[])
{
	int rv = 0;
	unsigned char pkcs10[4096] = { 0 };
	unsigned int  p10_len = sizeof(pkcs10);

	const char *dn = "CN=labixiaoxin,ST=Beijing,C=CN";

	rv = pkcs10_make_req(g_public_key, g_public_key_len, g_private_key, g_private_key_len, dn, pkcs10, &p10_len);
	if (rv != 0){
		return 1;
	}
	rv = pkcs10_verify_req(pkcs10, p10_len);
	if (rv != 0){
		return 1;
	}
	printf("PKCS10 create request success !!!\n");
	return 0;
}