Cryptography API: Next Generation(CNG)使用梳理——非对称加密算法应用(三)与OpenSSL3之间的非对称密钥转换(ECC)

此篇整理了一下OpenSSL3.0+和CNG之间密钥的相互转换

 ECC部分:

CNG导出密钥的内存BLOB结构为:BCRYPT_ECCPRIVATE_BLOBBCRYPT_ECCPUBLIC_BLOB

椭圆曲线(ECC) 公钥 BLOB (BCRYPT_ECCPUBLIC_BLOB) 在连续内存中具有以下格式。 X 和 Y 坐标是以大端格式编码的无符号整数。

BCRYPT_ECCKEY_BLOB
BYTE X[cbKey] // Big-endian.
BYTE Y[cbKey] // Big-endian.

OpenSSL中ECC公钥为点格式,可以用EC_POINT_set_affine_coordinates函数转换一下即可

椭圆曲线(ECC) 私钥 BLOB (BCRYPT_ECCPRIVATE_BLOB) 在连续内存中具有以下格式。 X 和 Y 坐标和 d 值是以大端格式编码的无符号整数。

BCRYPT_ECCKEY_BLOB
BYTE X[cbKey] // Big-endian.
BYTE Y[cbKey] // Big-endian.
BYTE d[cbKey] // Big-endian.

而在讲OpenSSL3关于ECC密钥的结构前,很遗憾的说OpenSSL3开始已经放弃了对EC_KEY结构的直接构造和读写,转而需要通过对EVP_PKEY抽象的读写和构造,所以整个转换过程有点繁琐和抽象

为了方便转换定义了 一些辅助函数

//判断导出的BLOB是否为EC公钥
bool Is_CNG_EC_Public_Key(PBCRYPT_ECCKEY_BLOB pubKey)
{
	switch(pubKey->dwMagic) {
	case BCRYPT_ECDH_PUBLIC_P256_MAGIC:
	case BCRYPT_ECDSA_PUBLIC_P256_MAGIC:
	case BCRYPT_ECDH_PUBLIC_P384_MAGIC:
	case BCRYPT_ECDSA_PUBLIC_P384_MAGIC:
	case BCRYPT_ECDH_PUBLIC_P521_MAGIC:
	case BCRYPT_ECDSA_PUBLIC_P521_MAGIC:
		return true;
	}
	return false;
}
//判断导出的BLOB是否为EC私钥
bool Is_CNG_EC_Private_Key(PBCRYPT_ECCKEY_BLOB pvtKey)
{
	switch(pvtKey->dwMagic) {
	case BCRYPT_ECDH_PRIVATE_P256_MAGIC:
	case BCRYPT_ECDSA_PRIVATE_P256_MAGIC:
	case BCRYPT_ECDH_PRIVATE_P384_MAGIC:
	case BCRYPT_ECDSA_PRIVATE_P384_MAGIC:
	case BCRYPT_ECDH_PRIVATE_P521_MAGIC:
	case BCRYPT_ECDSA_PRIVATE_P521_MAGIC:
		return true;
	}
	return false;
}
//将OpenSSL的nid转换为ECCKEY_BLOB中的Magic
int ECDH_NID_to_CNG_Magic(int nid, bool isPrivate)
{
	if (isPrivate) {
		switch(nid) {
		case NID_X9_62_prime256v1:
			return BCRYPT_ECDH_PRIVATE_P256_MAGIC;
		case NID_secp384r1:
			return BCRYPT_ECDH_PRIVATE_P384_MAGIC;
		case NID_secp521r1:
			return BCRYPT_ECDH_PRIVATE_P521_MAGIC;
		}
	}
	else {
		switch(nid) {
		case NID_X9_62_prime256v1:
			return BCRYPT_ECDH_PUBLIC_P256_MAGIC;
		case NID_secp384r1:
			return BCRYPT_ECDH_PUBLIC_P384_MAGIC;
		case NID_secp521r1:
			return BCRYPT_ECDH_PUBLIC_P521_MAGIC;
		}
	}
	return 0;
}
//同上
int ECDSA_NID_to_CNG_Magic(int nid, bool isPrivate)
{
	if (isPrivate) {
		switch(nid) {
		case NID_X9_62_prime256v1:
			return BCRYPT_ECDSA_PRIVATE_P256_MAGIC;
		case NID_secp384r1:
			return BCRYPT_ECDSA_PRIVATE_P384_MAGIC;
		case NID_secp521r1:
			return BCRYPT_ECDSA_PRIVATE_P521_MAGIC;
		}
	}
	else {
		switch(nid) {
		case NID_X9_62_prime256v1:
			return BCRYPT_ECDSA_PUBLIC_P256_MAGIC;
		case NID_secp384r1:
			return BCRYPT_ECDSA_PUBLIC_P384_MAGIC;
		case NID_secp521r1:
			return BCRYPT_ECDSA_PUBLIC_P521_MAGIC;
		}
	}
	return 0;
}
//将ECCKEY_BLOB中的Magic转换为OpenSSL的nid
int CNG_Magic_to_NID (int magic)
{
	switch(magic) {
	case BCRYPT_ECDH_PRIVATE_P256_MAGIC:
	case BCRYPT_ECDSA_PRIVATE_P256_MAGIC:
	case BCRYPT_ECDH_PUBLIC_P256_MAGIC:
	case BCRYPT_ECDSA_PUBLIC_P256_MAGIC:
		return NID_X9_62_prime256v1;
	case BCRYPT_ECDH_PRIVATE_P384_MAGIC:
	case BCRYPT_ECDSA_PRIVATE_P384_MAGIC:
	case BCRYPT_ECDH_PUBLIC_P384_MAGIC:
	case BCRYPT_ECDSA_PUBLIC_P384_MAGIC:
		return NID_secp384r1;
	case BCRYPT_ECDH_PRIVATE_P521_MAGIC:
	case BCRYPT_ECDSA_PRIVATE_P521_MAGIC:
	case BCRYPT_ECDH_PUBLIC_P521_MAGIC:
	case BCRYPT_ECDSA_PUBLIC_P521_MAGIC:
		return NID_secp521r1;
	}

	return 0;
}

