3.4 签名验签
签名验签的几种流程
假设EVP_PKEY *pkey已经生成完毕。
签名和验签各有几套流程:
- 基于EVP_PKEY_CTX的流程。待签名的数据为SM2的A1和A2步的计算结果,必须是32字节,所以此流程内不再做签名的A1和A2步。验签同理。
- 基于EVP_MD_CTX的流程1。待签名的数据为SM2的A1步的计算结果,可以是任意长度,所以此流程内不再做签名的A1步,从A2步开始执行。验签同理。
- 基于EVP_MD_CTX的流程2。待签名的数据为SM2的A1步的计算结果,可以是任意长度,所以此流程内不再做签名的A1步,从A2步开始执行。验签同理。
SM2_sign和SM2_verify流程图
几种签名验签流程都要涉及SM2_sign和SM2_verify,这两个函数又存在层层调用,所以下面列出SM2_sign和SM2_verify的流程图。
图3.1 SM2_sign和SM2_verify流程图
基于EVP_PKEY_CTX的流程
表3.2基于EVP_PKEY_CTX的签名验签使用步骤
签名 |
验签 | |
1 |
pkctx = EVP_PKEY_CTX_new( pkey, NULL)) |
pkctx = EVP_PKEY_CTX_new( pkey, NULL)) |
2 |
EVP_PKEY_sign_init(pkctx) |
EVP_PKEY_verify_init(pkctx) |
3 |
EVP_PKEY_CTX_set_ec_sign_type( pkctx, NID_sm_scheme) |
EVP_PKEY_CTX_set_ec_sign_type( pkctx, NID_sm_scheme) |
4 |
EVP_PKEY_sign( pkctx, sig, &siglen, dgst, dgstlen) |
EVP_PKEY_verify( pkctx, sig, siglen, dgst, dgstlen) |
5 |
EVP_PKEY_CTX_free(pkctx); |
EVP_PKEY_CTX_free(pkctx); |
其中
- EVP_PKEY_CTX_new和EVP_PKEY_CTX_free函数的使用说明请参见3.2 EVP_PKEY_CTX和EVP_PKEY操作。
- 剩余函数的说明见后。
基于EVP_PKEY_CTX的签名验签流程图如下。
图3.2 基于EVP_PKEY_CTX的签名验签流程图
以上流程图中的SM2_sign和SM2_verify的流程图见SM2_sign和SM2_verify的流程图。
基于EVP_MD_CTX的流程1
表3.3基于EVP_MD_CTX的流程1的签名验签使用步骤
签名 |
验签 | |
1 |
mdctx = EVP_MD_CTX_create() |
mdctx = EVP_MD_CTX_create() |
2 |
EVP_SignInit_ex(mdctx, md, NULL) |
EVP_VerifyInit_ex(mdctx,md,NULL) |
3 |
EVP_SignUpdate(mdctx, msg, mlen) |
EVP_VerifyUpdate(mdctx, msg,mlen) |
4 |
EVP_SignFinal(mdctx,sig,&slen,pkey) |
EVP_VerifyFinal(mdctx,sig,slen,pkey) |
5 |
EVP_MD_CTX_destroy(mdctx) |
EVP_MD_CTX_destroy(mdctx) |
其中
- EVP_MD_CTX_create和EVP_MD_CTX_destroy函数的使用说明请参见2.2 EVP_MD_CTX操作。
- 剩余函数的说明见后。
基于EVP_MD_CTX的流程1的签名验签流程图如下。
图3.3 基于EVP_MD_CTX的流程1的签名验签流程图
以上流程图中的SM2_sign和SM2_verify的流程图见SM2_sign和SM2_verify的流程图。
基于EVP_MD_CTX的流程2
表3.4基于EVP_MD_CTX的流程2的签名验签使用步骤
签名 |
验签 | |
1 |
mdctx = EVP_MD_CTX_create(); |
mdctx = EVP_MD_CTX_create(); |
2 |
EVP_DigestSignInit( mdctx, &pkctx, md, NULL, pkey); |
EVP_DigestVerifyInit( mdctx, &pkctx, md, NULL, pkey); |
3 |
EVP_PKEY_CTX_set_ec_sign_type( pkctx, type); |
EVP_PKEY_CTX_set_ec_sign_type( pkctx, type); |
4 |
EVP_DigestSignUpdate( mdctx, msg, msglen); |
EVP_DigestVerifyUpdate( mdctx, msg, msglen); |
5 |
EVP_DigestSignFinal( mdctx, sig, &siglen); |
EVP_DigestVerifyFinal( mdctx, sig, siglen); |
6 |
EVP_MD_CTX_destroy(mdctx); |
EVP_MD_CTX_destroy(mdctx); |
其中
- EVP_MD_CTX_create和EVP_MD_CTX_destroy函数的使用说明请参见2.2 EVP_MD_CTX操作。
- 剩余函数的说明见后。
基于EVP_MD_CTX的流程2的签名验签流程图如下。
图3.4 基于EVP_MD_CTX的流程2的签名验签流程图
以上流程图中的SM2_sign和SM2_verify的流程图见SM2_sign和SM2_verify的流程图。
EVP_PKEY_sign_init
函 数 名: int EVP_PKEY_sign_init(EVP_PKEY_CTX *ctx);
功能描述: 签名之前的EVP_PKEY_CTX初始化
说 明: -
参数说明:
ctx (in/out) EVP_PKEY_CTX
返 回 值: 1[成功],<=0[失败]
EVP_PKEY_CTX_set_ec_sign_type
函 数 名: #define EVP_PKEY_CTX_set_ec_sign_type(ctx, type)
功能描述: 利用曲线类型设置签名/验签的曲线参数
说 明: -
- 签名和验签之前都要调用该函数
- 便于后续利用对应的曲线参数生成密钥
- SM2的type = NID_sm_scheme
- 调用EVP_PKEY_CTX_ctrl
参数说明:
ctx (in/out) EVP_PKEY_CTX
type (in) 类型
返 回 值: 1[成功],<=0[失败]
EVP_PKEY_sign
函 数 名: int EVP_PKEY_sign(EVP_PKEY_CTX *ctx, unsigned char *sig, size_t *siglen, const unsigned char *tbs, size_t tbslen);
功能描述: 签名
说 明:
1. 需一次性将待签名数据送完
2. 返回的签名值sig是对SM2签名数据(r,s)做i2d_ECDSA_SIG的结果。
参数说明:
ctx (in/out) EVP_PKEY_CTX
sig (out) 签名(字符型)
siglen (out) 签名长度
tbs (in) 待签名数据
tbslen (in) 待签名数据长度
返 回 值: 1[成功],<=0[失败]
EVP_PKEY_verify_init
函 数 名: int EVP_PKEY_verify_init(EVP_PKEY_CTX *ctx);
功能描述: 验签之前的EVP_PKEY_CTX初始化
说 明: -
参数说明:
ctx (in/out) EVP_PKEY_CTX
返 回 值: 1[成功],<=0[失败]
EVP_PKEY_verify
函 数 名: int EVP_PKEY_verify(EVP_PKEY_CTX *ctx, const unsigned char *sig, size_t siglen, const unsigned char *tbs, size_t tbslen);
功能描述: 验签
说 明:
1. 需一次性将待验签数据送完
2. 函数内做d2i_ECDSA_SIG将签名还原为SM2签名数据(r, s)。
参数说明:
ctx (in/out) EVP_PKEY_CTX
sig (in) 签名(字符型)
siglen (in) 签名长度
tbs (in) 待验签数据
tbslen (in) 待验签数据长度
返 回 值: 1[验签通过],<=0[验签失败]
EVP_SignInit
函 数 名: # define EVP_SignInit(ctx, md)
功能描述: 签名之前的EVP_MD_CTX初始化
说 明: # define EVP_SignInit(ctx, md) EVP_DigestInit(ctx, md)
参数说明:
ctx (in/out) EVP_MD_CTX数据
md (in) 杂凑算法,如EVP_sm3()
返 回 值: 1[成功],<=0[失败]
EVP_SignInit_ex
函 数 名: # define EVP_SignInit_ex(ctx,md,eng)
功能描述: 签名之前的EVP_MD_CTX初始化
说 明: # define EVP_SignInit_ex(ctx,md,eng) EVP_DigestInit_ex(ctx,md,eng)
参数说明:
ctx (in/out) EVP_MD_CTX数据
md (in) 杂凑算法,如EVP_sm3()
eng (in) 引擎
返 回 值: 1[成功],<=0[失败]
EVP_SignUpdate
函 数 名: # define EVP_SignUpdate(ctx,m,mlen)
功能描述: 设置签名数据
说 明: -数据可分多次送入
# define EVP_SignUpdate(ctx,m,mlen) EVP_DigestUpdate(ctx,m,mlen)
参数说明:
ctx (in/out) EVP_MD_CTX数据
m (in) 数据
mlen (in) 数据长度
返 回 值: 1[成功],<=0[失败]
EVP_SignFinal
函 数 名: int EVP_SignFinal(EVP_MD_CTX *ctx, unsigned char *sig, unsigned int *slen, EVP_PKEY *pkey);
功能描述: 签名结束获取签名值
说 明: 1. 返回的签名值sig是对SM2签名数据(r,s)做i2d_ECDSA_SIG的结果。
参数说明:
ctx (in/out) EVP_MD_CTX数据
sig (out) 签名(字符型)
slen (out) 签名长度
pkey (in) 公钥
返 回 值: 1[成功],<=0[失败]
EVP_VerifyInit
函 数 名: # define EVP_VerifyInit (ctx, md)
功能描述: 验签之前的EVP_MD_CTX初始化
说 明: # define EVP_VerifyInit(ctx, md) EVP_DigestInit(ctx, md)
参数说明:
ctx (in/out) EVP_MD_CTX数据
md (in) 杂凑算法,如EVP_sm3()
返 回 值: 1[成功],<=0[失败]
EVP_VerifyInit_ex
函 数 名: # define EVP_VerifyInit_ex(ctx,md,eng)
功能描述: 验签之前的EVP_MD_CTX初始化
说 明:
# define EVP_VerifyInit_ex(ctx,md,eng) EVP_DigestInit_ex(ctx,md,eng)
参数说明:
ctx (in/out) EVP_MD_CTX数据
md (in) 杂凑算法,如EVP_sm3()
eng (in) 引擎
返 回 值: 1[成功],<=0[失败]
EVP_SignUpdate
函 数 名: # define EVP_VerifyUpdate(ctx,m,mlen)
功能描述: 设置待验签的消息数据
说 明: -数据可分多次送入
# define EVP_VerifyUpdate(ctx,m,mlen) EVP_DigestUpdate(ctx,m,mlen)
参数说明:
ctx (in/out) EVP_MD_CTX数据
m (in) 数据
mlen (in) 数据长度
返 回 值: 1[成功],<=0[失败]
EVP_VerifyFinal
函 数 名: int EVP_VerifyFinal(EVP_MD_CTX *ctx, const unsigned char *sig, unsigned int slen, EVP_PKEY *pkey);
功能描述: 获取验签结果(成功/失败)
说 明: 1. 输入的签名值sig是对SM2签名数据(r,s)做i2d_ECDSA_SIG的结果。
参数说明:
ctx (in/out) EVP_MD_CTX
sig (in) 签名(字符型)
slen (in) 签名长度
pkey (in) 公钥
返 回 值: 1[验签成功],<=0[验签失败]
签名验签示例代码
注意:其中密钥EVP_PKEY *pkey的生成代码参见密钥生成代码示例。
#define DEAL_ERR(lab)\
fprintf(stderr, "error: %s %d\n", __FILE__, __LINE__);\
goto lab;
int test_evp_pkey_sign(EVP_PKEY *pkey, int do_sm2)
{
int ret = 0;
EVP_PKEY_CTX *pkctx = NULL;
int type = do_sm2 ? NID_sm_scheme : NID_secg_scheme;
unsigned char dgst[EVP_MAX_MD_SIZE] = "hello world";
size_t dgstlen;
unsigned char sig[256];
size_t siglen;
if (!(pkctx = EVP_PKEY_CTX_new(pkey, NULL))) {DEAL_ERR(end);}
/* EVP_PKEY_sign() */
if (!EVP_PKEY_sign_init(pkctx)) {DEAL_ERR(end);}
if (!EVP_PKEY_CTX_set_ec_sign_type(pkctx, type)){DEAL_ERR(end);}
dgstlen = 32;
memset(sig, 0, sizeof(sig));
siglen = sizeof(sig);
if (!EVP_PKEY_sign(pkctx, sig, &siglen, dgst, dgstlen)) {DEAL_ERR(end);}
if (!EVP_PKEY_verify_init(pkctx)) {DEAL_ERR(end);}
if (!EVP_PKEY_CTX_set_ec_sign_type(pkctx, type)) {DEAL_ERR(end);}
if (EVP_PKEY_verify(pkctx, sig, siglen, dgst, dgstlen) != SM2_VERIFY_SUCCESS) {DEAL_ERR(end);}
printf("%s(%s) passed\n", __FUNCTION__, OBJ_nid2sn(type));
ret = 1;
end:
EVP_PKEY_CTX_free(pkctx);
return ret;
}