gmssl编程之签发X509证书

  • 前言
  • 命令行实现方式
  • 编程实现方式
    • step1. 产生密钥对
    • step2. 生成证书请求
    • step3. 签发X509
  • 完整代码
  • 运行结果

前言

最近由于项目需求,需要通过代码组装并签发标准X509格式数字证书。故而查询资料对gmssl/openssl中签发证书流程就行了一番研究,终于完成。先记录如下.

命令行实现方式

命令行方式下使用gmssl/openssl指令进行证书签发主要有三步:生成密钥对(私钥)、生成证书请求、签发证书。
具体小伙伴们可参考小编这篇文章:Gmssl生成自签名证书

编程实现方式

同理,编程方式其实就是通过gmssl/openssl库相关接口完成命令行方式的三步。
当然签发用户证书首先需要有CA根证书,因为是测试,所这里小编是通过gmssl命令行方式生成了一对CA根证书(cacert.pem)及CA根私钥(cakey.pem).

step1. 产生密钥对

密钥对与算法有关,看小伙伴们实际需要,gmssl中都有相关的接口去完成该功能。例如:
RSA密钥对

 RSA  *ret = NULL;BIGNUM *bn = NULL;unsigned long e = RSA_F4;int bits = 2048;bn = BN_new();ret = RSA_new();BN_set_word(bn, e)RSA_generate_key_ex(ret, bits, bn, NULL);//......

EC密钥对

 EC_KEY *ret = NULL;ret = EC_KEY_new_by_curve_name(NID_sm2p256v1);EC_KEY_generate_key(ret);//......

step2. 生成证书请求

有了密钥对后,接下来需要组装出证书请求,即X509_REQ结构数据。

生成证书请求主要流程如下:

  1. 创建X509_REQ对象
    通过X509_REQ_new方法实现。
  2. 设置版本号
    版本信息使用X509_REQ_set_version方法进行设置。
  3. 设置使用者信息
    使用者信息可使用X509_REQ_set_subject_name方法直接设置;
    或者通过X509_REQ_get_subject_name先取得X509_NAME,然后使用X509_NAME_add_entry_by_NID方法单独设置X509_NAME中的某一项。
  4. 设置公钥
    对于EC公钥,通过EVP_PKEY_assign_EC_KEY方法将step1中生成的公钥填入到x509_req中;
    对于RSA公钥,通过EVP_PKEY_assign_RSA方法将step1中生成的公钥填入到x509_req中。
  5. 设置扩展项信息(可选)
    扩展项信息可不填;
    若要填写,可通过sk_X509_EXTENSION_new_null、X509V3_EXT_conf、sk_X509_EXTENSION_push、X509_REQ_add_extensions等方法实现。
  6. 设置签名值
    签名值通过X509_REQ_sign方法,使用step1中生成的私钥对x509_req进行签名后自动填入。

step3. 签发X509

签发x509证书即通过step2中的证书请求组装出X509结构证书数据,并使用CA根证书对其进行签名。

组装并签发证书主要流程如下:

  1. 创建X509对象
    通过X509_new创建X509对象newcert;
  2. 设置版本号
    通过X509_set_version完成设置;
  3. 设置序列号
    通过X509_set_serialNumber方法实现;
  4. 从X509_REQ中导出使用者信息并设置到X509中
    通过X509_REQ_get_subject_name导出使用者信息;
    通过X509_set_subject_name方法设置到newcert中。
  5. 从CA根证书中导出使用者信息并设置到X509的颁发者项中
    通过X509_get_subject_name导出CA根证书中的使用者信息;
    通过X509_set_issuer_name方法设置到newcert的颁发者项中
  6. 从X509_REQ中导出公钥并设置到X509中
    通过X509_REQ_get_pubkey取得证书请求中的公钥;
    通过X509_REQ_verify方法对证书请求中签名值进行验证;(可选)
    通过X509_set_pubkey方法将公钥设置到newcert中;
  7. 设置证书有效期
    通过X509_gmtime_adj设置数值形式(以s为单位)的时间;
    或者通过X509_set1_notBefore及X509_set1_notAfter设置ASN1_TIME格式的时间;
  8. 设置证书扩展项信息(可选)
    扩展项信息根据实际需要来,可不设置。
  9. 设置签名值
    通过X509_sign方法及CA根私钥对newcert进行签名,并将签名值填入到newcert对象中。

