基于OpenSSL,实现SM2密文数据的ASN1编码转换

本文介绍如何基于OpenSSL实现SM2密文数据的ASN1编码转换,解决不同接口间数据格式不一致的问题。由于OpenSSL未提供SM2密文的ASN1结构和函数,需要自定义编码结构体。内容涵盖C1C3C2和C1C2C3两种格式的转换,并提及ASN1在线编码转换工具的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

基于OpenSSL, 实现算法的 Engine专栏

本专栏订阅后可查看以下文章

1、基于OpenSSL,实现RSA使用的hash的ASN1编码转换

2、基于OpenSSL,实现RSA公私钥的ASN1编码转换

3、实现SM2公私钥的格式转换

4、基于OpenSSL,实现SM2签名数据的ASN1编码转换

5、基于OpenSSL,实现SM2密文数据的ASN1编码转换

在实际应用场景中会遇到ASN1数据的编码转换,如,在硬件密码库中使用的API接口和在OpenSSL中使用的接口,很有可能是两种格式数据的表现形式,因此,本文提供以下方式进行数据格式的转换。

由于SM2密文格式分为C1C3C2和C1C2C3两种格式,所以在OpenSSL中并没有提供定义的ASN1编码结构和相关函数的定义,因为,需要用户自己定义ASN1编码的结构体。具体如下:

ASN1在线编码转换工具

/****************************************************************
 * FileName:    SM2_cipher.c
 * Author:      labixiaoxin1849
 * Date:        2022-08-27
 * Description: call of OpenSSL,RSA key format conversion 
 ****************************************************************/


#include <string.h>
#include <openssl/ec.h>
#include <openssl/asn1.h>
#include <openssl/asn1t.h>

// 自定义ASN1编码结构
typedef struct SM2_Ciphertext_st SM2_Ciphertext;
DECLARE_ASN1_FUNCTIONS(SM2_Ciphertext)
struct SM2_Ciphertext_st {
	BIGNUM *C1x;
	BIGNUM *C1y;
	ASN1_OCTET_STRING *C3;
	ASN1_OCTET_STRING *C2;
};

// SM2密文转ASN1编码(ASN1编码格式由上述SM2_Ciphertext_st定义)
// cipher_x[in] : 密文C1的x分量,长度为32,不是公钥的x坐标
// cipher_y[in] : 密文C1的y分量,长度为32,不是公钥的y坐标
// cipher_m[in] : 密文C3,长度为32,原文的哈希值
// cipher_c[in] : 密文C2
// cipher_l[in] : 密文C2的长度
// pbDer[out] : ASN1编码的SM2密文
// uiDerLen[out] : ASN1编码的SM2密文长度
int SM2_Cipher_Struct_to_Der(unsigned char *cipher_x, unsigned char *cipher_y, unsigned char *cipher_m, unsigned char *cipher_c, unsigned int cipher_l, unsigned char *pbDer, unsigned int *uiDerLen)
{
	SM2_Ciphertext *xcipher;
	BIGNUM *x, *y;
	int ret = 1;

	do {
		x = y = NULL;
		xcipher = SM2_Ciphertext_new();
		x = BN_bin2bn(cipher_x, 32, NULL);
		y = BN_bin2bn(cipher_y, 32, NULL);
		if (!xcipher || !x || !y) {
			break;
		}

		if (xcipher->C1x) {
			BN_free(xcipher->C1x);
		}
		xcipher->C1x = x;

		if (xcipher->C1y) {
			BN_free(xcipher->C1y);
		}
		xcipher->C1y = y;

		x = y = NULL;

		if (!ASN1_OCTET_STRING_set(xcipher->C3, cipher_m, 32)) {
			break;
		}

		if (!ASN1_OCTET_STRING_set(xcipher->C2, cipher_c, cipher_l)) {
			break;
		}
		if (pbDer) {
			*uiDerLen = i2d_SM2_Ciphertext(xcipher, &pbDer);
		}
		else {
			*uiDerLen = i2d_SM2_Ciphertext(xcipher, NULL);
		}
		if ((int)*uiDerLen < 1) {
			break;
		}

		ret = 0;
	} while (0);

	if (xcipher) {
		SM2_Ciphertext_free(xcipher);
	}
	if (x) {
		BN_free(y);
	}
	if (x) {
		BN_free(y);
	}

	return ret;
}

