tsx81428 发表于 2024-8-14 15:48:15

[openSSL]tls_construct_cert_verify署名流程

前言

tls_construct_cert_verify是openSSL源码中的一个方法,用于进行TLS握手阶段的署名流程。
从概念上讲,数字署名的流程是如许的:

[*]客户端根据一段公共消息天生消息摘要,一样平常是使用sha256算法。
[*]客户端对消息摘要使用私钥进行署名,将署名后的消息发送给服务端。
[*]服务端收到署名消息后,使用客户端公钥处理来得到摘要消息。对公共消息也使用sha256算法来提取摘要,对比摘要,成功则能够验证客户端的身份
流程图
一、流程图

https://i-blog.csdnimg.cn/direct/f890e8a996f04d42b2313ec70cd31492.png
二、tls_construct_cert_verify代码分析

int tls_construct_cert_verify(SSL *s, WPACKET *pkt)
{
    EVP_PKEY *pkey = NULL;
    const EVP_MD *md = NULL;
    EVP_MD_CTX *mctx = NULL;
    EVP_PKEY_CTX *pctx = NULL;
    size_t hdatalen = 0, siglen = 0;
    void *hdata;
    unsigned char *sig = NULL;
    unsigned char tls13tbs;
    const SIGALG_LOOKUP *lu = s->s3->tmp.sigalg;

    if (lu == NULL || s->s3->tmp.cert == NULL) {
      SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_CONSTRUCT_CERT_VERIFY,
               ERR_R_INTERNAL_ERROR);
      goto err;
    }
    pkey = s->s3->tmp.cert->privatekey;

    if (pkey == NULL || !tls1_lookup_md(lu, &md)) {
      SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_CONSTRUCT_CERT_VERIFY,
               ERR_R_INTERNAL_ERROR);
      goto err;
    }

    mctx = EVP_MD_CTX_new();
    if (mctx == NULL) {
      SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_CONSTRUCT_CERT_VERIFY,
               ERR_R_MALLOC_FAILURE);
      goto err;
    }

    /* Get the data to be signed */
    if (!get_cert_verify_tbs_data(s, tls13tbs, &hdata, &hdatalen)) {
      /* SSLfatal() already called */
      goto err;
    }

    if (SSL_USE_SIGALGS(s) && !WPACKET_put_bytes_u16(pkt, lu->sigalg)) {
      SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_CONSTRUCT_CERT_VERIFY,
               ERR_R_INTERNAL_ERROR);
      goto err;
    }
    siglen = EVP_PKEY_size(pkey);
    sig = OPENSSL_malloc(siglen);
    if (sig == NULL) {
      SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_CONSTRUCT_CERT_VERIFY,
               ERR_R_MALLOC_FAILURE);
      goto err;
    }

    if (EVP_DigestSignInit(mctx, &pctx, md, NULL, pkey) <= 0) {
      SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_CONSTRUCT_CERT_VERIFY,
               ERR_R_EVP_LIB);
      goto err;
    }

    if (lu->sig == EVP_PKEY_RSA_PSS) {
      if (EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PSS_PADDING) <= 0
            || EVP_PKEY_CTX_set_rsa_pss_saltlen(pctx,
                                                RSA_PSS_SALTLEN_DIGEST) <= 0) {
            SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_CONSTRUCT_CERT_VERIFY,
                     ERR_R_EVP_LIB);
            goto err;
      }
    }
    if (s->version == SSL3_VERSION) {
      if (EVP_DigestSignUpdate(mctx, hdata, hdatalen) <= 0
            || !EVP_MD_CTX_ctrl(mctx, EVP_CTRL_SSL3_MASTER_SECRET,
                              (int)s->session->master_key_length,
                              s->session->master_key)
            || EVP_DigestSignFinal(mctx, sig, &siglen) <= 0) {

            SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_CONSTRUCT_CERT_VERIFY,
                     ERR_R_EVP_LIB);
            goto err;
      }
    } else if (EVP_DigestSign(mctx, sig, &siglen, hdata, hdatalen) <= 0) {
      SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_CONSTRUCT_CERT_VERIFY,
               ERR_R_EVP_LIB);
      goto err;
    }