完整代码


RSA *gen_RSA()
{RSA                *ret = NULL;RSA                *rsa = NULL;BIGNUM         *bn = NULL;unsigned long   e = RSA_F4;int             bits = 2048;if ((bn = BN_new()) == NULL){printf("BN_new err\n");return NULL;}if ((ret = RSA_new()) == NULL){printf("RSA_new err\n");goto END;}if (!BN_set_word(bn, e) || !RSA_generate_key_ex(ret, bits, bn, NULL)){printf("BN_set_word or RSA_generate_key_ex err\n");goto END;}ret = rsa;rsa = NULL;END:if (bn){BN_free(bn);}if (rsa){RSA_free(rsa);}return ret;
}EC_KEY *gen_EC_KEY()
{ EC_KEY *ret = NULL;///* 获取实现的椭圆曲线个数 *///EC_builtin_curve *curves = NULL;//int crv_len = 0;//int nid = 0;//crv_len = EC_get_builtin_curves(NULL, 0);//curves = (EC_builtin_curve *)malloc(sizeof(EC_builtin_curve) * crv_len);///* 获取椭圆曲线列表 *///EC_get_builtin_curves(curves, crv_len);//for (int i = 0; i < crv_len; i++) {// printf("***** %d *****\n", i);//  printf("nid = %d\n", curves[i].nid);//   printf("comment = %s\n", curves[i].comment);//}///*//nid=curves[0].nid;会有错误,原因是密钥太短//*////* 选取一种椭圆曲线 *///nid = curves[25].nid;//根据椭圆曲线参数 创建密钥结构if (!(ret = EC_KEY_new_by_curve_name(NID_sm2p256v1))) {printf("EC_KEY_new_by_curve_name err!\n");return NULL;}/* 生成密钥 */if (!(EC_KEY_generate_key(ret))){printf("EC_KEY_generate_key err.\n"); EC_KEY_free(ret);return NULL;}return ret;
}//
int Add_X509V3_extensions(X509 *cert, X509 * root, int nid, char *value)
{X509_EXTENSION *ex;X509V3_CTX ctx;/* This sets the 'context' of the extensions. *//* No configuration database *///  X509V3_set_ctx_nodb(&ctx);      /* Issuer and subject certs: both the target since it is self signed,* no request and no CRL*/X509V3_set_ctx(&ctx, root, cert, NULL, NULL, 0);ex = X509V3_EXT_conf_nid(NULL, &ctx, nid, value);if (!ex)return 0;X509_add_ext(cert, ex, -1);X509_EXTENSION_free(ex);return 1;
}X509_REQ *generate_X509_REQ()
{X509_REQ *ret = NULL;BIO          *outbio = NULL;EC_KEY       *eckey = NULL;X509_REQ     *x509_req = NULL;X509_NAME    *x509_name = NULL;EVP_PKEY     *pKey = NULL;long         lVer = 0L;const char   *szCountry = "CA";const char   *szProvince = "HUNAN";const char     *szCity = "ChangSha";const char  *szOrganization = "AHdms";const char     *szOrganizationUnit = "KFB";const char   *szCommon = "localhost";/* ---------------------------------------------------------- ** Create the Input/Output BIO's.                             ** ---------------------------------------------------------- */outbio = BIO_new(BIO_s_file());outbio = BIO_new_fp(stdout, BIO_NOCLOSE);// 1. generate EC keyeckey = gen_EC_KEY();if (eckey == NULL){BIO_printf(outbio, "Error generate EC_KEY\n");goto free_all;}// create x509_req objectx509_req = X509_REQ_new();if (x509_req == NULL) {BIO_printf(outbio, "Error creating new X509_REQ object\n");goto free_all;}// 2. setup version numberif (!X509_REQ_set_version(x509_req, lVer)){BIO_printf(outbio, "Error setting version to X509_REQ object\n");goto free_all;}char tmp_buf[512] = { '\0' };int tmpBufLen = sizeof(tmp_buf);//从CA证书中获取C,ST,O,OU// 3. set subject of x509 reqx509_name = X509_REQ_get_subject_name(x509_req);//Cif (!X509_NAME_add_entry_by_NID(x509_name, NID_countryName, MBSTRING_UTF8, (unsigned char *)szCountry, -1, -1, 0)) {BIO_printf(outbio, "Error adding entry [NID_countryName] to X509_REQ object\n");goto free_all;}//STif (!X509_NAME_add_entry_by_NID(x509_name, NID_stateOrProvinceName, MBSTRING_UTF8, (unsigned char *)szProvince, -1, -1, 0)) {BIO_printf(outbio, "Error adding entry [NID_stateOrProvinceName] to X509_REQ object\n");goto free_all;}//Lif (!X509_NAME_add_entry_by_NID(x509_name, NID_localityName, MBSTRING_UTF8, (unsigned char *)szCity, -1, -1, 0)) {BIO_printf(outbio, "Error adding entry [NID_localityName] to X509_REQ object\n");goto free_all;}//Oif (!X509_NAME_add_entry_by_NID(x509_name, NID_organizationName, MBSTRING_UTF8, (unsigned char *)szOrganization, -1, -1, 0)) {BIO_printf(outbio, "Error adding entry [NID_organizationName] to X509_REQ object\n");goto free_all;}//OU   OU在openssl.conf中默认是可选的if (!X509_NAME_add_entry_by_NID(x509_name, NID_organizationalUnitName, MBSTRING_UTF8, (unsigned char *)szOrganizationUnit, -1, -1, 0)) {BIO_printf(outbio, "Error adding entry [NID_organizationalUnitName] to X509_REQ object\n");goto free_all;}//CNif (!X509_NAME_add_entry_by_NID(x509_name, NID_commonName, MBSTRING_UTF8, (unsigned char *)szCommon, -1, -1, 0)) {BIO_printf(outbio, "Error adding entry [NID_commonName] to X509_REQ object\n");goto free_all;}// 4. set public key of x509 reqpKey = EVP_PKEY_new();if (!EVP_PKEY_assign_EC_KEY(pKey, eckey)) {BIO_printf(outbio, "Error EVP_PKEY_assign_EC_KEY operation\n");EC_KEY_free(eckey);goto free_all;}eckey = NULL;  // will be free eckey when EVP_PKEY_free(pKey)if (1 != (X509_REQ_set_pubkey(x509_req, pKey))){BIO_printf(outbio, "Error setting pubkey to X509_REQ object\n");goto free_all;}加入一组可选的扩展属性//STACK_OF(X509_EXTENSION) *extlist = sk_X509_EXTENSION_new_null();//X509_EXTENSION*ext = X509V3_EXT_conf(NULL, NULL, REQ_SUBJECT_ALT_NAME, value); //生成扩展对象//sk_X509_EXTENSION_push(extlist, ext);//X509_REQ_add_extensions(x509_req, extlist); // 加入扩展项目。// 5. set sign key of x509 reqint len = X509_REQ_sign(x509_req, pKey, EVP_sm3());  // return x509_req->signature->lengthif (len <= 0){unsigned long ulErr = ERR_get_error(); // 获取错误号char szErrMsg[1024] = { 0 };char *pTmp = NULL;pTmp = ERR_error_string(ulErr, szErrMsg); // 格式:error:errId:库:函数:原因printf("%s\n", szErrMsg);BIO_printf(outbio, "Error sign X509_REQ\n");goto free_all;}ret = x509_req;x509_req = NULL;free_all:BIO_free_all(outbio);if (pKey){EVP_PKEY_free(pKey);}if (x509_req){X509_REQ_free(x509_req);}return ret;
}X509 *generate_X509()
{X509 *ret = NULL;BIO               *outbio = NULL;X509_REQ         *certreq = NULL;ASN1_INTEGER *aserial = NULL;EVP_PKEY *ca_privkey, *req_pubkey;X509 *cacert = NULL;X509 *newcert = NULL;X509_NAME                    *name;EVP_MD                       const *digest = NULL;FILE                         *fp;long                         valid_secs = 31536000;/* ---------------------------------------------------------- ** Create the Input/Output BIO's.                             ** ---------------------------------------------------------- */outbio = BIO_new(BIO_s_file());outbio = BIO_new_fp(stdout, BIO_NOCLOSE);/* ---------------------------------------------------------- ** These function calls initialize openssl for correct work.  ** ---------------------------------------------------------- */OpenSSL_add_all_algorithms();ERR_load_BIO_strings();ERR_load_crypto_strings();/* -------------------------------------------------------- ** Load the signing CA Certificate file                    ** ---------------------------------------------------------*/if (!(fp = fopen(CACERT, "r"))) {BIO_printf(outbio, "Error reading CA cert file\n");return NULL;}if (!(cacert = PEM_read_X509(fp, NULL, NULL, NULL))) {BIO_printf(outbio, "Error loading CA cert into memory\n");fclose(fp);return NULL;}fclose(fp);/* -------------------------------------------------------- ** Import CA private key file for signing                   ** ---------------------------------------------------------*/ca_privkey = EVP_PKEY_new();if (!(fp = fopen(CAKEY, "r"))) {BIO_printf(outbio, "Error reading CA private key file\n");goto END;}if (!(ca_privkey = PEM_read_PrivateKey(fp, NULL, NULL, NULL))) {BIO_printf(outbio, "Error importing key content from file\n");fclose(fp);goto END;}fclose(fp);/* -------------------------------------------------------- ** generate x509_req                                        ** ---------------------------------------------------------*/if (!(certreq = generate_X509_REQ())){BIO_printf(outbio, "Error generate X509_REQ\n");goto END;}/* --------------------------------------------------------- ** Build Certificate with data from request                  ** ----------------------------------------------------------*/if (!(newcert = X509_new())) {BIO_printf(outbio, "Error creating new X509 object\n");goto END;}if (X509_set_version(newcert, 2) != 1) {BIO_printf(outbio, "Error setting certificate version\n");goto END;}/* --------------------------------------------------------- ** set the certificate serial number here                    ** If there is a problem, the value defaults to '0'          ** ----------------------------------------------------------*/aserial = ASN1_INTEGER_new();ASN1_INTEGER_set(aserial, 0);if (!X509_set_serialNumber(newcert, aserial)) {BIO_printf(outbio, "Error setting serial number of the certificate\n");goto END;}/* --------------------------------------------------------- ** Extract the subject name from the request                 ** ----------------------------------------------------------*/if (!(name = X509_REQ_get_subject_name(certreq)))BIO_printf(outbio, "Error getting subject from cert request\n");/* --------------------------------------------------------- ** Set the new certificate subject name                      ** ----------------------------------------------------------*/if (X509_set_subject_name(newcert, name) != 1) {BIO_printf(outbio, "Error setting subject name of certificate\n");goto END;}/* --------------------------------------------------------- ** Extract the subject name from the signing CA cert         ** ----------------------------------------------------------*/if (!(name = X509_get_subject_name(cacert))) {BIO_printf(outbio, "Error getting subject from CA certificate\n");goto END;}/* --------------------------------------------------------- ** Set the new certificate issuer name                       ** ----------------------------------------------------------*/if (X509_set_issuer_name(newcert, name) != 1) {BIO_printf(outbio, "Error setting issuer name of certificate\n");goto END;}/* --------------------------------------------------------- ** Extract the public key data from the request              ** ----------------------------------------------------------*/if (!(req_pubkey = X509_REQ_get_pubkey(certreq))) {BIO_printf(outbio, "Error unpacking public key from request\n");goto END;}/* --------------------------------------------------------- ** Optionally: Use the public key to verify the signature    ** ----------------------------------------------------------*/if (X509_REQ_verify(certreq, req_pubkey) != 1) {BIO_printf(outbio, "Error verifying signature on request\n");goto END;}/* --------------------------------------------------------- ** Set the new certificate public key                        ** ----------------------------------------------------------*/if (X509_set_pubkey(newcert, req_pubkey) != 1) {BIO_printf(outbio, "Error setting public key of certificate\n");goto END;}/* ---------------------------------------------------------- ** Set X509V3 start date (now) and expiration date (+365 days)** -----------------------------------------------------------*/if (!(X509_gmtime_adj(X509_get_notBefore(newcert), 0))) {BIO_printf(outbio, "Error setting start time\n");goto END;}if (!(X509_gmtime_adj(X509_get_notAfter(newcert), valid_secs))) {BIO_printf(outbio, "Error setting expiration time\n");goto END;}/* ----------------------------------------------------------- ** Add X509V3 extensions                                       ** ------------------------------------------------------------*///使用者密钥标识Add_X509V3_extensions(newcert, cacert, NID_subject_key_identifier, "hash");//颁发者密钥标识Add_X509V3_extensions(newcert, cacert, NID_authority_key_identifier, "keyid,issuer");//密钥用途Add_X509V3_extensions(newcert, cacert, NID_key_usage, "Digital Signature, Key Encipherment, Data Encipherment");//增强型密钥用途Add_X509V3_extensions(newcert, cacert, NID_ext_key_usage, "critical,clientAuth");/* ----------------------------------------------------------- ** Set digest type, sign new certificate with CA's private key ** ------------------------------------------------------------*/digest = EVP_sm3();if (!X509_sign(newcert, ca_privkey, digest)) {BIO_printf(outbio, "Error signing the new certificate\n");goto END;}/* ------------------------------------------------------------ **  print the certificate                                       ** -------------------------------------------------------------*/if (!PEM_write_bio_X509(outbio, newcert)) {BIO_printf(outbio, "Error printing the signed certificate\n");}ret = newcert;newcert = NULL;END:/* ---------------------------------------------------------- ** Free up all structures                                     ** ---------------------------------------------------------- */EVP_PKEY_free(ca_privkey);X509_REQ_free(certreq);ASN1_INTEGER_free(aserial);X509_free(newcert);BIO_free_all(outbio);return ret;
}int _tmain(int argc, _TCHAR* argv[])
{X509 *cert = generate_X509();if (cert != NULL){BIO *out = NULL;int ret = 0;out = BIO_new_file("./newcert.cer", "w");ret = PEM_write_bio_X509(out, cert);BIO_free_all(out);}getchar();return 0;
}

