3.6数字信封
数据封装与解封流程
假设EVP_PKEY *pkey已经生成完毕。
表3.5数据封装与解封步骤
封装 |
解封 | |
1 |
cctx = EVP_CIPHER_CTX_new(); |
cctx = EVP_CIPHER_CTX_new(); |
2 |
EVP_SealInit(cctx, EVP_sms4_cbc(), ek, eklen, iv, pubk, npubk); |
EVP_OpenInit(cctx, EVP_sms4_cbc(), ek[1], ekl[1], iv, pkey[1]); |
3 |
EVP_SealUpdate( cctx, p, &clen, msg, mlen); |
EVP_OpenUpdate( cctx, p, &len, cbuf, clen); |
4 |
EVP_SealFinal(cctx, p, &clen); |
EVP_OpenFinal(cctx, p, &len); |
5 |
EVP_CIPHER_CTX_free(cctx); |
EVP_PKEY_CTX_free(pkctx); |
其中
- EVP_CIPHER_CTX_new和EVP_CIPHER_CTX_free函数的使用说明请参见1.3 CIPHER_CTX操作。
- 剩余函数的说明见本节。
int EVP_SealFinal(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl);
EVP_SealInit
函 数 名: int EVP_SealInit(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *type, unsigned char **ek, int *ekl, unsigned char *iv, EVP_PKEY **pubk, int npubk);
功能描述: 数据封装的初始化
说 明: -
- 函数内部随机生成对称密钥key和iv,用于后续送入数据的分组加密。
- 对称密钥key用公钥pubk[]和SM2加密输出,ek[i]=SM2_ENCpubk[i](key), i=0,1,2,…,npubk,即每个ek[i]都能独立恢复key。
- 由于内部调用EVP_PKEY_encrypt_old,所以只能使用标准SM2。
参数说明:
ctx (in/out) EVP_CIPHER_CTX数据
type (in) 分组算法,如EVP_sms4_cbc()
ek (out) 被加密保护的对称密钥信息(多个独立副本)
ekl (out) 对应的ek的长度
iv (out) 初始化向量
pubk (in) SM2公钥(多个)
npubk (in) 公钥个数
返 回 值: 1[成功],<=0[失败]
EVP_SealUpdate
函 数 名: # define EVP_SealUpdate(ctx, out, outl, in, inl)
功能描述: 数据封装
说 明:
- -# define EVP_SealUpdate(ctx, out, outl, in, inl) EVP_EncryptUpdate ( ctx, out, outl, in, inl)
- 由于采用的是数字信封技术,所以输出的密文是对明文采用对称加密
参数说明:
ctx (in/out) EVP_CIPHER_CTX数据
out (out) 密文(对称算法加密的结果)
outl (out) 密文长度
in (in) 明文消息
inl (in) 明文长度
返 回 值: 1[成功],<=0[失败]
EVP_SealFinal
函 数 名: int EVP_SealFinal(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl);
功能描述: 数据封装的结束
说 明: -
- 主要工作就是调用对称加密结束函数EVP_EncryptFinal_ex,所以这一步还可能输出密文数据。
参数说明:
ctx (in/out) EVP_CIPHER_CTX数据
out (out) 密文(对称算法加密的结果)
outl (out) 密文长度
返 回 值: 1[成功],<=0[失败]
EVP_OpenInit
函 数 名: int EVP_OpenInit(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *type, const unsigned char *ek, int ekl, const unsigned char *iv, EVP_PKEY *priv);
功能描述: 数据解封的初始化
说 明: -
- 对称密钥key利用封装时的被保护密钥ek[i]恢复,此处的输入参数iv是封装初始化EVP_SealInit的输出数据。
- ek和priv需对应,因为封装时ek[i]=SM2_ENCpubk[i](key), i=0,1,2,…,npubk。
- 由于内部调用EVP_PKEY_decrypt_old,所以只能使用标准SM2。
参数说明:
ctx (in/out) EVP_CIPHER_CTX数据
type (in) 分组算法,如EVP_sms4_cbc()
ek (out) 被加密保护的对称密钥信息(1份)
ekl (out) ek的长度
iv (out) 初始化向量
priv (in) SM2私钥
返 回 值: 1[成功],<=0[失败]
EVP_OpenUpdate
函 数 名: # define EVP_OpenUpdate(ctx, out, outl, in, inl)
功能描述: 数据解封
说 明:
- -# define EVP_OpenUpdate(ctx, out, outl, in, inl) EVP_DecryptUpdate(ctx, out, outl, in, inl)
- 由于采用的是数字信封技术,所以输出的明文是对密文采用对称解密
参数说明:
ctx (in/out) EVP_CIPHER_CTX数据
out (out) 明文(对称算法解密的结果)
outl (out) 明文长度
in (in) 密文消息
inl (in) 密文长度
返 回 值: 1[成功],<=0[失败]
EVP_OpenFinal
函 数 名: int EVP_OpenFinal(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl);
功能描述: 数据解封的结束
说 明: -
- 主要工作就是调用对称解密结束函数EVP_DecryptFinal_ex,所以这一步还可能输出明文数据。
参数说明:
ctx (in/out) EVP_CIPHER_CTX数据
out (out) 明文(对称算法解密的结果)
outl (out) 明文长度
返 回 值: 1[成功],<=0[失败]
加封解封示例代码
注意:其中密钥EVP_PKEY *pkey的生成代码参见密钥生成代码示例。
#define DEAL_ERR(lab)\
fprintf(stderr, "error: %s %d\n", __FILE__, __LINE__);\
goto lab;
int test_evp_seal()
{
int ret = 0;
EVP_PKEY *pkey[NUM_PKEYS] = {0};
EVP_CIPHER_CTX *cctx = NULL;
unsigned char iv[16];
unsigned char *ek[NUM_PKEYS] = {0};
int ekl[NUM_PKEYS];
unsigned char msg1[] = "Hello ";
unsigned char msg2[] = "World!";
unsigned char cbuf[256];
unsigned char mbuf[256];
unsigned char *p;
int len, clen, mlen, i;
BIO *out, = NULL;
for (i = 0; i < NUM_PKEYS; i++) {
if (!(pkey[i] = genpkey(NID_sm2p256v1, out, 1))) { DEAL_ERR(end);}
ekl[i] = MAX_PKEY_SIZE;
ek[i] = OPENSSL_malloc(ekl[i]);
}
RAND_bytes(iv, sizeof(iv));//luoying useless
if (!(cctx = EVP_CIPHER_CTX_new())) {DEAL_ERR(end);}
if ((i = EVP_SealInit(cctx, EVP_sms4_cbc(), ek, ekl, iv, pkey, NUM_PKEYS)) != NUM_PKEYS) {DEAL_ERR(end);}
p = cbuf;
len = sizeof(cbuf);
if (!EVP_SealUpdate(cctx, p, &len, msg1, sizeof(msg1)-1)) {DEAL_ERR(end);}
p += len;
len = sizeof(cbuf) - (p - cbuf);
if (!EVP_SealUpdate(cctx, p, &len, msg2, sizeof(msg2)-1)) {DEAL_ERR(end);}
p += len;
len = sizeof(cbuf) - (p - cbuf);
if (!EVP_SealFinal(cctx, p, &len)) {DEAL_ERR(end);}
p += len;
clen = p - cbuf;
//---------------------------------------------------
if (!EVP_OpenInit(cctx, EVP_sms4_cbc(), ek[1], ekl[1], iv, pkey[1])) {DEAL_ERR(end);}
memset(mbuf, 0, sizeof(mbuf));
p = mbuf;
len = sizeof(mbuf);
if (!EVP_OpenUpdate(cctx, p, &len, cbuf, clen)) {DEAL_ERR(end);}
p += len;
len = sizeof(mbuf) - len;
if (!EVP_OpenFinal(cctx, p, &len)) {DEAL_ERR(end);}
p += len;
mlen = p - mbuf;
printf("%s() passed\n", __FUNCTION__);
ret = 1;
end:
EVP_CIPHER_CTX_free(cctx);
for (i = 0; i < NUM_PKEYS; i++) {
EVP_PKEY_free(pkey[i]);
OPENSSL_free(ek[i]);
}
return ret;
}