基于openssl3.0,已知ECC算法x,y如何构建公钥的EVP_PKEY结构


前言

在将项目从 OpenSSL 1.1 升级到 OpenSSL 3.0 的过程中,开发者可能会遇到许多 API 的变化和弃用,尤其是在处理椭圆曲线(ECC)相关的函数时。OpenSSL 3.0 引入了一些新的 API,并对原有的 EC 相关函数进行了替换或弃用。尽管如此,我们仍然可以使用这些新 API 来创建和处理 EVP_PKEY 结构,以满足现代加密需求。在已知ECC算法公钥下x,y以及算法NID的前提下,如何使用openssl-3.0中的接口构建EVP_PKEY结构呢?

一、问题分析

在openssl-1.1中,我们可以很轻松创建ECC公钥,但在openssl-3.0中很多相关的API已经被替换或弃用,导致代码无法使用。

二、在openssl-1.1中的解决方案

注:
1.代码实现中的返回值定义,日志输出函数,内存释放函数需自行实现。

2.1使用ECC算法的NID构建EC_KEY结构
		EC_KEY  ecPublicKey  = NULL;
		ecPublicKey = EC_KEY_new_by_curve_name(nid);
		if (NULL == (*ecPublicKey)) {
		        LOG_E("EC_KEY_new_by_curve_name error : %ld, %s", ERR_get_error(), ERR_reason_error_string(ERR_get_error()));
		        return -1;
		}
2.2将公钥的x,y转换成大数BIGNUM
	BIGNUM* bnX = NULL;
    BIGNUM* bnY = NULL;
    bnX = BN_bin2bn(x, 32, NULL);
    if (NULL == bnX) {
        LOG_E("BN_bin2bn error : %ld, %s", ERR_get_error(), ERR_reason_error_string(ERR_get_error()));
        EC_KEY_free(*ecPublicKey);
        *ecPublicKey = NULL;
        return -1;
    }

    bnY = BN_bin2bn(y, 32, NULL);
    if (NULL == bnY) {
        LOG_E("BN_bin2bn error : %ld, %s", ERR_get_error(), ERR_reason_error_string(ERR_get_error()));
        EC_KEY_free(*ecPublicKey);
        *ecPublicKey = NULL;
        WY_OPENSSL_FREE(BIGNUM, bnX);
        return -1;
    }
2.3将公钥信息x,y大数存放在EC_KEY结构中
	if (1 != EC_KEY_set_public_key_affine_coordinates(*ecPublicKey, bnX, bnY)) {
        LOG_E("BN_bin2bn : %ld, %s", ERR_get_error(), ERR_reason_error_string(ERR_get_error()));
        EC_KEY_free(*ecPublicKey);
        *ecPublicKey = NULL;
        WY_OPENSSL_FREE(BIGNUM, bnX);
        WY_OPENSSL_FREE(BIGNUM, bnY);
        return -1;
    }
2.4完整代码
int get_ec_public_key_from_x_y(const unsigned char *x, const unsigned char *y, int nid, EC_KEY **ecPublicKey)
{
    if ((NULL == x) || (NULL == y)) {
        LOG_E("param error");
        return ERROR_PARAM;
    }

    if (NULL != (*ecPublicKey)) {
        WY_OPENSSL_FREE(EC_KEY, *ecPublicKey);
        *ecPublicKey = NULL;
    }
    
    *ecPublicKey = EC_KEY_new_by_curve_name(nid);
    if (NULL == (*ecPublicKey)) {
        LOG_E("EC_KEY_new_by_curve_name error : %ld, %s", ERR_get_error(), ERR_reason_error_string(ERR_get_error()));
        return -1;
    }

    BIGNUM* bnX = NULL;
    BIGNUM* bnY = NULL;
    bnX = BN_bin2bn(x, 32, NULL);
    if (NULL == bnX) {
        LOG_E("BN_bin2bn error : %ld, %s", ERR_get_error(), ERR_reason_error_string(ERR_get_error()));
        EC_KEY_free(*ecPublicKey);
        *ecPublicKey = NULL;
        return -1;
    }

    bnY = BN_bin2bn(y, 32, NULL);
    if (NULL == bnY) {
        LOG_E("BN_bin2bn error : %ld, %s", ERR_get_error(), ERR_reason_error_string(ERR_get_error()));
        EC_KEY_free(*ecPublicKey);
        *ecPublicKey = NULL;
        WY_OPENSSL_FREE(BIGNUM, bnX);
        return -1;
    }

    if (1 != EC_KEY_set_public_key_affine_coordinates(*ecPublicKey, bnX, bnY)) {
        LOG_E("BN_bin2bn : %ld, %s", ERR_get_error(), ERR_reason_error_string(ERR_get_error()));
        EC_KEY_free(*ecPublicKey);
        *ecPublicKey = NULL;
        WY_OPENSSL_FREE(BIGNUM, bnX);
        WY_OPENSSL_FREE(BIGNUM, bnY);
        return -1;
    }

    WY_OPENSSL_FREE(BIGNUM, bnX);
    WY_OPENSSL_FREE(BIGNUM, bnY);
    return 0;
}