#ifndef OPENSSL_NO_GOST
    {
      int pktype = lu->sig;

      if (pktype == NID_id_GostR3410_2001
            || pktype == NID_id_GostR3410_2012_256
            || pktype == NID_id_GostR3410_2012_512)
            BUF_reverse(sig, NULL, siglen);
    }
#endif

    if (!WPACKET_sub_memcpy_u16(pkt, sig, siglen)) {
      SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_CONSTRUCT_CERT_VERIFY,
               ERR_R_INTERNAL_ERROR);
      goto err;
    }

    /* Digest cached records and discard handshake buffer */
    if (!ssl3_digest_cached_records(s, 0)) {
      /* SSLfatal() already called */
      goto err;
    }

    OPENSSL_free(sig);
    EVP_MD_CTX_free(mctx);
    return 1;
err:
    OPENSSL_free(sig);
    EVP_MD_CTX_free(mctx);
    return 0;
}

[*]tls1_lookup_md(lu, &md)获取天生消息摘要的算法md。
[*]get_cert_verify_tbs_data(s, tls13tbs, &hdata, &hdatalen)天生待处理的数据hdata。
[*]EVP_DigestSignInit(mctx, &pctx, md, NULL, pkey)初始化
[*]EVP_DigestSign(mctx, sig, &siglen, hdata, hdatalen) 提取消息摘要,并进行署名
1.EVP_DigestSignInit(mctx, &pctx, md, NULL, pkey)初始化

int EVP_DigestSignInit(EVP_MD_CTX *ctx, EVP_PKEY_CTX **pctx,
                     const EVP_MD *type, ENGINE *e, EVP_PKEY *pkey)
{
    return do_sigver_init(ctx, pctx, type, e, pkey, 0);
}

static int do_sigver_init(EVP_MD_CTX *ctx, EVP_PKEY_CTX **pctx,
                        const EVP_MD *type, ENGINE *e, EVP_PKEY *pkey,
                        int ver)
{
    if (ctx->pctx == NULL)
      ctx->pctx = EVP_PKEY_CTX_new(pkey, e);
    if (ctx->pctx == NULL)
      return 0;

    if (!(ctx->pctx->pmeth->flags & EVP_PKEY_FLAG_SIGCTX_CUSTOM)) {

      if (type == NULL) {
            int def_nid;
            if (EVP_PKEY_get_default_digest_nid(pkey, &def_nid) > 0)
                type = EVP_get_digestbynid(def_nid);
      }

      if (type == NULL) {
            EVPerr(EVP_F_DO_SIGVER_INIT, EVP_R_NO_DEFAULT_DIGEST);
            return 0;
      }
    }

    if (ver) {
      if (ctx->pctx->pmeth->verifyctx_init) {
            if (ctx->pctx->pmeth->verifyctx_init(ctx->pctx, ctx) <= 0)
                return 0;
            ctx->pctx->operation = EVP_PKEY_OP_VERIFYCTX;
      } else if (ctx->pctx->pmeth->digestverify != 0) {
            ctx->pctx->operation = EVP_PKEY_OP_VERIFY;
            ctx->update = update;
      } else if (EVP_PKEY_verify_init(ctx->pctx) <= 0) {
            return 0;
      }
    } else {
      if (ctx->pctx->pmeth->signctx_init) {
            if (ctx->pctx->pmeth->signctx_init(ctx->pctx, ctx) <= 0)
                return 0;
            ctx->pctx->operation = EVP_PKEY_OP_SIGNCTX;
      } else if (ctx->pctx->pmeth->digestsign != 0) {
            ctx->pctx->operation = EVP_PKEY_OP_SIGN;
            ctx->update = update;
      } else if (EVP_PKEY_sign_init(ctx->pctx) <= 0) {
            return 0;
      }
    }
    if (EVP_PKEY_CTX_set_signature_md(ctx->pctx, type) <= 0)
      return 0;
    if (pctx)
      *pctx = ctx->pctx;
    if (ctx->pctx->pmeth->flags & EVP_PKEY_FLAG_SIGCTX_CUSTOM)
      return 1;
    if (!EVP_DigestInit_ex(ctx, type, e))
      return 0;
    /*
   * This indicates the current algorithm requires
   * special treatment before hashing the tbs-message.
   */
    if (ctx->pctx->pmeth->digest_custom != NULL)
      return ctx->pctx->pmeth->digest_custom(ctx->pctx, ctx);

    return 1;
}
这里引入了2个概念EVP_MD_CTX 和EVP_PKEY_CTX
EVP_MD_CTX 是消息摘要上下文,包罗待署名的数据。
md_data既是其中的数据,当进行署名时,会更新md_data。
EVP_MD_CTX 包罗了EVP_PKEY_CTX
struct evp_md_ctx_st {
    const EVP_MD *digest;       //提取摘要消息的方法
    ENGINE *engine;             //engine,如果有自定义的engine,则会赋该值
    unsigned long flags;
    void *md_data;            //数据
    /* Public key context for sign/verify */
    EVP_PKEY_CTX *pctx;         //EVP_PKEY_CTX对象
    /* Update function: usually copied from EVP_MD */
    //update方法,一般就是digest提取摘要消息的方法中的update。
    int (*update) (EVP_MD_CTX *ctx, const void *data, size_t count);
}


