OpenSSL 3.0从MPI表示的私钥创建PEM格式的EC私钥

20jt8wwn  于 2023-03-07  发布在  其他
关注(0)|答案(1)|浏览(293)

我想将一个生成PEM格式的EC私钥的函数更新到OpenSSL 3.0。大多数使用的EC函数现在都被弃用了。下面你可以看到被弃用的函数。

ERR Security_GetECKey(KeyPairECType enKeyPairECType, const unsigned char *puchKey, unsigned int uiLength, char **ppstPEMKey)
{
    ERR err;
    int iNID;
    BIGNUM *pBigNumPrivate;
    BN_CTX *pBigNumCtx;
    EC_KEY *pEcKey;
    EC_POINT *pEcPoint;
    const EC_GROUP *pEcGroup;
    EVP_PKEY *pEVPPKey;
    int iBIOLength;
    BIO *pBIOOut;
    const unsigned char *puchPEMKey;

    err = ERR_NONE;

    if((enKeyPairECType > KeyPairECType_Max) ||
       (ppstPEMKey == NULL) ||
       (uiLength == 0) ||
       (puchKey == NULL))
    {
        err = Err_Code(ERR_INVALID_PARAM);
    }
    else
    {
        switch(enKeyPairECType)
        {
            case KeyPairECType_NID_SECP521r1:
                iNID = NID_secp521r1;
                break;
            default:
                iNID = 0;
                err = Err_Code(ERR_INVALID_PARAM);
                break;
        }

        if(err == ERR_NONE)
        {
            THREAD_LOCK_LIB_TOOLS();
            /*
             * Use length from mpi header + 4. instead of length of whole char array.
             * Length of char array (uiLength) could be longer than mpi
             */
            pBigNumPrivate = BN_mpi2bn((unsigned char*)puchKey, ((puchKey[0]<<24) | (puchKey[1]<<16) | (puchKey[2]<<8) | puchKey[3]) + 4, NULL);
            if(pBigNumPrivate != NULL)
            {
                pBigNumCtx = BN_CTX_new();
                if(pBigNumCtx != NULL)
                {
                    /*
                     * TODO: The low-level EC_KEY_... API functions are deprecated with OpenSSL 3.0!
                     * Ignore the warnings for now until the source code is adapted to use
                     * the appropriate high-level APIs.
                     */
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
                    pEcKey = EC_KEY_new_by_curve_name(iNID);
                    if(pEcKey != NULL)
                    {
                        pEcGroup = EC_KEY_get0_group(pEcKey);
                        if(pEcGroup != NULL)
                        {
                            pEcPoint = EC_POINT_new(pEcGroup);
                            if(pEcPoint != NULL)
                            {
                                if(EC_KEY_set_private_key(pEcKey, pBigNumPrivate) != FALSE)
                                {
                                    if(EC_POINT_mul(pEcGroup, pEcPoint, pBigNumPrivate, NULL, NULL, pBigNumCtx) != FALSE)
                                    {
                                        if(EC_KEY_set_public_key(pEcKey, pEcPoint) != FALSE)
                                        {
                                            pEVPPKey = EVP_PKEY_new();
                                            if(pEVPPKey != NULL)
                                            {
                                                if(EVP_PKEY_set1_EC_KEY(pEVPPKey, pEcKey) != FALSE)
                                                {
                                                    pBIOOut = BIO_new(BIO_s_mem());
                                                    if(pBIOOut != NULL)
                                                    {
                                                        gstPassword = NULL;
                                                        if((PEM_write_bio_PrivateKey(pBIOOut, pEVPPKey, NULL, NULL, 0, Security_PEM_Password_CB, NULL)) == 0)
                                                        {
                                                            err = Err_Code(ERR_TOOLS_SECURITY_GET_EC_KEY);
                                                        }
                                                        else
                                                        {
                                                            iBIOLength = BIO_get_mem_data(pBIOOut, &puchPEMKey);
                                                            if(iBIOLength > 0)
                                                            {
                                                                err = Mem_AllocEx((void **)ppstPEMKey, (iBIOLength + 1) * sizeof(char));
                                                                if(err == ERR_NONE)
                                                                {
                                                                    memcpy(*ppstPEMKey, puchPEMKey, iBIOLength);
                                                                    (*ppstPEMKey)[iBIOLength] = '\00';
                                                                }
                                                            }
                                                            else
                                                            {
                                                                err = Err_Code(ERR_TOOLS_SECURITY_GET_EC_KEY);
                                                            }

                                                        }
                                                        BIO_free(pBIOOut);
                                                    }
                                                    EVP_PKEY_free(pEVPPKey);
                                                }
                                                else
                                                {
                                                    err = Err_Code(ERR_TOOLS_SECURITY_GET_EC_KEY);
                                                }
                                            }
                                            else
                                            {
                                                err = Err_Code(ERR_TOOLS_SECURITY_GET_EC_KEY);
                                            }

                                        }
                                        else
                                        {
                                            err = Err_Code(ERR_TOOLS_SECURITY_GET_EC_KEY);
                                        }
                                    }
                                    else
                                    {
                                        err = Err_Code(ERR_TOOLS_SECURITY_GET_EC_KEY);
                                    }
                                }
                                else
                                {
                                    err = Err_Code(ERR_TOOLS_SECURITY_GET_EC_KEY);
                                }
                            }
                            else
                            {
                                err = Err_Code(ERR_TOOLS_SECURITY_GET_EC_KEY);
                            }
                        }
                        else
                        {
                            err = Err_Code(ERR_TOOLS_SECURITY_GET_EC_KEY);
                        }
                        EC_KEY_free(pEcKey);
                    }
                    /*
                     * TODO: Remove end of #pragma above if adapted!
                     */
#pragma GCC diagnostic pop
                    BN_CTX_free(pBigNumCtx);
                }
                else
                {
                    err = Err_Code(ERR_TOOLS_SECURITY_GET_EC_KEY);
                }
                BN_free(pBigNumPrivate);
            }
            else
            {
                err = Err_Code(ERR_TOOLS_SECURITY_GET_EC_KEY);
            }

            THREAD_UNLOCK_LIB_TOOLS();
        }
    }

    return err;
}