int EC_KEY_to_EVP_PKEY(const EC_KEY *ecKey, EVP_PKEY **evpKey)
{
    if (NULL == ecKey) {
        LOG_E("param error");
        return ERROR_PARAM;
    }

    if (NULL != (*evpKey)) {
        EVP_PKEY_free(*evpKey);
        *evpKey = NULL;
    }

    *evpKey = EVP_PKEY_new();
    if (NULL == (*evpKey)) {
        LOG_E("EVP_PKEY_new : %ld, %s", ERR_get_error(), ERR_reason_error_string(ERR_get_error()));
        return ERROR_EVP_PKEY_NEW;
    }

    if (1 != EVP_PKEY_set1_EC_KEY(*evpKey, (EC_KEY*)ecKey)) {
        LOG_E("EVP_PKEY_set1_EC_KEY : %ld, %s", ERR_get_error(), ERR_reason_error_string(ERR_get_error()));
        EVP_PKEY_free(*evpKey);
        *evpKey = NULL;
        return -1;
    }
    LOG_D("ec key to evp key success");
    return 0;
}

int get_evp_public_key_ecc(const unsigned char *, size_t keyLen, int nid, EVP_PKEY **evpKey)
{
    if ((NULL == key) || (keyLen <= 0)) {
        LOG_E("param error");
        return ERROR_PARAM;
    }

    int ret = 0;
    unsigned char tmpPublicKey[1024] = {0};

    if (keyLen == 65) {
        memcpy(tmpPublicKey, key + 1, keyLen -1);
    } else if (keyLen == 64) {
        memcpy(tmpPublicKey, key, keyLen);
    } else {
        return ERROR_NOT_SUPPORT;
    }
    unsigned char tmpX[64] = {0};
    unsigned char tmpY[64] = {0};
    memcpy(tmpX, tmpPublicKey, 32);
    memcpy(tmpY, tmpPublicKey + 32, 32);
    LOG_H((const unsigned char*)tmpX, 32);
    LOG_H((const unsigned char*)tmpY, 32);
    EC_KEY *ecPublicKey = NULL;
    ret = get_ec_public_key_from_x_y(tmpX, tmpY, nid, &ecPublicKey);
    if (0 != ret) {
        LOG_E("get ec public key error");
        return ret;
    }

    ret = EC_KEY_to_EVP_PKEY(ecPublicKey, evpKey);
    if (0 != ret) {
        LOG_E("ec key to evp key error");
        EC_KEY_free(ecPublicKey);
        ecPublicKey = NULL;
        return ret;
    }
    EC_KEY_free(ecPublicKey);
    ecPublicKey = NULL;
    return ret;
}

三、在openssl-3.0中的解决方案

注:
1.代码实现中的返回值定义,日志输出函数,内存释放函数需自行实现。
2.如果x,y的二进制数据中包含0x00构建EVP_PKEY结构将失败。

/**
 * @brief                           		   通过x,y,以及算法的nid构建公钥的EVP_PKEY结构
 *
 * @param x[in]                   				ECC算法公钥中,x的数据
 * @param y[in]                					ECC算法公钥中,y的数据
 * @param nid[in]                				当前ECC算法的NID
 * @param evpPublicKey[out]                     公钥的ECVP_PKEY结构
 *
 * @return                         			    0 - success, !0 - error
 */
