本专栏订阅后可查看以下文章
基于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;
}