我曾尝试用OpenSSL 3.0支持的EVP密钥函数替换过时的EC函数。以下是我的最新尝试:

ERR Security_GetECKey(KeyPairECType enKeyPairECType, const unsigned char *puchKey, unsigned int uiLength, char **ppstPEMKey)
{
    ERR err;

    THREAD_INIT_LOCK_LIB_TOOLS();

    err = ERR_NONE;

    if((enKeyPairECType > KeyPairECType_Max) ||
       (ppstPEMKey == NULL) ||
       (uiLength == 0) ||
       (puchKey == NULL))
    {
        err = Err_Code(ERR_INVALID_PARAM);
    }
    else
    {
        const char *pstCurveName;

        switch(enKeyPairECType)
        {
            case KeyPairECType_NID_SECP521r1:
                pstCurveName = SN_secp521r1;
                break;
            default:
                pstCurveName = NULL;
                err = Err_Code(ERR_INVALID_PARAM);
                break;
        }

        if(err == ERR_NONE)
        {
            BIGNUM *pBigNumPrivate;

            THREAD_LOCK_LIB_TOOLS();
            /*
             * Use length from mpi header + 4. instead of length of whole char array.
             * Length of char array (uiLength) could be longer than mpi
             */
            pBigNumPrivate = BN_mpi2bn((unsigned char *) puchKey, ((puchKey[0] << 24) | (puchKey[1] << 16) | (puchKey[2] << 8) | puchKey[3]) + 4, NULL);
            if(pBigNumPrivate != NULL)
            {
                int iNid = OBJ_sn2nid(pstCurveName);
                if(iNid != NID_undef)
                {
                    EC_GROUP *pEcGroup = EC_GROUP_new_by_curve_name(iNid);
                    if(pEcGroup != NULL)
                    {
                        EC_POINT *pEcPoint = EC_POINT_new(pEcGroup);
                        BN_CTX *pBigNumCtx = BN_CTX_new();
                        if((pEcPoint != NULL) && (pBigNumCtx != NULL) && EC_POINT_mul(pEcGroup, pEcPoint, pBigNumPrivate, NULL, NULL, pBigNumCtx))
                        {
                            size_t uzPubKeySize = EC_POINT_point2oct(pEcGroup, pEcPoint, POINT_CONVERSION_COMPRESSED, NULL, 0, NULL);
                            if(uzPubKeySize > 0)
                            {
                                unsigned char *puchPubKey = NULL;
                                err = Mem_AllocEx((void **) &puchPubKey, uzPubKeySize);
                                if((err == ERR_NONE) && (EC_POINT_point2oct(pEcGroup, pEcPoint, POINT_CONVERSION_COMPRESSED, puchPubKey, uzPubKeySize, NULL)))
                                {
                                    OSSL_PARAM_BLD *paramBuild = OSSL_PARAM_BLD_new();
                                    if((paramBuild != NULL) &&
                                       OSSL_PARAM_BLD_push_utf8_string(paramBuild, OSSL_PKEY_PARAM_GROUP_NAME, pstCurveName, 0) &&
                                       OSSL_PARAM_BLD_push_BN(paramBuild, OSSL_PKEY_PARAM_PRIV_KEY, pBigNumPrivate) &&
                                       OSSL_PARAM_BLD_push_octet_ptr(paramBuild, OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY, puchPubKey, uzPubKeySize))
                                    {
                                        EVP_PKEY *pEVPPKey = NULL;
                                        OSSL_PARAM *pParams = OSSL_PARAM_BLD_to_param(paramBuild);
                                        EVP_PKEY_CTX *pKeyCtx = EVP_PKEY_CTX_new_from_name(NULL, "EC", NULL);

                                        if((pParams != NULL) &&
                                           (pKeyCtx != NULL) &&
                                           (EVP_PKEY_fromdata_init(pKeyCtx) > 0) &&
                                           ((EVP_PKEY_fromdata(pKeyCtx, &pEVPPKey, EVP_PKEY_KEYPAIR, pParams)) > 0))
                                        {
                                            EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(pEVPPKey, NULL);
                                            if(EVP_PKEY_check(ctx) <= 0)
                                                ERR_print_errors_fp(stderr);

                                            BIO *pBIOOut = BIO_new(BIO_s_mem());
                                            if(pBIOOut != NULL)
                                            {
                                                if((PEM_write_bio_PrivateKey(pBIOOut, pEVPPKey, NULL, NULL, 0, NULL, NULL)) == 0)
                                                {
                                                    ERR_print_errors_fp(stderr);
                                                    err = Err_Code(ERR_TOOLS_SECURITY_GET_EC_KEY);
                                                }
                                                else
                                                {
                                                    const unsigned char *puchPEMKey;
                                                    int iBIOLength = BIO_get_mem_data(pBIOOut, &puchPEMKey);
                                                    if(iBIOLength > 0)
                                                    {
                                                        err = Mem_AllocEx((void **) ppstPEMKey, (iBIOLength + 1) * sizeof(char));
                                                        if(err == ERR_NONE)
                                                        {
                                                            memcpy(*ppstPEMKey, puchPEMKey, iBIOLength);
                                                            (*ppstPEMKey)[iBIOLength] = '\00';
                                                        }
                                                    }
                                                    else
                                                    {
                                                        err = Err_Code(ERR_TOOLS_SECURITY_GET_EC_KEY);
                                                    }
                                                }
                                                BIO_free(pBIOOut);
                                            }
                                            else
                                            {
                                                err = Err_Code(ERR_TOOLS_SECURITY_GET_EC_KEY);
                                            }
                                            EVP_PKEY_free(pEVPPKey);
                                        }
                                        else
                                        {
                                            err = Err_Code(ERR_TOOLS_SECURITY_GET_EC_KEY);
                                        }
                                        EVP_PKEY_CTX_free(pKeyCtx);
                                        OSSL_PARAM_free(pParams);
                                    }
                                    else
                                    {
                                        err = Err_Code(ERR_TOOLS_SECURITY_GET_EC_KEY);
                                    }
                                    OSSL_PARAM_BLD_free(paramBuild);
                                }
                                else
                                {
                                    err = Err_Code(ERR_TOOLS_SECURITY_GET_EC_KEY);
                                }
                                Mem_Free((void **) &puchPubKey);
                            }
                            else
                            {
                                err = Err_Code(ERR_TOOLS_SECURITY_GET_EC_KEY);
                            }
                        }
                        else
                        {
                            err = Err_Code(ERR_TOOLS_SECURITY_GET_EC_KEY);
                        }
                        EC_POINT_free(pEcPoint);
                        BN_CTX_free(pBigNumCtx);
                    }
                    else
                    {
                        err = Err_Code(ERR_TOOLS_SECURITY_GET_EC_KEY);
                    }
                    EC_GROUP_free(pEcGroup);
                }
                else
                {
                    err = Err_Code(ERR_TOOLS_SECURITY_GET_EC_KEY);
                }
            }
            else
            {
                err = Err_Code(ERR_TOOLS_SECURITY_GET_EC_KEY);
            }
            BN_free(pBigNumPrivate);
        }
        else
        {
            err = Err_Code(ERR_TOOLS_SECURITY_GET_EC_KEY);
        }
        THREAD_UNLOCK_LIB_TOOLS();
    }
    return err;
}