// SM2密文ASN1编码转结构(ASN1编码格式由上述SM2_Ciphertext_st定义)
// pbDer[in] : ASN1编码的SM2密文
// uiDerLen[in] : ASN1编码的SM2密文长度
// cipher_x[out] : 密文C1的x分量,长度为32,不是公钥的x坐标
// cipher_y[out] : 密文C1的y分量,长度为32,不是公钥的y坐标
// cipher_m[out] : 密文C3,长度为32,原文的哈希值
// cipher_c[out] : 密文C2
// cipher_l[out] : 密文C2的长度
int SM2_Cipher_Der_to_Struct(const unsigned char *pbDer, unsigned int uiDerLen, unsigned char *cipher_x, unsigned char *cipher_y, unsigned char *cipher_m, unsigned char *cipher_c, unsigned int *cipher_l)
{
	unsigned char pbTmp[8192] = { 0 };
	const unsigned char *data;
	int len;
	SM2_Ciphertext *xcipher;
	int ret = 1;

	do {
		xcipher = d2i_SM2_Ciphertext(NULL, &pbDer, uiDerLen);
		if (!xcipher) {
			break;
		}

		len = BN_bn2bin(xcipher->C1x, pbTmp);
		if (len > 32) {
			break;
		}
		memcpy(cipher_x, pbTmp, len);

		memset(pbTmp, 0, sizeof(pbTmp));
		len = BN_bn2bin(xcipher->C1y, pbTmp);
		if (len > 32) {
			break;
		}
		memcpy(cipher_y, pbTmp, len);

		len = ASN1_STRING_length(xcipher->C3);
		if (len > 32) {
			break;
		}
		data = ASN1_STRING_get0_data(xcipher->C3);
		if (!data) {
			break;
		}
		memcpy(cipher_m, data, len);

		*cipher_l = ASN1_STRING_length(xcipher->C2);
		if (*cipher_l > 512) {
			break;
		}
		data = ASN1_STRING_get0_data(xcipher->C2);
		if (!data) {
			break;
		}
		memcpy(cipher_c, data, *cipher_l);

		ret = 0;
	} while (0);

	SM2_Ciphertext_free(xcipher);
	
	return ret;
}

ASN1_SEQUENCE(SM2_Ciphertext) = {
	ASN1_SIMPLE(SM2_Ciphertext, C1x, BIGNUM),
	ASN1_SIMPLE(SM2_Ciphertext, C1y, BIGNUM),
	ASN1_SIMPLE(SM2_Ciphertext, C3, ASN1_OCTET_STRING),
	ASN1_SIMPLE(SM2_Ciphertext, C2, ASN1_OCTET_STRING),
} ASN1_SEQUENCE_END(SM2_Ciphertext)

// 由OpenSSL提供的宏,实现自定义的ASN1编码
// 调用此宏,便定义了函数: SM2_Ciphertext_new、SM2_Ciphertext_free、d2i_SM2_Ciphertext、i2d_SM2_Ciphertext
IMPLEMENT_ASN1_FUNCTIONS(SM2_Ciphertext)


