opensll 证书及CRL生成

证书生成

通用命令

查看摘要算法列表
openssl list -digest-algorithms
openssl crl -in crl.pem -text 查看信息
openssl x509 -in cert.pem -text

1、sm2证书生成

生成私钥

openssl ecparam -genkey -name SM2 -out private_key.pem

(RSA 公钥的字节结构可以使用标准的SubjectPublicKeyInfo(SPKI)格式来表示。这个格式由一系列的 DER 编码的 ASN.1 结构组成)
用私钥生成公钥

openssl ec -in private_key.pem -pubout -out sm2PubKey.pem

生成证书
1)生成配置文件cert.conf

[ req ]
default_bits = 256
prompt = no
default_md = sm3
distinguished_name = dn
[ dn ]
C = CN
ST = Beijing
L = Beijing
O = Your Organization
OU = Your Unit
CN = Your Common Name

default_bits:指定生成的密钥长度,默认为 256 位。在 SM2 中,通常使用 256 位曲线。
prompt:设置是否显示提示信息。在此示例中,设置为 no,即不显示提示信息。
default_md:指定摘要算法类型,默认为 SM3。SM3 是中国国家标准的密码杂凑算法,用于计算数字签名和生成证书请求。
distinguished_name:指定证书主题的字段。
C:国家名(Country),两个字母的国家代码。例如,CN 表示中国。
ST:省/自治区/直辖市名(State/Province/Region)。
L:地区/城市名(Locality/City)。
O:组织名(Organization)。
OU:组织单位名(Organizational Unit)。
CN:通用名称(Common Name),即证书的主题。
2)用私钥和配置文件生成证书
openssl req -new -key private_key.pem -out cert_req.csr -config cert.conf

不使用配置文件的时候

openssl req -x509 -key private_key.pem -out certificate.pem -days 365 -config <(echo "[ req ]\ndefault_bits = 256\nprompt = no\ndefault_md = SM3\ndistinguished_name = dn\n[ dn ]\nC = CN\nST = SC\nL = Chengdu\nO = zdxlz\nOU = zdxlz\nCN = Test")

易错点

140345702581568:error:100C508A:elliptic curve routines:pkey_ec_ctrl:invalid digest type:../crypto/ec/ec_pmeth.c:331

openssl 1.1.1版本后支持国密算法(去掉default_md = sm3)

2、CRL生成

介绍
包含撤销的证书列表的签名数据结构,类似于信用卡的黑名单,公布某些数字证书无效。
CRL可以分为完全CRL和增量CRL。在完全CRL中包含了所有的被撤销证书信息,增量CRL由一系列的CRL来表明被撤销的证书信息,它每次发布的CRL是对前面发布CRL的增量扩充。
基本的CRL信息有:被撤销证书序列号、撤销时间、撤销原因、签名者以及CRL签名等信息。
基于CRL的验证是一种不严格的证书认证。CRL能证明在CRL中被撤销的证书是无效的。但是,它不能给出不在CRL中的证书的状态。如果执行严格的认证,需要采用在线方式进行认证,即OCSP认证。

结构体

typedef struct X509_revoked_st//一个被撤销证书的信息
{
    ASN1_INTEGER *serialNumber;//被撤销证书的序列号;
    ASN1_TIME *revocationDate;//撤销时间
    STACK_OF(X509_EXTENSION) *extensions;//扩展项,可选
    int sequence;//顺序号,用于排序,表示当前被撤销证书信息在crl中的顺序
} X509_REVOKED;
//主体信息
typedef struct X509_crl_info_st
{
    ASN1_INTEGER *version;//crl版本
    X509_ALGOR *sig_alg;//crl签名法
    X509_NAME *issuer;//签发者信息
    ASN1_TIME *lastUpdate;//上次更新时间
    ASN1_TIME *nextUpdate;//下次更新时间
    STACK_OF(X509_REVOKED) *revoked;//被撤销证书信息
    STACK_OF(X509_EXTENSION) *extensions;//
    ASN1_ENCODING enc;//
} X509_CRL_INFO;
// 完整crl数据结构
struct X509_crl_st
{
    X509_CRL_INFO *crl;//crl信息主体
    X509_ALGOR *sig_alg;//签名算法,与X509_CRL_INFO中的一致
    ASN1_BIT_STRING *signature;//签名值
    int references;//引用
} ;