[*]
EVP_PKEY_CTX是密钥上下文,包罗了密钥和其他加密相关的信息。EVP_PKEY_CTX包罗了EVP_PKEY,即包罗了密钥对象pkey。
struct evp_pkey_ctx_st {
    /* Method associated with this operation */
    const EVP_PKEY_METHOD *pmeth;       //签名的方法
    /* Engine that implements this method or NULL if builtin */
    ENGINE *engine;                     //自定义的engine
    /* Key: may be NULL */
    EVP_PKEY *pkey;                     //私钥
    /* Peer key for key agreement, may be NULL */
    EVP_PKEY *peerkey;
    /* Actual operation */
    int operation;
    /* Algorithm specific data */
    void *data;
    /* Application specific data */
    void *app_data;
    /* Keygen callback */
    EVP_PKEY_gen_cb *pkey_gencb;
    /* implementation specific keygen data */
    int *keygen_info;
    int keygen_info_count;
}
EVP_PKEY是一个密钥的结构,可能包罗多种范例的密钥,好比EC、RSA、DSA等
struct evp_pkey_st {
    int type;
    int save_type;
    CRYPTO_REF_COUNT references;
    const EVP_PKEY_ASN1_METHOD *ameth;
    ENGINE *engine;
    ENGINE *pmeth_engine; /* If not NULL public key ENGINE to use */
    union {
      void *ptr;
# ifndef OPENSSL_NO_RSA
      struct rsa_st *rsa;   /* RSA */
# endif
# ifndef OPENSSL_NO_DSA
      struct dsa_st *dsa;   /* DSA */
# endif
# ifndef OPENSSL_NO_DH
      struct dh_st *dh;       /* DH */
# endif
# ifndef OPENSSL_NO_EC
      struct ec_key_st *ec;   /* ECC */
      ECX_KEY *ecx;         /* X25519, X448, Ed25519, Ed448 */
# endif
    } pkey;
    int save_parameters;
    STACK_OF(X509_ATTRIBUTE) *attributes; /* [ 0 ] */
    CRYPTO_RWLOCK *lock;
}
EVP_PKEY_CTX_new

do_sigver_init方法中首先调用了EVP_PKEY_CTX_new
EVP_PKEY_CTX *EVP_PKEY_CTX_new(EVP_PKEY *pkey, ENGINE *e)
{
    return int_ctx_new(pkey, e, -1);
}