int main(int argc, char *argv[]){
	
	unsigned char c1x[] = {
		0x57, 0xE9, 0x62, 0x22, 0xAB, 0x0A, 0x75, 0x43, 0x81, 0xA8, 0x43, 0x6A, 0xD9, 0x5F, 0xCC, 0xDD, 
		0x6B, 0x0D, 0xD4, 0xF6, 0x24, 0x1D, 0x13, 0xA0, 0x80, 0x2D, 0xE5, 0xD7, 0x09, 0x82, 0xF0, 0xF0
	};
	unsigned char c1y[] = {
		0x97, 0xD3, 0x79, 0x5C, 0x79, 0x50, 0xF2, 0x78, 0x63, 0x50, 0x03, 0xA7, 0x08, 0xD5, 0x6C, 0x4F, 
		0xCB, 0x42, 0x44, 0xE4, 0xE2, 0x8C, 0xAA, 0x4F, 0x74, 0x4E, 0xF9, 0xE7, 0xA6, 0x82, 0x62, 0xE4 
	};
	unsigned char c3[] = {
		0xE7, 0x1B, 0x12, 0x51, 0x61, 0x40, 0xD3, 0xAC, 0x0C, 0x74, 0x84, 0x91, 0xBF, 0xE3, 0xEC, 0x0E, 
		0x60, 0x58, 0x24, 0xAA, 0xD5, 0x66, 0xC4, 0x48, 0x84, 0x10, 0xF4, 0x3C, 0x69, 0x29, 0xD0, 0x6C 
	};
	unsigned char c2[] = {	
		0xB5, 0x92, 0xBF, 0x02, 0xD0, 0x53, 0xC2, 0x1A, 0x8A, 0x4A, 0xF1, 0x56, 0x21, 0x2E, 0xBD, 0x37
	};
	
	unsigned char der[256] = {0};
	unsigned int derlen = 0;
	
	unsigned char c1x2[32] = {0};
	unsigned char c1y2[32] = {0};
	unsigned char c32[32] = {0};
	unsigned char c22[32] = {0};
	unsigned int c22len = 0;
	
	// der 设置为NULL可以获得结构长度
	int rv = SM2_Cipher_Struct_to_Der(c1x, c1y, c3, c2, sizeof(c2), der, &derlen);
	if (rv != 0){
		return rv;
	}
	rv = SM2_Cipher_Der_to_Struct(der, derlen, c1x2, c1y2, c32, c22, &c22len);
	if (rv != 0){
		return rv;
	}
	if(memcmp(c1x, c1x2, 32) != 0 || memcmp(c1y, c1y2, 32) != 0 || memcmp(c3, c32, 32) != 0 || 
		sizeof(c2) != c22len || memcmp(c2, c22, c22len) != 0) {
		return -1;
	}
	printf("SM2 cipher exchange success!!!\n");
	return 0;
}


OpenSSL是一种开源加密库,支持多种加密算法,其中包括SM2算法SM2是中国制定的一种非对称加密算法,适用于数字证书、数字签名等领域。在OpenSSL中,SM2实现主要包括以下部分: 1. EC_KEY结构体:用于表示椭圆曲线密钥对,包括公钥和私钥。SM2算法中使用的是椭圆曲线secp256k1。 2. EVP_PKEY结构体:用于表示EVP(Enveloped Data Processing)算法密钥对。EVP算法OpenSSL中的高层抽象接口,用于支持多种加密算法,包括SM2。 3. SM2签名和验签:使用EVP_DigestSign函数进行签名操作,使用EVP_DigestVerify函数进行验签操作。 4. SM2加密和解密:使用EVP_SealInit函数初始化加密上下文,使用EVP_SealUpdate函数加密数据,最后使用EVP_SealFinal函数结束加密操作。解密操作类似,使用EVP_OpenInit函数初始化解密上下文,使用EVP_OpenUpdate函数解密数据,最后使用EVP_OpenFinal函数结束解密操作。 关于SM2算法的16进制表示,具体表示方式根据不同的数据类型而有所不同。如果是表示SM2密钥对中的公钥和私钥,可以使用十六进制字符串表示。例如: 私钥: 4d1cc6b7d778d8ddc4f32fd3a4f98e437bfad7c92d8f6e9b34d4c6f06d540cea 公钥: 04e126a4846f32b0ab79a7ebfe06f5f3bdeffcf79c71a8cb8c947b163b9f8b6f8c7726519c0055367ae139d22ebf3db9c9c83e08a8e7d5e74a3b3c4fc9bbde77e 如果是表示SM2签名和验签的结果,可以使用DER编码ASN.1编码。如果是表示SM2加密和解密的结果,则可以使用十六进制字符串表示加密后的密文
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蜡笔小新1849

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值