博客專欄

        EEPW首頁 > 博客 > 基于openssl3.0,已知ECC算法x,y如何構建公鑰的EVP_PKEY結構

        基于openssl3.0,已知ECC算法x,y如何構建公鑰的EVP_PKEY結構

        發布人:電子禪石 時間:2025-02-25 來源:工程師 發布文章

        前言

        在將項目從 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結構

        1

        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結構中

        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;

            }

        2.4完整代碼

        1

        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;

        }



        ————————————————


                                

        原文鏈接:https://blog.csdn.net/dream_yaya/article/details/140553623


        *博客內容為網友個人發布,僅代表博主個人觀點,如有侵權請聯系工作人員刪除。



        關鍵詞: openssl

        技術專區

        關閉
        主站蜘蛛池模板: 屯昌县| 乌审旗| 唐河县| 肇源县| 蓬溪县| 赫章县| 乡城县| 阳新县| 额济纳旗| 类乌齐县| 潍坊市| 龙州县| 西乡县| 开原市| 唐河县| 博罗县| 深圳市| 苍梧县| 师宗县| 松潘县| 洛扎县| 屏东市| 丹寨县| 稻城县| 梁山县| 兴国县| 安徽省| 建平县| 文安县| 玉溪市| 莱芜市| 彩票| 惠水县| 庆元县| 玉门市| 开封县| 班玛县| 神池县| 江山市| 资兴市| 离岛区|