看起来我没有创建创建PEM密钥所需的EVP结构的所有参数。密钥生成后的检查返回以下错误:

C01BFCF7FF7F0000:error:080C0102:elliptic curve routines:ossl_ec_key_public_check_quick:passed a null parameter:crypto/ec/ec_key.c:444:

PEM_write_bio_PrivateKey之后,最终输出以下错误:

C01BFCF7FF7F0000:error:080C0102:elliptic curve routines:i2d_ECPrivateKey:passed a null parameter:crypto/ec/ec_asn1.c:1031:
C01BFCF7FF7F0000:error:1C8C0100:Provider routines:key_to_p8info:malloc failure:providers/implementations/encode_decode/encode_key2any.c:94:

我已经没有什么想法了,OpenSSL手册页也没有什么帮助,有人能帮我吗?

nx7onnlm

nx7onnlm1#

据我所知,新的高级接口不能创建一个“密钥对”PKEY,其中的public是从一个原始的private派生出来的,您可以做的是创建一个private-only PKEY并将其写出来--根据RFC 5915(又名SEC 1)和由知道自己在做什么的人编写的软件(如OpenSSL),这是有效的,但不推荐(至少在这种情况下!),* 将 * 阅读它。然而,很多软件不是由这样的人编写的,可能会拒绝或错误处理这样的文件。在这种情况下,或者如果你只是真的想要一个密钥对,你能做的就是把私钥“写出”内存,然后“读回”-- * API * 做的 *(内部)从private派生public,(编辑)导致,但它也将编码标志设置为no-public,因此您必须修复它以获得可以作为密钥对编写的PKEY。
我真的不喜欢“if precondition 1 if precondition 2 if precondition 3......”的风格,这种风格会导致代码中的正常情况,我通常关心的情况,被缩进到最右边,我不得不到邻居家去读它(特别是在像现在这样的冬天),所以我使用了微不足道的abort-on-error;您几乎肯定希望将其更改回适合您的程序设计的内容。