运行结果


OK, 打完收工
————————————————————————————————
我不休息我还能学 ⊂(‘ω’⊂ )))Σ≡=─༄༅༄༅༄༅༄༅༄༅

gmssl编程之签发X509证书相关推荐

  1. gmssl编程之X509证书解析

    gmssl编程之X509证书解析 引言 X509语法结构 基本项 证书版本号 证书序列号 证书颁发者 证书使用者 证书有效期 证书公钥 扩展项 基本约束 密钥用途 增强型密钥用途 颁发者标识 使用者标 ...

  2. (转)创建X509证书,并获取证书密钥的一点研究

    创建X509证书,并获取证书密钥的一点研究 作者:肖波 个人博客:http://blog.csdn.net/eaglet ; http://www.cnblogs.com/eaglet 2007/7 ...

  3. php获取x509证书信息,创建X509证书,并获取证书密钥的一点研究

    作者:肖波 背景 服务器SSL数字证书和客户端单位数字证书的格式遵循X.509标准.X.509是由国际电信联盟(ITU-T)制定的数字证书标准.为了提供公用网络用户目录信息服务,ITU于1988年制定 ...

  4. 【系统安全】X509证书介绍

    概述 Windows Communication Foundation (WCF) 是 Microsoft 为构建面向服务的应用程序而提供的统一编程模型(摘自MSDN),在分布式环境下的安全问题尤为重 ...

  5. WCF安全之X509证书

    概述 Windows Communication Foundation (WCF) 是 Microsoft 为构建面向服务的应用程序而提供的统一编程模型(摘自MSDN),在分布式环境下的安全问题尤为重 ...

  6. X509证书认证流程介绍

    X509证书介绍 X.509 是由国际电信联盟(ITU-T)制定的数字证书标准,相信这是人尽皆知的了,目前X.509证书据我所知有三个版本,.net中使用的是x.509-2,X.509-2 版引入了主 ...

  7. 关于X509证书和密钥的概念

    证书概述 证书主要包括颁发者和被办法者的信息,以及被颁发者的公钥,和CA机构对这些信息的认证, 主要内容: **版本** 识别用于该证书的 X.509 标准的版本,这可以影响证书中所能指定的信息.迄今 ...

  8. http系列---OpenSSL生成根证书CA及签发子证书

    文章目录 1. 前言 2. 修改OpenSSL的配置 3. 生成根证书 4. 用根证书签发server端证书 5. 用根证书签发client端证书 6. 导出证书 7. 附项目证书目录 1. 前言 系 ...

  9. OpenSSL生成根证书CA及签发子证书

    转自:https://yq.aliyun.com/articles/40398 摘要: 系统:CentOS7 32位 目标:使用OpenSSL生成一个CA根证书,并用这个根证书颁发两个子证书serve ...

  10. 搭建CA并签发数字证书

    Openssl完成私有CA yum install openssl openssl-devel -y   ---完成私有软件包的安装 修改openssl.cnf配置文件 [root@CA ~]# vi ...

