设置private后设置public key有什么用?

What is the purpose of setting public key after setting private?

我想知道为什么我必须在调用 EC_POINT_point2hex 之前调用 EC_KEY_set_public_key 已经调用了 EC_KEY_set_private_key?是不是已经通过设置私钥确定了?

#include <stdio.h>
#include <stdlib.h>
#include <openssl/ec.h>
#include <openssl/obj_mac.h>
#include <openssl/bn.h>
#include <openssl/ossl_typ.h>

int main()
{
     EC_KEY *eckey = NULL;
     EC_POINT *pub_key = NULL;
     const EC_GROUP *group = NULL;
     BIGNUM *priv;
     BN_CTX *ctx;



     priv = BN_new();
     ctx = BN_CTX_new();

     BN_hex2bn(&priv, "EDCC6224FEE390A57C76C13A9BECC9502A6F3B1BF6F72B6ED11B83A0F0E3E9FC");
     eckey = EC_KEY_new_by_curve_name(NID_secp256k1);
     group = EC_KEY_get0_group(eckey);
     pub_key = EC_POINT_new(group);

     EC_KEY_set_private_key(eckey, priv);

     if (!EC_POINT_mul(group, pub_key, priv, NULL, NULL, ctx))
       printf("Error at EC_POINT_mul.\n");

     EC_KEY_set_public_key(eckey, pub_key);

     char *cc = EC_POINT_point2hex(group, pub_key, POINT_CONVERSION_UNCOMPRESSED, ctx);

     char *c=cc;

     int i;

     for (i=0; i<130; i++)
     {
       printf("%c", *c++);
     }

     printf("\n");

     BN_CTX_free(ctx);

     free(cc);

     BN_free(priv);

     return 0;
}

根据1.1.1f版本的libssl1.1,我偶然发现了如下定义:

int EC_KEY_set_private_key(EC_KEY *key, const BIGNUM *priv_key)
{
    if (key->group == NULL || key->group->meth == NULL)
        return 0;
    if (key->group->meth->set_private != NULL
        && key->group->meth->set_private(key, priv_key) == 0)
        return 0;
    if (key->meth->set_private != NULL
        && key->meth->set_private(key, priv_key) == 0)
        return 0;
    BN_clear_free(key->priv_key);
    key->priv_key = BN_dup(priv_key);
    return (key->priv_key == NULL) ? 0 : 1;
}

int EC_KEY_set_public_key(EC_KEY *key, const EC_POINT *pub_key)
{
    if (key->meth->set_public != NULL
        && key->meth->set_public(key, pub_key) == 0)
        return 0;
    EC_POINT_free(key->pub_key);
    key->pub_key = EC_POINT_dup(pub_key, key->group);
    return (key->pub_key == NULL) ? 0 : 1;
}

我试图深入研究 apt source 提供的 libssl1.1 来源,但我没有得到 key->meth->set_private / 'key->meth->set_public' 因为那:

void EC_KEY_METHOD_set_init(EC_KEY_METHOD *meth,
                            int (*init)(EC_KEY *key),
                            void (*finish)(EC_KEY *key),
                            int (*copy)(EC_KEY *dest, const EC_KEY *src),
                            int (*set_group)(EC_KEY *key, const EC_GROUP *grp),
                            int (*set_private)(EC_KEY *key,
                                               const BIGNUM *priv_key),
                            int (*set_public)(EC_KEY *key,
                                              const EC_POINT *pub_key))
{
    meth->init = init;
    meth->finish = finish;
    meth->copy = copy;
    meth->set_group = set_group;
    meth->set_private = set_private;
    meth->set_public = set_public;
}

user@host openssl-1.1.1f$ grep -R "EC_KEY_METHOD_set_init"
util/libcrypto.num:EC_KEY_METHOD_set_init                  3777 1_1_0   EXIST::FUNCTION:EC
include/openssl/ec.h:void EC_KEY_METHOD_set_init(EC_KEY_METHOD *meth,
crypto/ec/ec_kmeth.c:void EC_KEY_METHOD_set_init(EC_KEY_METHOD *meth,
user@host openssl-1.1.1f$

那么...为什么在调用 pub_key 作为参数的 EC_POINT_point2hex 之前必须将 pub_key 设置为 eckey

EC_KEY_METHOD_set_init 用于修改默认方法,但未使用。默认情况下函数指针 set_private 和 set_pucblic 等于 0.

static const EC_KEY_METHOD openssl_ec_key_method = {
    "OpenSSL EC_KEY method",
    0,
    0,0,0,0,0,0,
    ossl_ec_key_gen,
    ossl_ecdh_compute_key,
    ossl_ecdsa_sign,
    ossl_ecdsa_sign_setup,
    ossl_ecdsa_sign_sig,
    ossl_ecdsa_verify,
    ossl_ecdsa_verify_sig
};

static const EC_KEY_METHOD *default_ec_key_meth = &openssl_ec_key_method;

EC_KEY_METHOD的定义是:

struct ec_key_method_st {
    const char *name;
    int32_t flags;
    int (*init)(EC_KEY *key);
    void (*finish)(EC_KEY *key);
    int (*copy)(EC_KEY *dest, const EC_KEY *src);
    int (*set_group)(EC_KEY *key, const EC_GROUP *grp);
    int (*set_private)(EC_KEY *key, const BIGNUM *priv_key);
    int (*set_public)(EC_KEY *key, const EC_POINT *pub_key);
    int (*keygen)(EC_KEY *key);
    int (*compute_key)(unsigned char **pout, size_t *poutlen,
                       const EC_POINT *pub_key, const EC_KEY *ecdh);
    int (*sign)(int type, const unsigned char *dgst, int dlen, unsigned char
                *sig, unsigned int *siglen, const BIGNUM *kinv,
                const BIGNUM *r, EC_KEY *eckey);
    int (*sign_setup)(EC_KEY *eckey, BN_CTX *ctx_in, BIGNUM **kinvp,
                      BIGNUM **rp);
    ECDSA_SIG *(*sign_sig)(const unsigned char *dgst, int dgst_len,
                           const BIGNUM *in_kinv, const BIGNUM *in_r,
                           EC_KEY *eckey);

    int (*verify)(int type, const unsigned char *dgst, int dgst_len,
                  const unsigned char *sigbuf, int sig_len, EC_KEY *eckey);
    int (*verify_sig)(const unsigned char *dgst, int dgst_len,
                      const ECDSA_SIG *sig, EC_KEY *eckey);
};

因此 EC_KEY_set_private_key 将仅设置 priv_key 而不是 pub_key,后者必须由 EC_KEY_set_public_key 设置。一般来说,拥有私钥并不意味着拥有public密钥,而是意味着我们可以计算它。所以最好保存起来,免得重做几次重算。

最后,EC_POINT_point2hex 允许以十六进制格式打印一个点。 y你打印 pub_key 但它可能是曲线上的任何一点。