您的位置:首页 > 教育 > 锐评 > [openSSL]tls_construct_cert_verify签名流程

[openSSL]tls_construct_cert_verify签名流程

2024/10/5 18:31:58 来源:https://blog.csdn.net/TangYuanTuoHai/article/details/141162520  浏览:    关键词:[openSSL]tls_construct_cert_verify签名流程

文章目录

  • 前言
  • 一、流程图
  • 二、tls_construct_cert_verify代码分析
    • 1.EVP_DigestSignInit(mctx, &pctx, md, NULL, pkey)初始化
      • EVP_PKEY_CTX_new
      • EVP_PKEY_sign_init(ctx->pctx)
      • EVP_DigestInit_ex(ctx, type, e)
    • 2.EVP_DigestSign(mctx, sig, &siglen, hdata, hdatalen)
      • EVP_DigestSignUpdate(ctx, tbs, tbslen)
      • EVP_DigestSignFinal(ctx, sigret, siglen)
        • EVP_DigestFinal_ex(ctx, md, &mdlen)
        • EVP_PKEY_sign(ctx->pctx, sigret, siglen, md, mdlen)


前言

tls_construct_cert_verify是openSSL源码中的一个方法,用于进行TLS握手阶段的签名流程。

从概念上讲,数字签名的流程是这样的:

  1. 客户端根据一段公共消息生成消息摘要,一般是使用sha256算法。
  2. 客户端对消息摘要使用私钥进行签名,将签名后的消息发送给服务端。
  3. 服务端收到签名消息后,使用客户端公钥处理来得到摘要消息。对公共消息也使用sha256算法来提取摘要,对比摘要,成功则能够验证客户端的身份

流程图

一、流程图

在这里插入图片描述

二、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[TLS13_TBS_PREAMBLE_SIZE + EVP_MAX_MD_SIZE];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);}
#endifif (!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;
}
  1. tls1_lookup_md(lu, &md)获取生成消息摘要的算法md。
  2. get_cert_verify_tbs_data(s, tls13tbs, &hdata, &hdatalen)生成待处理的数据hdata。
  3. EVP_DigestSignInit(mctx, &pctx, md, NULL, pkey)初始化
  4. 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_RSAstruct rsa_st *rsa;     /* RSA */
# endif
# ifndef OPENSSL_NO_DSAstruct dsa_st *dsa;     /* DSA */
# endif
# ifndef OPENSSL_NO_DHstruct dh_st *dh;       /* DH */
# endif
# ifndef OPENSSL_NO_ECstruct 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_ENGINEif (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
#endifpmeth = EVP_PKEY_meth_find(id);if (pmeth == NULL) {
#ifndef OPENSSL_NO_ENGINEENGINE_finish(e);
#endifEVPerr(EVP_F_INT_CTX_NEW, EVP_R_UNSUPPORTED_ALGORITHM);return NULL;}ret = OPENSSL_zalloc(sizeof(*ret));if (ret == NULL) {
#ifndef OPENSSL_NO_ENGINEENGINE_finish(e);
#endifEVPerr(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;} elsectx->engine = NULL;} else {if (!ctx->digest) {EVPerr(EVP_F_EVP_DIGESTINIT_EX, EVP_R_NO_DIGEST_SET);return 0;}type = ctx->digest;}
#endifif (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_ENGINEskip_to_init:
#endifif (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);
}
  1. 这里由于没有传入自定义的engine,且系统内也没有对应nid的engine,所以直接跳到了ctx->digest = type;
  2. 是将ctx中的摘要方法进行赋值,这里是sha256方法。
  3. 之后的ctx->update = type->update;则是将ctx的update方法赋值为sha256的update方法。
  4. 最后调用了 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;elsesctx = 0;if (sigret) {unsigned char md[EVP_MAX_MD_SIZE];unsigned int mdlen = 0;if (ctx->flags & EVP_MD_CTX_FLAG_FINALISE) {if (sctx)r = ctx->pctx->pmeth->signctx(ctx->pctx, sigret, siglen, ctx);elser = 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)来对摘要消息进行签名。
至此,完成了摘要消息的提取以及签名的流程。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com