函数

int X509_CRL_add0_revoked(X509_CRL *crl, X509_REVOKED *rev)//添加一个被撤销证书的信息
int X509_CRL_print(BIO *bp,X509_CRL *x)//打印crl内容到BIO中
int X509_CRL_print_fp(FILE *fp, X509_CRL *x)//将crl的内容输出到fp中,此函数调用了X509_CRL_print
int X509_CRL_set_issuer_name(X509_CRL *x, X509_NAME *name)//设置crl的颁发者
int X509_CRL_set_lastUpdate(X509_CRL *x, ASN1_TIME *tm)//设置crl上次发布时间
int X509_CRL_set_nextUpdate(X509_CRL *x, ASN1_TIME *tm)//设置crl下次发布时间
int X509_CRL_set_version(X509_CRL *x, long version)//设置crl版本
int X509_CRL_sign(X509_CRL *x, EVP_PKEY *pkey, const EVP_MD *md)//对crl进行签名,pkey为私钥,md为摘要算法,结果存放在x-> signature中
int X509_CRL_sort(X509_CRL *c)//根据证书序列号对crl排序,此函数实现采用了堆栈排序,堆栈的比较函数为X509_REVOKED_cmp(crypto/asn1/x_crl.c)
//添加CRL扩展,nid为要添加的扩展标识,value为被添加的具体扩展项的内部数据结构地址,crit表明是否为关键扩展,flags表明何种操作。此函数调用X509V3_add1_i2d函数
int X509_CRL_add1_ext_i2d(X509_CRL *x, int nid, void *value, int crit,  unsigned long flags)
int X509_CRL_add_ext(X509_CRL *x, X509_EXTENSION *ex, int loc)//添加扩展项到指定堆栈位置,此函数调用X509v3_add_ext,进行堆栈插入操作。
int X509_CRL_cmp(const X509_CRL *a, const X509_CRL *b)//CRL比较,此函数调用X509_NAME_cmp,只比较颁发者的名字是否相同。
X509_EXTENSION *X509_CRL_delete_ext(X509_CRL *x, int loc)//删除CRL扩展项堆栈中的某一项,loc指定被删除项在堆栈中的位置
int X509_CRL_digest(const X509_CRL *data, const EVP_MD *type,unsigned char *md, unsigned int *len)//CRL摘要,本函数对X509_CRL进行摘要,type指定摘要算法,摘要结果存放在md中,len表明摘要结果长度
X509_CRL *X509_CRL_dup(X509_CRL *crl);
void *X509_CRL_get_ext_d2i(X509_CRL *x, int nid, int *crit, int *idx)//CRL中的获取扩展项,此函数用于获取crl中指定扩展项的内部数据结构,返回值为具体的扩展项数据结构地址,nid为扩展项标识,它调用了X509V3_get_d2i函数。
int X509_CRL_get_ext_by_critical(X509_CRL *x, int crit, int lastpos)
int X509_CRL_get_ext_by_NID(X509_CRL *x, int nid, int lastpos)
int X509_CRL_get_ext_by_OBJ(X509_CRL *x, ASN1_OBJECT *obj, int lastpos)
int X509_CRL_get_ext_count(X509_CRL *x)//获取crl中扩展项的个数
int X509_CRL_verify(X509_CRL *a, EVP_PKEY *r)//验证CRL。EVP_PKEY结构r中需要给出公钥(与签名相对应的)验证内容:签名,签发者证书,验证crl完整性
long X509_CRL_get_version(const X509_CRL *crl);//获取版本号
X509_NAME *X509_CRL_get_issuer(const X509_CRL *crl);//获取颁发者名称
char *X509_NAME_oneline(const X509_NAME *a, char *buf, int size);//将 X.509 证书的主题或颁发者(发行者)的名称转换为可打印的字符串形式
ASN1_TIME *X509_CRL_get_lastUpdate(X509_CRL *crl)
ASN1_TIME *X509_CRL_get_nextUpdate(X509_CRL *crl)
STACK_OF(X509_REVOKED) *X509_CRL_get_REVOKED(X509_CRL *crl);//用于获取撤销证书列表,该函数返回一个 STACK_OF(X509_REVOKED) 结构的指针,即撤销证书的列表。STACK_OF(X509_REVOKED) 是 OpenSSL 提供的一种堆栈数据结构,用于存储多个 X509_REVOKED(被撤销的证书)对象
static inline int sk_X509_REVOKED_num(const stack_st_X509_REVOKED *sk)//获取 STACK_OF(X509_REVOKED) 结构中元素数量的函数
static inline x509_revoked_st *sk_X509_REVOKED_value(const stack_st_X509_REVOKED *sk, int idx)//获取指定索引位置的元素的函数,返回撤销证书列表中指定索引位置的被撤销证书对象
const ASN1_INTEGER *X509_REVOKED_get0_serialNumber(const X509_REVOKED *x);// 获取被撤销证书的序列号
unsigned char *ASN1_STRING_data(ASN1_STRING *x)//获取 ASN1_STRING 结构中数据的指针	
int ASN1_STRING_length(const ASN1_STRING *x);//用于获取 ASN1_STRING 结构中数据的长度
ASN1_INTEGER *X509_get_serialNumber(X509 *x);//获取证书序列号