//将OpenSSL的nid转换为OpenSSL的SN
char* OpenSSL_NID_to_SN(int nid)
{
	switch(nid) {
	case NID_X9_62_prime256v1:
		return SN_X9_62_prime256v1;
	case NID_secp384r1:
		return SN_secp384r1;
	case NID_secp521r1:
		return SN_secp521r1;
	}
	return NULL;
}

//将ECCKEY_BLOB中的Magic转换为OpenSSL的sn
char* CNG_Magic_to_SN(int magic)
{
	switch(magic) {
	case BCRYPT_ECDH_PRIVATE_P256_MAGIC:
	case BCRYPT_ECDSA_PRIVATE_P256_MAGIC:
	case BCRYPT_ECDH_PUBLIC_P256_MAGIC:
	case BCRYPT_ECDSA_PUBLIC_P256_MAGIC:
		return SN_X9_62_prime256v1;
	case BCRYPT_ECDH_PRIVATE_P384_MAGIC:
	case BCRYPT_ECDSA_PRIVATE_P384_MAGIC:
	case BCRYPT_ECDH_PUBLIC_P384_MAGIC:
	case BCRYPT_ECDSA_PUBLIC_P384_MAGIC:
		return SN_secp384r1;
	case BCRYPT_ECDH_PRIVATE_P521_MAGIC:
	case BCRYPT_ECDSA_PRIVATE_P521_MAGIC:
	case BCRYPT_ECDH_PUBLIC_P521_MAGIC:
	case BCRYPT_ECDSA_PUBLIC_P521_MAGIC:
		return SN_secp521r1;
	}
	return NULL;
}