static EVP_PKEY_CTX *int_ctx_new(EVP_PKEY *pkey, ENGINE *e, int id)
{
    EVP_PKEY_CTX *ret;
    const EVP_PKEY_METHOD *pmeth;

    if (id == -1) {
      if (pkey == NULL)
            return 0;
      id = pkey->type;
    }
#ifndef OPENSSL_NO_ENGINE
    if (e == NULL && pkey != NULL)
      e = pkey->pmeth_engine != NULL ? pkey->pmeth_engine : pkey->engine;
    /* Try to find an ENGINE which implements this method */
    if (e) {
      if (!ENGINE_init(e)) {
            EVPerr(EVP_F_INT_CTX_NEW, ERR_R_ENGINE_LIB);
            return NULL;
      }
    } else {
      e = ENGINE_get_pkey_meth_engine(id);
    }

    /*
   * If an ENGINE handled this method look it up. Otherwise use internal
   * tables.
   */
    if (e)
      pmeth = ENGINE_get_pkey_meth(e, id);
    else
#endif
      pmeth = EVP_PKEY_meth_find(id);

    if (pmeth == NULL) {
#ifndef OPENSSL_NO_ENGINE
      ENGINE_finish(e);
#endif
      EVPerr(EVP_F_INT_CTX_NEW, EVP_R_UNSUPPORTED_ALGORITHM);
      return NULL;
    }

    ret = OPENSSL_zalloc(sizeof(*ret));
    if (ret == NULL) {
#ifndef OPENSSL_NO_ENGINE
      ENGINE_finish(e);
#endif
      EVPerr(EVP_F_INT_CTX_NEW, ERR_R_MALLOC_FAILURE);
      return NULL;
    }
    ret->engine = e;
    ret->pmeth = pmeth;
    ret->operation = EVP_PKEY_OP_UNDEFINED;
    ret->pkey = pkey;
    if (pkey != NULL)
      EVP_PKEY_up_ref(pkey);

    if (pmeth->init) {
      if (pmeth->init(ret) <= 0) {
            ret->pmeth = NULL;
            EVP_PKEY_CTX_free(ret);
            return NULL;
      }
    }

    return ret;
}
由于这里的参数传入的engine为NULL,所以ENGINE_get_pkey_meth_engine(id);,这个方法会根据nid来查找是否存在实现了对应方法的engine。
由于在原生流程中没有该engine,那么调用pmeth = EVP_PKEY_meth_find(id);来根据nid查找EVP_PKEY_METHOD。EVP_PKEY_meth_find是根据nid来在standard_methods中查找默认的实现。这里由于是ecc的,所以返回的是&ec_pkey_meth。
得到了EVP_PKEY_METHOD后,便通过ret->pmeth = pmeth;来给EVP_PKEY_CTX中的pmeth 赋值。
即ctx->pctx->pmeth 便有值了。
EVP_PKEY_sign_init(ctx->pctx)