1、配置文件crl.conf

[ca]
default_ca = CA_default  	CA 的默认设置

[CA_default]
dir = /path/to/CA/folder
crl_dir = $dir/crl  		指定存放 CRL 文件的文件夹路径
database = $dir/index.txt 	 指定用于存储证书签发和撤销信息的数据库文件(索引文件)路径。该文件记录了已颁发的证书和被撤销的证书的相关信息。
serial = $dir/serial		指定一个用于跟踪证书序列号的文件路径。每当颁发一个新证书时,该文件中的序列号将递增。
private_key = /path/to/ca.key	指定 CA 的私钥文件路径。CRL 是由 CA 使用其私钥签名的
certificate = /path/to/ca.crt	指定 CA 的证书文件路径。该证书将包含在生成的 CRL 中。
crlnumber = $dir/crlnumber		指定存储 CRL 号码的文件路径。每次生成 CRL 时,该号码将递增。
default_crl_days = 30			指定生成的 CRL 的默认有效期(以天为单位)。在生成 CRL 时可以覆盖此值.

[crl_ext] 						CRL 扩展字段的部分
issuerAltName=issuer:copy		指定 CRL 的颁发者别名,以及将从 CA 证书中拷贝的颁发者信息。
authorityKeyIdentifier=keyid:always	声明 CRL 中的颁发者密钥标识符(AKI)字段始终包含在 CRL 中

[req]	
default_bits = 2048				
prompt = no						控制是否在生成证书请求时提示用户输入信息。将其设置为 "no" 可以禁用提示,使用默认值填充
default_md = sha256				指定证书签名所使用的消息摘要算法,默认为 SHA256
distinguished_name = dn			定义证书主题(Subject)的字段信息

[dn]
CN = Your CA Name				
部分参数可忽略
openssl ca -config crl.conf -gencrl -out crl.pem

测试程序

#include "x509.h"
#include "pem.h"
#include "ossl_typ.h"
#include "../third-Part/openssl/include/crypto/x509.h"
#include "../third-Part/openssl/include/crypto/asn1.h"
#include "Debug.h"
#include "parseCRL.h"
#include "parseCertTest.h"