// 75474348.c 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/bio.h>
#include <openssl/core_names.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/param_build.h>
#include <openssl/pem.h>
#include <openssl/rand.h>

void fail (const char * str, int data)
{ fprintf(stderr, "%s:%d\n", str, data); ERR_print_errors_fp(stderr); exit(1); }

int main (void)
{
    // fake MPI input for testing
    unsigned char puchKey[4+66] = {0,0,0,66}; /* unsigned int uiLength */
    RAND_bytes(puchKey+4+1,66-1);

    const char *pstCurveName = SN_secp521r1;

    BIGNUM *pBigNumPrivate = BN_mpi2bn((unsigned char *) puchKey, ((puchKey[0] << 24) | (puchKey[1] << 16) | (puchKey[2] << 8) | puchKey[3]) + 4, NULL);
    if(pBigNumPrivate == NULL) fail("BN_mpi2bin",0);

    OSSL_PARAM_BLD *paramBuild = OSSL_PARAM_BLD_new();
    if((paramBuild == NULL) ) fail("OSSL_PARAM_BLD_new",0);
    if(!OSSL_PARAM_BLD_push_utf8_string(paramBuild, OSSL_PKEY_PARAM_GROUP_NAME, pstCurveName, 0) 
        ||!OSSL_PARAM_BLD_push_BN(paramBuild, OSSL_PKEY_PARAM_PRIV_KEY, pBigNumPrivate) 
        ||!OSSL_PARAM_BLD_push_int(paramBuild, OSSL_PKEY_PARAM_EC_INCLUDE_PUBLIC, 0/*no*/)
        ) fail("OSS_PARAM_BLD_push-3",0);
    OSSL_PARAM *pParams = OSSL_PARAM_BLD_to_param(paramBuild);
    EVP_PKEY_CTX *pKeyCtx = EVP_PKEY_CTX_new_from_name(NULL, "EC", NULL);
    EVP_PKEY *pEVPPKey = NULL;
    if((pParams == NULL) || (pKeyCtx == NULL) ) fail("OSSL_PARAM_BLD_to_param|EVP_PKEY_CTX_new_",0);
    if(EVP_PKEY_fromdata_init(pKeyCtx) <= 0
        ||EVP_PKEY_fromdata(pKeyCtx, &pEVPPKey, OSSL_KEYMGMT_SELECT_ALL_PARAMETERS|OSSL_KEYMGMT_SELECT_PRIVATE_KEY/*EVP_PKEY_KEYPAIR*/, pParams) <= 0 )
        fail("EVP_PKEY_fromdata-2",0);

#if want_to_convert_internally_to_include_private
    unsigned char buf[50+70], *ptr = buf;
    if( i2d_PrivateKey(pEVPPKey,&ptr) <= 0 ) fail("i2d",0);
    size_t len = ptr-buf; ptr = buf;
    if( !d2i_PrivateKey(EVP_PKEY_EC,&pEVPPKey,&ptr,len) ) fail("d2i",0);
    if( !EVP_PKEY_set_int_param(pEVPPKey,"include-public",1) ) fail("EVP_PKEY_set-include-public",0);
#endif

    BIO *pBIOOut = BIO_new(BIO_s_mem());
    if(pBIOOut == NULL) fail("BIO_new",0);
    if((PEM_write_bio_PrivateKey(pBIOOut, pEVPPKey, NULL, NULL, 0, NULL, NULL)) == 0) fail("PEM_write_bio",0);
    const unsigned char *puchPEMKey;
    int iBIOLength = BIO_get_mem_data(pBIOOut, &puchPEMKey);
    if(iBIOLength <= 0) fail("BIO_get_mem_data",iBIOLength);
#if 0
    if( !(*ppstPEMKey = malloc (iBIOLength + 1)) ) abort();
    memcpy(*ppstPEMKey, puchPEMKey, iBIOLength);
    (*ppstPEMKey)[iBIOLength] = '\00';
#else // output to user for testing
    fwrite(puchPEMKey,1,iBIOLength,stdout);
#endif

    BIO_free(pBIOOut);
    EVP_PKEY_free(pEVPPKey);
    EVP_PKEY_CTX_free(pKeyCtx);
    OSSL_PARAM_free(pParams);
    OSSL_PARAM_BLD_free(paramBuild);
}

相关问题