OpenSSL中关于ECC密钥内部参数(详见

其中公钥主要包含group和pub

"group" (OSSL_PKEY_PARAM_GROUP_NAME) <UTF8 string>
"pub" (OSSL_PKEY_PARAM_PUB_KEY) <octet string>

私钥则是在公钥的基础上多了一个priv

"group" (OSSL_PKEY_PARAM_GROUP_NAME) <UTF8 string>
"pub" (OSSL_PKEY_PARAM_PUB_KEY) <octet string>
"priv" (OSSL_PKEY_PARAM_PRIV_KEY) <unsigned integer>


公钥格式转换

CNG->OpenSSL3

基本的流程就是

一、将CNG导出的BCRYPT_ECCPUBLIC_BLOB中的X和Y坐标格式转换为OpenSSL中的pub的点格式,也就是"pub" (OSSL_PKEY_PARAM_PUB_KEY)

二、获取椭圆曲线的SN名字,也就是"group" (OSSL_PKEY_PARAM_GROUP_NAME)

备注:虽说内部有独立的"qx" (OSSL_PKEY_PARAM_EC_PUB_X)和"qy" (OSSL_PKEY_PARAM_EC_PUB_Y)属性,但是这是只读的,并不能设置

上代码:

EVP_PKEY* CNG_to_EVP_EC_pub_key(EVP_PKEY_CTX* ctx, PBCRYPT_ECCKEY_BLOB ecPubKey)
{
	if(!Is_CNG_EC_Public_Key(ecPubKey)) return NULL;
	EVP_PKEY* pkey = NULL;
	BIGNUM* qx = NULL;
	BIGNUM* qy = NULL;

	OSSL_PARAM_BLD *param_bld;
	OSSL_PARAM *params = NULL;

	BN_CTX* bctx = BN_CTX_secure_new();//BN_CTX_new();
	BN_CTX_start(bctx);
	qx = BN_CTX_get(bctx);
	qy = BN_CTX_get(bctx);

	//定位公钥中的X坐标值数据的位置
	BYTE* EC_X_blob = (BYTE*)ecPubKey + sizeof(BCRYPT_ECCKEY_BLOB);
	//定位公钥中的Y坐标值数据的位置
	BYTE* EC_Y_blob = EC_X_blob + ecPubKey->cbKey;
	//获取公钥中的X坐标值
	BN_bin2bn(EC_X_blob, ecPubKey->cbKey, qx);
	//获取公钥中的Y坐标值
	BN_bin2bn(EC_Y_blob, ecPubKey->cbKey, qy);

	unsigned char *pbuf = NULL;
	//将坐标qx、qy转化为点格式
	//借助CNG_Magic_to_NID辅助函数动态获取椭圆曲线的nid
	EC_GROUP *ecgroup = EC_GROUP_new_by_curve_name(CNG_Magic_to_NID(ecPubKey->dwMagic));
	EC_POINT *pub = EC_POINT_new(ecgroup);
	//映射坐标到椭圆曲线,获取点
	EC_POINT_set_affine_coordinates(ecgroup, pub, qx, qy, bctx);
	//将点转化为非压缩(POINT_CONVERSION_UNCOMPRESSED)二进制格式
	size_t pub_out_len = EC_POINT_point2buf(ecgroup, pub, POINT_CONVERSION_UNCOMPRESSED, &pbuf, bctx);

	param_bld = OSSL_PARAM_BLD_new();
	if (qx != NULL && qy != NULL && param_bld != NULL
		&& OSSL_PARAM_BLD_push_utf8_string(param_bld, OSSL_PKEY_PARAM_GROUP_NAME/*"group"*/,
										CNG_Magic_to_SN(ecPubKey->dwMagic), 0)
		&& OSSL_PARAM_BLD_push_octet_string(param_bld, OSSL_PKEY_PARAM_PUB_KEY/*"pub"*/,
										pbuf, pub_out_len))
		params = OSSL_PARAM_BLD_to_param(param_bld);

	if (params == NULL
		|| EVP_PKEY_fromdata_init(ctx) <= 0
		|| EVP_PKEY_fromdata(ctx, &pkey, EVP_PKEY_PUBLIC_KEY, params) <= 0) {
			printf("new_get_pub_key error\n");
	}

	OSSL_PARAM_free(params);
	OSSL_PARAM_BLD_free(param_bld);

	OPENSSL_free(pbuf);
	EC_POINT_free(pub);
	EC_GROUP_free(ecgroup);

	BN_CTX_end(bctx);
	BN_CTX_free(bctx);

	return pkey;
}

反之OpenSSL3->CNG

一、直接获取qx和qy,或者通过获取"pub"后转换为qx和py

二、获取椭圆曲线

1、通过EVP_PKEY_get_group_name获取椭圆曲线的名字(SN)

2、通过OBJ_sn2nid将名字转换为nid

3、通过辅助函数ECDH_NID_to_CNG_Magic或者ECDSA_NID_to_CNG_Magic转换为BCRYPT_ECCKEY_BLOB中的Magic

备注:当然也可以直接定义一个辅助函数将EVP_PKEY_get_group_name获取椭圆曲线的名字(SN)直接转换为BCRYPT_ECCKEY_BLOB中的Magic

//返回值需要用OPENSSL_free释放
PBCRYPT_ECCKEY_BLOB EVP_to_CNG_EC_pub_key(EVP_PKEY* ecPubKey)
{
	EVP_PKEY* pkey = NULL;
	BIGNUM* qx = NULL;
	BIGNUM* qy = NULL;
	BN_CTX* bctx = NULL;

	//OpenSSL3 中没有找到直接获取EC_GROUP的方法,可以变相使用EVP_PKEY_get_group_name获取group的curve_name
	char curve_name[256] = {0};
	size_t curve_name_len = sizeof(curve_name);
	EVP_PKEY_get_group_name(ecPubKey, curve_name, curve_name_len, &curve_name_len);
	//从EVP_PKEY获取NID值
	int nid = OBJ_sn2nid(curve_name);

#ifdef GET_PARAM_EC_PUB_X_AND_Y
	//直接获取qx和qy元素,
	if (1 == EVP_PKEY_get_bn_param(ecPubKey, OSSL_PKEY_PARAM_EC_PUB_X/*"qx"*/, &qx)) {
		EVP_PKEY_get_bn_param(ecPubKey, OSSL_PKEY_PARAM_EC_PUB_Y/*"qy"*/, &qy);
	}
	else {//如果不能直接获取,则通过获取pub元素,而后转化为坐标
#else
		size_t out = 0;
		EVP_PKEY_get_octet_string_param(ecPubKey, OSSL_PKEY_PARAM_PUB_KEY/*"pub"*/, NULL, out, &out);
		if (!out) return NULL;

		bctx = BN_CTX_secure_new();//BN_CTX_new();
		BN_CTX_start(bctx);
		qx = BN_CTX_get(bctx);
		qy = BN_CTX_get(bctx);

		unsigned char* pub_data = (unsigned char*)OPENSSL_malloc(out);
		EVP_PKEY_get_octet_string_param(ecPubKey, OSSL_PKEY_PARAM_PUB_KEY/*"pub"*/, pub_data, out, &out);

		//将pub的点格式转换为坐标格式
		EC_GROUP *ecgroup = EC_GROUP_new_by_curve_name(nid);
		EC_POINT *pub = EC_POINT_new(ecgroup);
		EC_POINT_oct2point(ecgroup, pub, pub_data, out, bctx);
		EC_POINT_get_affine_coordinates(ecgroup, pub, qx, qy, bctx);

		EC_GROUP_free(ecgroup);
		EC_POINT_free(pub);
		OPENSSL_free(pub_data);
#endif
#ifdef GET_PARAM_EC_PUB_X_AND_Y
	}
#endif
	//获取qx和qy的大小
	ULONG cbX = BN_num_bytes(qx);
	ULONG cbY = BN_num_bytes(qy);

	ULONG cbKey = max(cbX, cbY);

	PBCRYPT_ECCKEY_BLOB pub_blob = (PBCRYPT_ECCKEY_BLOB)OPENSSL_malloc(sizeof(BCRYPT_ECCKEY_BLOB) + cbKey * 2);
	//借助辅助函数将nid转换为BCRYPT_ECCKEY_BLOB的Magic
	//此处为ECDH用于密钥交换ECDH_NID_to_CNG_Magic
	//如果是签名则使用ECDSA_NID_to_CNG_Magic
	pub_blob->dwMagic = ECDH_NID_to_CNG_Magic(nid, false);
	pub_blob->cbKey = cbKey;

	//使用BN_bn2binpad可以在缓冲区大于所需空间的情况下,自动在高位填充0
	BYTE* EC_X_blob = (BYTE*)pub_blob + sizeof(BCRYPT_ECCKEY_BLOB);
	BYTE* EC_Y_blob = EC_X_blob + cbKey;

	BN_bn2binpad(qx, EC_X_blob, cbKey);
	BN_bn2binpad(qy, EC_Y_blob, cbKey);

#ifdef GET_PARAM_EC_PUB_X_AND_Y
	if (bctx) {
#endif
		BN_CTX_end(bctx);
		BN_CTX_free(bctx);
#ifdef GET_PARAM_EC_PUB_X_AND_Y
	}
	else {
		BN_free(qx);
		BN_free(qy);
	}
#endif

	return pub_blob;
}

私钥格式转换

CNG->OpenSSL3

由于私钥只是比公钥多了一个priv,因此基本流程相同,多了一个将BCRYPT_ECCKEY_BLOB中的d转换为OpenSSL EVP_KEY中priv的过程而已

代码如下:

EVP_PKEY* CNG_to_EVP_EC_pvt_key(EVP_PKEY_CTX* ctx, PBCRYPT_ECCKEY_BLOB ecPvtKey)
{
	if(!Is_CNG_EC_Private_Key(ecPvtKey)) return NULL;
	EVP_PKEY* pkey = NULL;
	BIGNUM* qx = NULL;
	BIGNUM* qy = NULL;
	BIGNUM* d = NULL;

	OSSL_PARAM_BLD *param_bld;
	OSSL_PARAM *params = NULL;

	BN_CTX* bctx = BN_CTX_secure_new();//BN_CTX_new();
	BN_CTX_start(bctx);
	qx = BN_CTX_get(bctx);
	qy = BN_CTX_get(bctx);
	d = BN_CTX_get(bctx);

	BYTE* EC_X_blob = (BYTE*)ecPvtKey + sizeof(BCRYPT_ECCKEY_BLOB);
	BYTE* EC_Y_blob = EC_X_blob + ecPvtKey->cbKey;
	BYTE* EC_d_blob = EC_Y_blob + ecPvtKey->cbKey;//priv

	BN_bin2bn(EC_X_blob, ecPvtKey->cbKey, qx);
	BN_bin2bn(EC_Y_blob, ecPvtKey->cbKey, qy);
	BN_bin2bn(EC_d_blob, ecPvtKey->cbKey, d);

	unsigned char *pbuf = NULL;
	//将坐标qx、qy转化为点格式

	EC_GROUP *ecgroup = EC_GROUP_new_by_curve_name(CNG_Magic_to_NID(ecPvtKey->dwMagic));
	EC_POINT *pvt = EC_POINT_new(ecgroup);
	//映射坐标到椭圆曲线,获取点
	EC_POINT_set_affine_coordinates(ecgroup, pvt, qx, qy, bctx);
	//将点转化为非压缩(POINT_CONVERSION_UNCOMPRESSED)二进制格式
	size_t pvt_out_len = EC_POINT_point2buf(ecgroup, pvt, POINT_CONVERSION_UNCOMPRESSED, &pbuf, bctx);

	param_bld = OSSL_PARAM_BLD_new();
	if (qx != NULL && qy != NULL && param_bld != NULL
		&& OSSL_PARAM_BLD_push_utf8_string(param_bld, OSSL_PKEY_PARAM_GROUP_NAME/*"group"*/,
										CNG_Magic_to_SN(ecPvtKey->dwMagic), 0)
		&& OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_PRIV_KEY/*"priv"*/,
								d)
		&& OSSL_PARAM_BLD_push_octet_string(param_bld, OSSL_PKEY_PARAM_PUB_KEY/*"pub"*/,
										pbuf, pvt_out_len))
		params = OSSL_PARAM_BLD_to_param(param_bld);


	if (params == NULL
		|| EVP_PKEY_fromdata_init(ctx) <= 0
		|| EVP_PKEY_fromdata(ctx, &pkey, EVP_PKEY_KEYPAIR, params) <= 0) {
			printf("new_get_pub_key error\n");
	}

	OSSL_PARAM_free(params);
	OSSL_PARAM_BLD_free(param_bld);

	OPENSSL_free(pbuf);
	EC_POINT_free(pvt);
	EC_GROUP_free(ecgroup);

	BN_CTX_end(bctx);
	BN_CTX_free(bctx);

	return pkey;
}