int get_evp_pkey_from_ecc_xy(const unsigned char *x, const unsigned char *y, int nid, EVP_PKEY **evpPublicKey)
{
	// 判断参数是否为空
   if ((NULL == x) || (NULL == y) || (NULL == evpPublicKey)) {
       LOG_E("param error");
       return ERROR_PARAM;
   }

   if (NULL != (*evpPublicKey)) {
       WY_OPENSSL_FREE(EVP_PKEY, *evpPublicKey);
       *evpPublicKey = NULL;
   }

   unsigned char publicKey[512] = {0};
   int tmpPublicKeyLen = 0;
   char* algStr = NULL;
   switch (nid)
   {
   case NID_X9_62_prime256v1:
       algStr = "prime256v1";
       break;
   default:
       return ERROR_NOT_SUPPORT; 
       break;
   }

   size_t xLen = strlen((const char*)x);
   size_t yLen = strlen((const char*)y);
   // 用于表是数据的格式,通常使用0x04表示,构建公钥数据: 格式: 0x04 + x + y
   int hea = POINT_CONVERSION_UNCOMPRESSED;
   memcpy(publicKey + tmpPublicKeyLen, (unsigned char*)&hea, 1);
   tmpPublicKeyLen += 1;
   memcpy(publicKey + tmpPublicKeyLen, x, xLen);
   tmpPublicKeyLen += xLen;
   memcpy(publicKey + tmpPublicKeyLen, y, yLen);
   size_t publicKeyLen = tmpPublicKeyLen + yLen;

	// 
   OSSL_PARAM_BLD *paramsBuild = OSSL_PARAM_BLD_new();
   if (NULL == paramsBuild) {
       LOG_E("OSSL_PARAM_BLD_new error : %ld, %s", ERR_get_error(), ERR_reason_error_string(ERR_get_error()));
       return 1;
   }
	// 将ECC算法名称构建到结构体中
   OSSL_PARAM_BLD_push_utf8_string(paramsBuild, OSSL_PKEY_PARAM_GROUP_NAME, algStr, 0);
   // 将公钥数据构建到结构体中
   OSSL_PARAM_BLD_push_octet_string(paramsBuild, OSSL_PKEY_PARAM_PUB_KEY, publicKey, publicKeyLen);
   // 将构建器中已经定义的参数转换成可以被 OpenSSL 的其他部分使用的 OSSL_PARAM 数组
   OSSL_PARAM *params = OSSL_PARAM_BLD_to_param(paramsBuild);
   if (NULL == params) {
       LOG_E("OSSL_PARAM_BLD_to_param error : %ld, %s", ERR_get_error(), ERR_reason_error_string(ERR_get_error()));
       WY_OPENSSL_FREE(OSSL_PARAM_BLD, paramsBuild);
       return 1;
   }

	// 根据给定的算法名称创建一个 EVP_PKEY_CTX 上下文
   EVP_PKEY_CTX* ctx = EVP_PKEY_CTX_new_from_name(NULL, "EC", NULL);
   if (NULL == ctx) {
       LOG_E("EVP_PKEY_CTX_new_from_name error : %ld, %s", ERR_get_error(), ERR_reason_error_string(ERR_get_error()));
       WY_OPENSSL_FREE(OSSL_PARAM_BLD, paramsBuild);
       return 1;
   }

   EVP_PKEY *tmpEvpPublicKey = NULL;
   EVP_PKEY_fromdata_init(ctx);
   int status = EVP_PKEY_fromdata(ctx, &tmpEvpPublicKey, EVP_PKEY_PUBLIC_KEY, params);
   if (status <= 0 || tmpEvpPublicKey == NULL) {
       LOG_E("status : %d", status);
       LOG_E("EVP_PKEY_fromdata error : %ld, %s", ERR_get_error(), ERR_reason_error_string(ERR_get_error()));
       WY_OPENSSL_FREE(OSSL_PARAM, params);
       WY_OPENSSL_FREE(OSSL_PARAM_BLD, paramsBuild);
       WY_OPENSSL_FREE(EVP_PKEY_CTX, ctx);
       return 1;
   }

   *evpPublicKey = EVP_PKEY_dup(tmpEvpPublicKey);

   WY_OPENSSL_FREE(EVP_PKEY, tmpEvpPublicKey);
   WY_OPENSSL_FREE(OSSL_PARAM, params);
   WY_OPENSSL_FREE(OSSL_PARAM_BLD, paramsBuild);
   WY_OPENSSL_FREE(EVP_PKEY_CTX, ctx);

   return 0;
}

四、openssl相关函数的内存释放示例

// openssl相关函数的内存释放示例
#define WY_OPENSSL_FREE(name, ptr) openssl_##name##_free(ptr)
void openssl_EVP_PKEY_free(EVP_PKEY *ptr)
{
	if (NULL != ptr) {
        EVP_PKEY_free(ptr);
        ptr = NULL;
    }
}

五、参考资料

openssl-3.0实现已知x,y构建EVP_PKEY结构参考连接

总结

本文主要介绍了如何基于openssl3.0,在已知公钥x,y 以及算法的nid的前提下,如何构建公钥的EVP_PKEY结构。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值