之后由于一些判断条件(原生的ec_pkey_meth没有signctx_init方法),便进入EVP_PKEY_sign_init(ctx->pctx)
int EVP_PKEY_sign_init(EVP_PKEY_CTX *ctx)
{
    int ret;
    if (!ctx || !ctx->pmeth || !ctx->pmeth->sign) {
      EVPerr(EVP_F_EVP_PKEY_SIGN_INIT,
               EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
      return -2;
    }
    ctx->operation = EVP_PKEY_OP_SIGN;
    if (!ctx->pmeth->sign_init)
      return 1;
    ret = ctx->pmeth->sign_init(ctx);
    if (ret <= 0)
      ctx->operation = EVP_PKEY_OP_UNDEFINED;
    return ret;
}
这里判断了pmeth中是否存在须要的sign方法,之后调用sign_init方法。
EVP_DigestInit_ex(ctx, type, e)

int EVP_DigestInit_ex(EVP_MD_CTX *ctx, const EVP_MD *type, ENGINE *impl)
{
    EVP_MD_CTX_clear_flags(ctx, EVP_MD_CTX_FLAG_CLEANED);
#ifndef OPENSSL_NO_ENGINE
    /*
   * Whether it's nice or not, "Inits" can be used on "Final"'d contexts so
   * this context may already have an ENGINE! Try to avoid releasing the
   * previous handle, re-querying for an ENGINE, and having a
   * reinitialisation, when it may all be unnecessary.
   */
    if (ctx->engine && ctx->digest &&
      (type == NULL || (type->type == ctx->digest->type)))
      goto skip_to_init;
    if (type) {
      /*
         * Ensure an ENGINE left lying around from last time is cleared (the
         * previous check attempted to avoid this if the same ENGINE and
         * EVP_MD could be used).
         */
      ENGINE_finish(ctx->engine);
      if (impl != NULL) {
            if (!ENGINE_init(impl)) {
                EVPerr(EVP_F_EVP_DIGESTINIT_EX, EVP_R_INITIALIZATION_ERROR);
                return 0;
            }
      } else {
            /* Ask if an ENGINE is reserved for this job */
            impl = ENGINE_get_digest_engine(type->type);
      }
      if (impl != NULL) {
            /* There's an ENGINE for this job ... (apparently) */
            const EVP_MD *d = ENGINE_get_digest(impl, type->type);

            if (d == NULL) {
                EVPerr(EVP_F_EVP_DIGESTINIT_EX, EVP_R_INITIALIZATION_ERROR);
                ENGINE_finish(impl);
                return 0;
            }
            /* We'll use the ENGINE's private digest definition */
            type = d;
            /*
             * Store the ENGINE functional reference so we know 'type' came
             * from an ENGINE and we need to release it when done.
             */
            ctx->engine = impl;
      } else
            ctx->engine = NULL;
    } else {
      if (!ctx->digest) {
            EVPerr(EVP_F_EVP_DIGESTINIT_EX, EVP_R_NO_DIGEST_SET);
            return 0;
      }
      type = ctx->digest;
    }
#endif
    if (ctx->digest != type) {
      if (ctx->digest && ctx->digest->ctx_size) {
            OPENSSL_clear_free(ctx->md_data, ctx->digest->ctx_size);
            ctx->md_data = NULL;
      }
      ctx->digest = type;
      if (!(ctx->flags & EVP_MD_CTX_FLAG_NO_INIT) && type->ctx_size) {
            ctx->update = type->update;
            ctx->md_data = OPENSSL_zalloc(type->ctx_size);
            if (ctx->md_data == NULL) {
                EVPerr(EVP_F_EVP_DIGESTINIT_EX, ERR_R_MALLOC_FAILURE);
                return 0;
            }
      }
    }
#ifndef OPENSSL_NO_ENGINE
skip_to_init:
#endif
    if (ctx->pctx) {
      int r;
      r = EVP_PKEY_CTX_ctrl(ctx->pctx, -1, EVP_PKEY_OP_TYPE_SIG,
                              EVP_PKEY_CTRL_DIGESTINIT, 0, ctx);
      if (r <= 0 && (r != -2))
            return 0;
    }
    if (ctx->flags & EVP_MD_CTX_FLAG_NO_INIT)
      return 1;
    return ctx->digest->init(ctx);
}

[*]这里由于没有传入自定义的engine,且系统内也没有对应nid的engine,所以直接跳到了ctx->digest = type;
[*]是将ctx中的摘要方法进行赋值,这里是sha256方法。
[*]之后的ctx->update = type->update;则是将ctx的update方法赋值为sha256的update方法。
[*]最后调用了 ctx->digest->init(ctx);实际上是调用了sha256的init方法,注意,这里是提取摘要消息的开始。
2.EVP_DigestSign(mctx, sig, &siglen, hdata, hdatalen)

int EVP_DigestSign(EVP_MD_CTX *ctx, unsigned char *sigret, size_t *siglen,
                   const unsigned char *tbs, size_t tbslen)
{
    if (ctx->pctx->pmeth->digestsign != NULL)
      return ctx->pctx->pmeth->digestsign(ctx, sigret, siglen, tbs, tbslen);
    if (sigret != NULL && EVP_DigestSignUpdate(ctx, tbs, tbslen) <= 0)
      return 0;
    return EVP_DigestSignFinal(ctx, sigret, siglen);
}
这里由于没有digestsign ,所以直接跳到EVP_DigestSignUpdate
EVP_DigestSignUpdate(ctx, tbs, tbslen)

# define EVP_DigestSignUpdate(a,b,c)   EVP_DigestUpdate(a,b,c)

int EVP_DigestUpdate(EVP_MD_CTX *ctx, const void *data, size_t count)
{
    return ctx->update(ctx, data, count);
}
这里是调用了ctx->update,前面的流程中我们给ctx->update赋值为了sha256的update方法,所以实际上调用了sha256的update方法,进行消息摘要的提取。
EVP_DigestSignFinal(ctx, sigret, siglen)

int EVP_DigestSignFinal(EVP_MD_CTX *ctx, unsigned char *sigret,
                        size_t *siglen)
{
    int sctx = 0, r = 0;
    EVP_PKEY_CTX *pctx = ctx->pctx;
    ...
    if (pctx->pmeth->signctx)
      sctx = 1;
    else
      sctx = 0;
    if (sigret) {
      unsigned char md;
      unsigned int mdlen = 0;
      if (ctx->flags & EVP_MD_CTX_FLAG_FINALISE) {
            if (sctx)
                r = ctx->pctx->pmeth->signctx(ctx->pctx, sigret, siglen, ctx);
            else
                r = EVP_DigestFinal_ex(ctx, md, &mdlen);
      } else {
            ......
      }
      if (sctx || !r)
            return r;
      if (EVP_PKEY_sign(ctx->pctx, sigret, siglen, md, mdlen) <= 0)
            return 0;
    } else {
      if (sctx) {
            if (pctx->pmeth->signctx(pctx, sigret, siglen, ctx) <= 0)
                return 0;
      } else {
            int s = EVP_MD_size(ctx->digest);
            if (s < 0 || EVP_PKEY_sign(pctx, sigret, siglen, NULL, s) <= 0)
                return 0;
      }
    }
    return 1;
}
依次调用了EVP_DigestFinal_ex(ctx, md, &mdlen)和EVP_PKEY_sign(ctx->pctx, sigret, siglen, md, mdlen)
EVP_DigestFinal_ex(ctx, md, &mdlen)

int EVP_DigestFinal_ex(EVP_MD_CTX *ctx, unsigned char *md, unsigned int *size)
{
    int ret;

    OPENSSL_assert(ctx->digest->md_size <= EVP_MAX_MD_SIZE);
    ret = ctx->digest->final(ctx, md);
    if (size != NULL)
      *size = ctx->digest->md_size;
    if (ctx->digest->cleanup) {
      ctx->digest->cleanup(ctx);
      EVP_MD_CTX_set_flags(ctx, EVP_MD_CTX_FLAG_CLEANED);
    }
    OPENSSL_cleanse(ctx->md_data, ctx->digest->ctx_size);
    return ret;
}
这里调用了ctx->digest->final(ctx, md);,由于ctx->digest赋值为了sha256方法,所以这里实际调用了sha256的final方法,至此,依次调用了sha256的init、update和final,这就完成了消息摘要的提取的完整流程。
EVP_PKEY_sign(ctx->pctx, sigret, siglen, md, mdlen)

int EVP_PKEY_sign(EVP_PKEY_CTX *ctx,
                  unsigned char *sig, size_t *siglen,
                  const unsigned char *tbs, size_t tbslen)
{
    if (!ctx || !ctx->pmeth || !ctx->pmeth->sign) {
      EVPerr(EVP_F_EVP_PKEY_SIGN,
               EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
      return -2;
    }
    if (ctx->operation != EVP_PKEY_OP_SIGN) {
      EVPerr(EVP_F_EVP_PKEY_SIGN, EVP_R_OPERATON_NOT_INITIALIZED);
      return -1;
    }
    M_check_autoarg(ctx, sig, siglen, EVP_F_EVP_PKEY_SIGN)
      return ctx->pmeth->sign(ctx, sig, siglen, tbs, tbslen);
}
调用了ctx->pmeth->sign(ctx, sig, siglen, tbs, tbslen)方法。
ctx->pmeth是原生standard_methods中的默认&ec_pkey_meth实现。sign方法即为pkey_ec_sign方法
static int pkey_ec_sign(EVP_PKEY_CTX *ctx, unsigned char *sig, size_t *siglen,
                        const unsigned char *tbs, size_t tbslen)
{
    int ret, type;
    unsigned int sltmp;
    EC_PKEY_CTX *dctx = ctx->data;
    EC_KEY *ec = ctx->pkey->pkey.ec;
    const int sig_sz = ECDSA_size(ec);

    /* ensure cast to size_t is safe */
    if (!ossl_assert(sig_sz > 0))
      return 0;

    if (sig == NULL) {
      *siglen = (size_t)sig_sz;
      return 1;
    }

    if (*siglen < (size_t)sig_sz) {
      ECerr(EC_F_PKEY_EC_SIGN, EC_R_BUFFER_TOO_SMALL);
      return 0;
    }

    type = (dctx->md != NULL) ? EVP_MD_type(dctx->md) : NID_sha1;

    ret = ECDSA_sign(type, tbs, tbslen, sig, &sltmp, ec);

    if (ret <= 0)
      return ret;
    *siglen = (size_t)sltmp;
    return 1;
}
pkey_ec_sign中调用ECDSA_sign(type, tbs, tbslen, sig, &sltmp, ec)来对摘要消息进行署名。
至此,完成了摘要消息的提取以及署名的流程。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: [openSSL]tls_construct_cert_verify署名流程