OpenSSL3->CNG

和上面情况一样,只是多了一个将OpenSSL EVP_KEY中priv转换为BCRYPT_ECCKEY_BLOB中的d

代码如下:

PBCRYPT_ECCKEY_BLOB EVP_to_CNG_EC_pvt_key(EVP_PKEY* ecPvtKey)
{
	EVP_PKEY* pkey = NULL;
	BIGNUM* qx = NULL;
	BIGNUM* qy = NULL;
	BIGNUM* d = NULL;
	BN_CTX* bctx = NULL;

	//OpenSSL3 中没有找到直接获取EC_GROUP的方法,可以变相使用EVP_PKEY_get_group_name获取group的curve_name
	char curve_name[256] = {0};
	size_t curve_name_len = sizeof(curve_name);
	EVP_PKEY_get_group_name(ecPvtKey, curve_name, curve_name_len, &curve_name_len);
	int nid = OBJ_sn2nid(curve_name);

#ifdef GET_PARAM_EC_PVT_X_AND_Y
	//直接获取qx和qy元素
	if (1 == EVP_PKEY_get_bn_param(ecPubKey, OSSL_PKEY_PARAM_EC_PUB_X/*"qx"*/, &qx)) {
		EVP_PKEY_get_bn_param(ecPubKey, OSSL_PKEY_PARAM_EC_PUB_Y/*"qy"*/, &qy);
		EVP_PKEY_get_bn_param(ecPvtKey, OSSL_PKEY_PARAM_PRIV_KEY/*"priv"*/, &d);
	}
	else {//如果不能直接获取,则通过获取pub元素,而后转化为坐标
#else
	size_t out = 0;
	//可以使用i2d_PublicKey,此函数为通用函数,内部会按EC_KEY的类型再调用i2o_ECPublicKey(\crypto\asn1\i2d_evp.c OppenSSL 3.0.5 p123)
	//i2o_ECPublicKey可以从EC_KEY中读取公钥二进制结构(\crypto\ec\ec_asn1.c OppenSSL 3.0.5 p1159)
	EVP_PKEY_get_octet_string_param(ecPvtKey, OSSL_PKEY_PARAM_PUB_KEY/*"pub"*/, NULL, out, &out);
	if (!out) return NULL;

	bctx = BN_CTX_new();//BN_CTX_secure_new();
	BN_CTX_start(bctx);
	qx = BN_CTX_get(bctx);
	qy = BN_CTX_get(bctx);
	d = BN_CTX_get(bctx);

	unsigned char* pvt_data = (unsigned char*)OPENSSL_malloc(out);
	EVP_PKEY_get_octet_string_param(ecPvtKey, OSSL_PKEY_PARAM_PUB_KEY/*"pub"*/, pvt_data, out, &out);//qx和qy在OSSL_PKEY_PARAM_PUB_KEY中

	EC_GROUP *ecgroup = EC_GROUP_new_by_curve_name(nid);
	EC_POINT *pvt = EC_POINT_new(ecgroup);
	EC_POINT_oct2point(ecgroup, pvt, pvt_data, out, bctx);
	EC_POINT_get_affine_coordinates(ecgroup, pvt, qx, qy, bctx);

	//获取私钥部分
	EVP_PKEY_get_bn_param(ecPvtKey, OSSL_PKEY_PARAM_PRIV_KEY/*"priv"*/, &d);

	EC_GROUP_free(ecgroup);
	EC_POINT_free(pvt);
	OPENSSL_free(pvt_data);
#endif
#ifdef GET_PARAM_EC_PUB_X_AND_Y
}
#endif
	ULONG cbX = BN_num_bytes(qx);
	ULONG cbY = BN_num_bytes(qy);
	ULONG cbd = BN_num_bytes(d);

	ULONG cbKey = max(cbX, cbY);
	cbKey = max(cbKey, cbd);

	PBCRYPT_ECCKEY_BLOB pvt_blob = (PBCRYPT_ECCKEY_BLOB)OPENSSL_malloc(sizeof(BCRYPT_ECCKEY_BLOB) + cbKey * 3);
	//借助辅助函数将nid转换为BCRYPT_ECCKEY_BLOB的Magic
	//此处为ECDH用于密钥交换ECDH_NID_to_CNG_Magic
	//如果是签名则使用ECDSA_NID_to_CNG_Magic
	pvt_blob->dwMagic = ECDH_NID_to_CNG_Magic(nid, true);
	pvt_blob->cbKey = cbKey;

	//使用BN_bn2binpad可以在缓冲区大于所需空间的情况下,自动在高位填充0
	BYTE* EC_X_blob = (BYTE*)pvt_blob + sizeof(BCRYPT_ECCKEY_BLOB);
	BYTE* EC_Y_blob = EC_X_blob + cbKey;
	BYTE* EC_d_blob = EC_Y_blob + cbKey;

	BN_bn2binpad(qx, EC_X_blob, cbKey);
	BN_bn2binpad(qy, EC_Y_blob, cbKey);
	BN_bn2binpad(d, EC_d_blob, cbKey);

#ifdef GET_PARAM_EC_PVT_X_AND_Y
	if (bctx) {
#endif
		BN_CTX_end(bctx);
		BN_CTX_free(bctx);
#ifdef GET_PARAM_EC_PVT_X_AND_Y
	}
	else {
		BN_free(qx);
		BN_free(qy);
		BN_free(d);
	}
#endif

	return pvt_blob;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,这是一个比较复杂的问题,我会尽量详细地回答。 首先,ECC(Elliptic Curve Cryptography,椭圆曲线加密)是一种对称加密算法,它比传统的 RSA 算法更加高效、安全和灵活。在 OpenSSL 中,可以通过以下步骤对大文件进行 ECC 加密和解: 1. 生成 ECC 对 可以使用 OpenSSL 中的 EC 函数族来生成 ECC 对。具体步骤如下: ```c++ // 选择一个椭圆曲线 EC_KEY *key = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); // 生成对 if (EC_KEY_generate_key(key) != 1) { // 对生成失败 } // 获取公和私 EC_POINT *pub_key = EC_KEY_get0_public_key(key); BIGNUM *priv_key = EC_KEY_get0_private_key(key); ``` 2. 加载公和私加密和解之前,需要将公和私加载到内存中。可以使用 OpenSSL 中的 PEM_read_* 函数族从文件中读取,或者使用 d2i_ECPrivateKey 和 i2o_ECPrivateKey 函数直接从内存中读取和写入。 ```c++ // 从文件中读取私 FILE *fp = fopen("private_key.pem", "r"); EC_KEY *key = PEM_read_ECPrivateKey(fp, NULL, NULL, NULL); fclose(fp); // 从内存中读取公 unsigned char *pub_key_bytes = ...; // 从文件或网络中读取公的二进制表示 EC_KEY *key = EC_KEY_new(); EC_POINT *pub_key = EC_POINT_new(EC_KEY_get0_group(key)); if (EC_POINT_oct2point(EC_KEY_get0_group(key), pub_key, pub_key_bytes, pub_key_len, NULL) != 1) { // 公加载失败 } if (EC_KEY_set_public_key(key, pub_key) != 1) { // 公设置失败 } ``` 3. ECC 加密和解 在加载完公和私之后,可以使用 OpenSSL 中的 ECDH 函数族进行 ECC 加密和解。具体步骤如下: ```c++ // 加密 unsigned char *plaintext = ...; // 待加密的明文 size_t plaintext_len = ...; // 明文长度 unsigned char *ciphertext = (unsigned char *)malloc(EC_GROUP_get_degree(EC_KEY_get0_group(key)) / 8 + plaintext_len); size_t ciphertext_len = ECDH_compute_key(ciphertext, EC_GROUP_get_degree(EC_KEY_get0_group(key)) / 8, EC_POINT_point2oct(EC_KEY_get0_group(key), EC_KEY_get0_public_key(key), POINT_CONVERSION_UNCOMPRESSED, NULL, 0, NULL), key, NULL); // 解 unsigned char *plaintext = (unsigned char *)malloc(ciphertext_len - EC_GROUP_get_degree(EC_KEY_get0_group(key)) / 8); size_t plaintext_len = ECDH_compute_key(plaintext, ciphertext_len - EC_GROUP_get_degree(EC_KEY_get0_group(key)) / 8, ciphertext, key, NULL); ``` 需要注意的是,ECC 加密和解只能处理长度小于椭圆曲线阶数的数据。对于大文件,可以使用分块加密和解的方式,即先将文件分成若干个小块,每个小块分别进行加密或解,最后再将小块拼接起来即可。 希望这个回答能够帮到你。如果还有什么问题,请随时问我。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值