最新文章

  1. linux kernel随机数
  2. 海报推广神器:活码加多级加密跳转防封双重保护
  3. ajax实现简单的点击左侧菜单,右侧加载不同网页
  4. java jsf_使用Java和JSF构建一个简单的CRUD应用
  5. 用Hamcrest验证DateTime和日期
  6. 操作系统上机题目(多进程2)
  7. java 6789的10000次方,用MSSQL计算2的10000次方
  8. 一些实用但不为人知的Unix命令
  9. H3CNE实验(一)静态路由
  10. 智能陈桥五笔输入法 for linux,科技教程:在Linux下安装陈桥五笔输入法
  11. 亚马逊注册成功,需要的进一步的设置一:税务信息设置
  12. java -- 百度API 接口使用
  13. CSS新招式,临时记一下
  14. ipad批量删除图片
  15. 最标准的html模板
  16. 某宝滑块 x82y解决方法、x5sec
  17. 一款好看的404页面代码 | 滚动的404
  18. flatten层的作用
  19. 五子棋(C++面向对象实现)
  20. 美国访学J类签证费涨价15%|5月30日生效

热门文章

  1. biopython安装_python下如何安装biopython
  2. R语言数据分析报告 鲍鱼年龄预测
  3. ENSP之STP协议基本配置教程
  4. 计算机科学与技术的学士服是什么颜色的,学士服颜色分类 各色学士服都有什么讲究...
  5. vue 创建项目使用npm还是yarn
  6. c语言自动变量全局变量,C语言全局变量的一些简单介绍
  7. C语言全局变量,局部变量,静态局部变量的区分
  8. 关于KERNEL_SECURITY_CHECK_FAILURE蓝屏(BSOD)0x00000139错误解决思路
  9. android 视频播放器m3u8,Android 播放/下载M3U8视频(转)
  10. RescuePRO Deluxe(闪迪数据恢复) v6.0.3.1中文破解版