void parseCRL(char *fileStr, X509 *cert)
{
    FILE *crlFile = fopen(fileStr, "r");
    if(crlFile == NULL){
        debugPrintf("crlFile fopen err \n");
        return;
    }

    X509_CRL *crl = NULL;

    //PEM 文件的指针, 指向 X509_CRL 结构体指针的指针(读取到才有), 指向密码回调函数的指针,用户数据指针
    crl = PEM_read_X509_CRL(crlFile, &crl, NULL, NULL);
    if(crl == NULL){
        debugPrintf("PEM_read_X509_CRL err \n");
        return;
    }
    //DER格式读取
//    BIO *crl_bio = BIO_new_file(fileStr, "r");
//    crl = d2i_X509_CRL_bio(crl_bio, NULL);
//    BIO_free(crl_bio);

    //获取版本号
    long version = X509_CRL_get_version(crl);
    debugPrintf("version %ld\n", version);

    X509_NAME *issuer_name = X509_CRL_get_issuer(crl);
    char *issuer_str = X509_NAME_oneline(issuer_name, NULL, 0);
    debugPrintf("Issuer: %s\n", issuer_str);

    X509_ALGOR *sig_alg = &crl->sig_alg;
    //将一个对象的数字标识符(NID)转换为对应的短名(SN)
    const char *alg_name = OBJ_nid2sn(sig_alg->algorithm->nid);
    debugPrintf("Signature Algorithm: %s\n", alg_name);

    // 获取上次更新时间和下次更新时间
    ASN1_TIME *last_update = X509_CRL_get_lastUpdate(crl);
    ASN1_TIME *next_update = X509_CRL_get_nextUpdate(crl);
    printASN1Time(last_update);
    printASN1Time(next_update);

// 获取撤销列表,栈
    STACK_OF(X509_REVOKED) *revoked_list = X509_CRL_get_REVOKED(crl);
    int num_revoked = 0;
    if (revoked_list != NULL) {
        //获取revoked_list中的数量
        num_revoked = sk_X509_REVOKED_num(revoked_list);
        // 遍历撤销的证书列表
    }

    X509_REVOKED *revoked_cert;
    ASN1_INTEGER *serial_number;
    for (int i = 0; i < num_revoked; i++) {
        //获取指定索引位置的元素的函数,返回撤销证书列表中指定索引位置的被撤销证书对象
        revoked_cert = sk_X509_REVOKED_value(revoked_list, i);
        // 获取被撤销证书的序列号
        serial_number = X509_REVOKED_get0_serialNumber(revoked_cert);
        // 打印序列号,获取 ASN1_STRING 结构中数据的指针
        char *hex_serial = (char *)ASN1_STRING_data(serial_number);
        int hex_serial_len = ASN1_STRING_length(serial_number);
        for (int j = 0; j < hex_serial_len; j++) {
            printf("%02X", hex_serial[j]);
        }
        printf("\n");
    }
//获取证书序列号,验证吊销列表
    int isRevoked = 0;
    ASN1_INTEGER *cert_serial = X509_get_serialNumber(cert);
    for (int i = 0; i < num_revoked; ++i) {
        revoked_cert = sk_X509_REVOKED_value(revoked_list, i);
        serial_number = X509_REVOKED_get0_serialNumber(revoked_cert);
        // 比较证书的序列号
        if (ASN1_INTEGER_cmp(cert_serial, serial_number) == 0) {
            isRevoked = 1;
            break;
        }
    }
    debugPrintf("check serialNumber \n");
}

说明
结合证书校验代码,测试用该证书生成的crl(如果获取的吊销列表为空,注意crl配置文件里面的证书吊销的序列号是否有)。
DER格式和PEM格式略有不同

static void _getASN1Time(ASN1_TIME* Time, char *rcvTime)
{
    //BIO是一种用于进行输入/输出操作的抽象类型。它提供了一种统一的接口,可用于在不同的数据源和目标之间进行读写操作。
    BIO* bio = BIO_new(BIO_s_mem());
    if (bio == NULL) {
        return;
    }
    if (ASN1_TIME_print(bio, Time) <= 0) {
        BIO_free(bio);
        return;
    }
    char buf[1024];
    //从bio对象中读取结果
    int len = BIO_read(bio, buf, sizeof(buf) - 1);
    if (len <= 0) {
        BIO_free(bio);
        return;
    }
    buf[len] = '\0';
    memcpy(rcvTime, buf, len);
    BIO_free(bio);
}
/**
 * 从字节流或字节数组读取证书结构存储X509
 * @param buffer    证书数据
 * @param size      大小
 * @param crl       返回的证书
 * @return  0成功
 */
int crlInit(unsigned char* buffer, int size, X509_CRL **crl)
{
    TRACE_INFO("crlInit");
    TRACE_HEX_DUMP("buffer", buffer, size);
    //der格式
    X509_CRL* _crl = d2i_X509_CRL(NULL, &buffer, size);
    if (_crl != NULL)
    {
        TRACE_INFO("d2i_X509_CRL err");
    } else{
        BIO *bio = BIO_new_mem_buf(buffer, -1);
        //pem格式
        _crl = PEM_read_bio_X509_CRL(bio, NULL,NULL,NULL);
        if(_crl ==NULL){
            TRACE_INFO("read crl err");
            return -1;
        }
    }
    *crl = _crl;//值传递
    return 0;
}
/**
 * 获取基本crl基本信息
 * @param crl   输入crl
 * @param x509_CRL_INFO 返回的结构体信息
 * @return
 */
int crlGetInfo(X509_CRL *crl, _X509_CRL_INFO *x509_CRL_INFO)
{
    if(x509_CRL_INFO == NULL)
    {
        TRACE_INFO("x509_CRL_INFO NULL");
        return -1;
    }
    x509_CRL_INFO->version = X509_CRL_get_version(crl) + 1;
    if(X509_CRL_get_issuer(crl) != NULL){
        X509_NAME *issuer_name = X509_CRL_get_issuer(crl);
        char *issuer_str = X509_NAME_oneline(issuer_name, NULL, 0);
        x509_CRL_INFO->issuer_name_len = (int)strlen(issuer_str);
        x509_CRL_INFO->issuer_name = issuer_str;
    }

    X509_ALGOR *sig_alg = &crl->sig_alg;
    //将一个对象的数字标识符(NID)转换为对应的短名(SN)
    const char *alg_name = OBJ_nid2sn(sig_alg->algorithm->nid);
    memcpy(x509_CRL_INFO->alg_name, alg_name, strlen(alg_name));

    ASN1_TIME *thisUpdate = X509_CRL_get_lastUpdate(crl);
    ASN1_TIME *nextUpdate = X509_CRL_get_nextUpdate(crl);
    _getASN1Time(thisUpdate,x509_CRL_INFO->thisUpdate);
    _getASN1Time(nextUpdate,x509_CRL_INFO->nextUpdate);

    if(crl->signature.data != NULL)
    {
        x509_CRL_INFO->signLen = crl->signature.length;
        x509_CRL_INFO->signDate = crl->signature.data;
    }
    if(crl->crl_number != NULL)
    {
        x509_CRL_INFO->crlNumber_len = crl->crl_number->length;
        x509_CRL_INFO->crlNumber = crl->crl_number->data;
    }
    if(crl->crl_number != NULL)
    {
        x509_CRL_INFO->base_crl_number_len = crl->base_crl_number->length;
        x509_CRL_INFO->base_crl_number = crl->base_crl_number->data;
    }
    return 0;
}
/**
 *
 * @param crl  输入 x509 crl
 * @param cert_serial   输入需要校验证书的序列号
 * @return  1 证书合法不在吊销列表,0校验失败,证书不合法
 */
int crlCheckRevokedList(X509_CRL *crl, ASN1_INTEGER *cert_serial)
{
    STACK_OF(X509_REVOKED) *revoked_list = X509_CRL_get_REVOKED(crl);
    int num_revoked = 0;
    if (revoked_list != NULL){
        num_revoked = sk_X509_REVOKED_num(revoked_list);
    } else{
        TRACE_INFO("X509_CRL_get_REVOKED NULL");
        return 1;
    }

    X509_REVOKED *revoked_cert;
    ASN1_INTEGER *serial_number;
    for (int i = 0; i < num_revoked; i++) {
        //获取指定索引位置的元素的函数,返回撤销证书列表中指定索引位置的被撤销证书对象
        revoked_cert = sk_X509_REVOKED_value(revoked_list, i);
        // 获取被撤销证书的序列号
        serial_number = X509_REVOKED_get0_serialNumber(revoked_cert);

        char *hex_serial = (char *)ASN1_STRING_data(serial_number);
        int hex_serial_len = ASN1_STRING_length(serial_number);
        TRACE_HEX_DUMP("hex_serial", hex_serial, hex_serial_len);
        //对比序列号是否是吊销列表
        if (ASN1_INTEGER_cmp(cert_serial, serial_number) == 0) {
            return 0;//查出吊销列表
        }
    }
    return 1;
}
  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值