一、服务端和客户端校验自己证书

服务端和客户端在tls_construct_server_certificate/tls_construct_client_certificate阶段都会进行对自己的证书进行校验,在tls_construct_server_certificate/tls_construct_client_certificate都调用了函数ssl3_output_cert_chain

unsigned long ssl3_output_cert_chain(SSL *s, CERT_PKEY *cpk) {   ......   if (!ssl_add_cert_chain(s, cpk, &l))    return 0;   ......}

然后进入ssl_add_cert_chain函数,部分函数如下

/* Add certificate chain to internal SSL BUF_MEM structure */
int ssl_add_cert_chain(SSL *s, CERT_PKEY *cpk, unsigned long *l)
{......if (chain_store) {X509_STORE_CTX *xs_ctx = X509_STORE_CTX_new();if (xs_ctx == NULL) {SSLerr(SSL_F_SSL_ADD_CERT_CHAIN, ERR_R_MALLOC_FAILURE);return (0);}if (!X509_STORE_CTX_init(xs_ctx, chain_store, x, NULL)) {X509_STORE_CTX_free(xs_ctx);SSLerr(SSL_F_SSL_ADD_CERT_CHAIN, ERR_R_X509_LIB);return (0);}(void)X509_verify_cert(xs_ctx);/* Don't leave errors in the queue */ERR_clear_error();......}return 1;
}

其中X509_STORE_CTX这个结构主要是用来校验证书用,一般都会使用X509_STORE这个已经设置好的对象来初始化X509_STORE_CTX,初始化函数如下:

int X509_STORE_CTX_init(X509_STORE_CTX *ctx, X509_STORE *store,X509 *x509,  STACK_OF(X509) *chain){......if (store && store->verify)ctx->verify = store->verify;elsectx->verify = internal_verify;......
}

这个初始化函数参数说明一下,第二个参数store是用来初始化第一个参数ctx,第三个参数x509表示待验证的证书,chain是不信任证书链。

具体的校验函数也是在该init函数里面设定的,如果自己不需要(一般也不需要),那么使用gmssl自带实现的internal_verify函数来进行证书链的校验。

上面忽略了验证证书的函数X509_verify_cert的返回值处理,其解释如下

链不完整是有效的(因为通常我们不在链中包括根证书)。因此,我们故意忽略此调用返回的错误。我们实际上并不是在验证证书——我们只是在尽可能多地构建链

进入X509_verify_cert函数对证书链的内容进行校验,

int X509_verify_cert(X509_STORE_CTX *ctx)
{......if (DANETLS_ENABLED(dane))ret = dane_verify(ctx);elseret = verify_chain(ctx);......return ret;
}

首先需要构造一个待校验的证书链,该证书链的末端为待校验的证书,证书链的顶端为自签名ca证书,并且顶端ca证书必须是本地store中保存已有的。构造好了这么一个证书链之后,再一级一级的从顶端证书校验到末尾,即完成了整个证书链的校验。

接着进入函数verify_chain(ctx)

static int verify_chain(X509_STORE_CTX *ctx)
{int err;int ok;/** Before either returning with an error, or continuing with CRL checks,* instantiate chain public key parameters.*/if ((ok = build_chain(ctx)) == 0 ||(ok = check_chain_extensions(ctx)) == 0 ||(ok = check_auth_level(ctx)) == 0 ||(ok = check_name_constraints(ctx)) == 0 ||(ok = check_id(ctx)) == 0 || 1)X509_get_pubkey_parameters(NULL, ctx->chain);if (ok == 0 || (ok = ctx->check_revocation(ctx)) == 0)return ok;....../* Verify chain signatures and expiration times */ok = (ctx->verify != NULL) ? ctx->verify(ctx) : internal_verify(ctx);if (!ok)return ok;......
}

在verify_chain函数中ctx->verify(ctx)在上面函数ssl_add_cert_chain中通过X509_STORE_CTX_init(xs_ctx, chain_store, x, NULL)进行初始化。另外这个init函数中还设置了很多回调函数,基本上都是把X509_STORE的函数抄一份给X509_STORE_CTX。

    if (store && store->verify)ctx->verify = store->verify;elsectx->verify = internal_verify;

下面开始具体讲一讲internal_verify这个函数。进入internal_verify函数中验证链签名和过期时间,internal_verify函数如下

static int internal_verify(X509_STORE_CTX *ctx)
{......while (n >= 0) {EVP_PKEY *pkey;/** Skip signature check for self signed certificates unless explicitly* asked for.  It doesn't add any security and just wastes time.  If* the issuer's public key is unusable, report the issuer certificate* and its depth (rather than the depth of the subject).*/if (xs != xi || (ctx->param->flags & X509_V_FLAG_CHECK_SS_SIGNATURE)) {if ((pkey = X509_get0_pubkey(xi)) == NULL) {if (!verify_cb_cert(ctx, xi, xi != xs ? n+1 : n,X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY))return 0;} else if (X509_verify(xs, pkey) <= 0) {if (!verify_cb_cert(ctx, xs, n,X509_V_ERR_CERT_SIGNATURE_FAILURE))return 0;}}check_cert:/* Calls verify callback as needed */if (!x509_check_cert_time(ctx, xs, n))return 0;/** Signal success at this depth.  However, the previous error (if any)* is retained.*/ctx->current_issuer = xi;ctx->current_cert = xs;ctx->error_depth = n;if (!ctx->verify_cb(1, ctx))return 0;if (--n >= 0) {xi = xs;xs = sk_X509_value(ctx->chain, n);}}return 1;
}

判断证书链里面的证书个数,对证书链中的证书,验证深度为证书个数减1,首先通过函数x509_check_cert_time验证证书链中每个证书的时间有效期是否过期,接着对证书进行验签,通过CA证书中的公钥进行验签。

除非明确要求,否则跳过自签名证书的签名检查。它没有增加任何安全性,只是浪费时间。如果颁发者的公钥不可用,请报告颁发者证书及其深度(而不是主题的深度)。

verify的参数设定了证书验证的深度,超过此深度,即视为证书验证失败。所谓证书验证深度,就是证书链(这里指服务器证书的证书链)的长度,即从根CA到服务器证书的层数,如果签发服务器证书的CA本身就是根CA,那么证书链长度就为1

对证书是否过期进行判断,通过函数x509_check_cert_time

int x509_check_cert_time(X509_STORE_CTX *ctx, X509 *x, int depth)
{time_t *ptime;int i;//ctx->param->flags=0x8000if (ctx->param->flags & X509_V_FLAG_USE_CHECK_TIME) ptime = &ctx->param->check_time;else if (ctx->param->flags & X509_V_FLAG_NO_CHECK_TIME)return 1;elseptime = NULL;i = X509_cmp_time(X509_get0_notBefore(x), ptime);if (i >= 0 && depth < 0)return 0;if (i == 0 && !verify_cb_cert(ctx, x, depth,      X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD))return 0;if (i > 0 && !verify_cb_cert(ctx, x, depth, X509_V_ERR_CERT_NOT_YET_VALID))return 0;i = X509_cmp_time(X509_get0_notAfter(x), ptime);if (i <= 0 && depth < 0)return 0;if (i == 0 && !verify_cb_cert(ctx, x, depth,         509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD))       return 0;if (i < 0 && !verify_cb_cert(ctx, x, depth, x509_V_ERR_CERT_HAS_EXPIRED))return 0;return 1;
}

该函数通过X509_cmp_time的返回值和verify_cb_cert函数来判断是否通过

其中X509_cmp_time函数如下:

int X509_cmp_time(const ASN1_TIME *ctm, time_t *cmp_time)
{char *str;ASN1_TIME atm;long offset;char buff1[24], buff2[24], *p;int i, j, remaining;p = buff1;remaining = ctm->length;str = (char *)ctm->data;/** Note that the following (historical) code allows much more slack in the* time format than RFC5280. In RFC5280, the representation is fixed:* UTCTime: YYMMDDHHMMSSZ* GeneralizedTime: YYYYMMDDHHMMSSZ*/if (ctm->type == V_ASN1_UTCTIME) {/* YYMMDDHHMM[SS]Z or YYMMDDHHMM[SS](+-)hhmm */int min_length = sizeof("YYMMDDHHMMZ") - 1;int max_length = sizeof("YYMMDDHHMMSS+hhmm") - 1;if (remaining < min_length || remaining > max_length)return 0;memcpy(p, str, 10);p += 10;str += 10;remaining -= 10;} else {/* YYYYMMDDHHMM[SS[.fff]]Z or YYYYMMDDHHMM[SS[.f[f[f]]]](+-)hhmm */int min_length = sizeof("YYYYMMDDHHMMZ") - 1;int max_length = sizeof("YYYYMMDDHHMMSS.fff+hhmm") - 1;if (remaining < min_length || remaining > max_length)return 0;memcpy(p, str, 12);p += 12;str += 12;remaining -= 12;}if ((*str == 'Z') || (*str == '-') || (*str == '+')) {*(p++) = '0';*(p++) = '0';} else {/* SS (seconds) */if (remaining < 2)return 0;*(p++) = *(str++);*(p++) = *(str++);remaining -= 2;/** Skip any (up to three) fractional seconds...* TODO(emilia): in RFC5280, fractional seconds are forbidden.* Can we just kill them altogether?*/if (remaining && *str == '.') {str++;remaining--;for (i = 0; i < 3 && remaining; i++, str++, remaining--) {if (*str < '0' || *str > '9')break;}}}*(p++) = 'Z';*(p++) = '\0';/* We now need either a terminating 'Z' or an offset. */if (!remaining)return 0;if (*str == 'Z') {if (remaining != 1)return 0;offset = 0;} else {/* (+-)HHMM */if ((*str != '+') && (*str != '-'))return 0;/* Historical behaviour: the (+-)hhmm offset is forbidden in RFC5280. */if (remaining != 5)return 0;if (str[1] < '0' || str[1] > '9' || str[2] < '0' || str[2] > '9' ||str[3] < '0' || str[3] > '9' || str[4] < '0' || str[4] > '9')return 0;offset = ((str[1] - '0') * 10 + (str[2] - '0')) * 60;offset += (str[3] - '0') * 10 + (str[4] - '0');if (*str == '-')offset = -offset;}atm.type = ctm->type;atm.flags = 0;atm.length = sizeof(buff2);atm.data = (unsigned char *)buff2;       //buff2中储存当前时间//获取当前设备时间if (X509_time_adj(&atm, offset * 60, cmp_time) == NULL)return 0;if (ctm->type == V_ASN1_UTCTIME) {i = (buff1[0] - '0') * 10 + (buff1[1] - '0');if (i < 50)i += 100;           /* cf. RFC 2459 */j = (buff2[0] - '0') * 10 + (buff2[1] - '0');if (j < 50)j += 100;if (i < j)return -1;if (i > j)return 1;}i = strcmp(buff1, buff2);if (i == 0)      /* wait a second then return younger :-) */return -1;elsereturn i;
}

判断证书时间有效期是否过期逻辑如下:

  • 判断现有时间长度是否满足条件,使用UTCtime格式为YYMMDDHHMMSSZ

  • 取出notbefore的时间中的年份,上面buff1和buff2中取出的为ASICC格式,算出的i判断与50的大小

当YY大于等于50,年将被认为是19YY;

当YY不到50,年将被认为是20YY。

参考:<RFC2459 Internet X.509 公钥基础设施>

  • 得到时间后判断notbefore时间是否满足要求,当满足要求,i<j,return -1

  • 同理notafter时间是否满足要求,当满足要求,i>j,return 1

  • 若当前设备时间与notbefore或notafter年份一致,则进行直接比较buff1和buff2的内容,buff1为notbefore/notafter的时间,buff2为当前设备时间,输出格式如下

证书时间为:

notBefore=May 29 08:48:26 2019 GMT

notAfter=Jul  7 08:48:26 2023 GMT

buff1=190529084826Z

buff2=220705004647Z

buff1=230707084826Z

buff2=220705004647Z

在x509_check_cert_time函数中,当时间不满足要求的时候,返回值需要根据verify_cb_cert来确定。

其中verify_cb_cert函数返回值为

return ctx->verify_cb(0, ctx);

上述的返回值在函数ssl_verify_cert_chain->X509_STORE_CTX_set_verify_cb进行初始化,

X509_STORE_CTX_set_verify_cb(ctx, s->verify_callback);

而s->verify_callback所指向的函数为自己写的callabck函数

可以提供参考如下:

static int verify_callback(int ok, X509_STORE_CTX *ctx){    int cert_error = X509_STORE_CTX_get_error(ctx); if (!ok)    {        switch (cert_error)        {       case X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER:               //33   case X509_V_ERR_KEYUSAGE_NO_CRL_SIGN:                   //35    case X509_V_ERR_DIFFERENT_CRL_SCOPE:                    //44     case X509_V_ERR_CRL_PATH_VALIDATION_ERROR:              //54      case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD:         //15      case X509_V_ERR_CRL_NOT_YET_VALID:                      //11      case X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD:         //16      case X509_V_ERR_CRL_HAS_EXPIRED:                        //12   case X509_V_ERR_CRL_SIGNATURE_FAILURE:                  //8   case X509_V_ERR_UNABLE_TO_GET_CRL:                      //3    ok = 1;               /*要求:证书撤销列表本地的错误,不应该导致客户端连接不成功*/              /*检查证书的时候,若失败,线程投递错误,导致ssl_read失败*/       /*在回调中修改了函数返回值,让crl的错误返回成功,但是错误还投递了,这里让他清空一下。*/               ERR_clear_error();               break;      }   }  return (ok);
}

根据上面代码可知,即使在函数x509_check_cert_time中判断时间的返回值i满足条件,但是上面verify_callbacka函数中ok=0,cert_error为X509_V_ERR_CERT_HAS_EXPIRED,为10,解释为证书过期,但是不在上面的case中,所以返回ok=0,导致证书有效性验证失败

二、服务端和客户端校验对端证书

服务端和客户端在tls_process_server_certificate/tls_process_client_certificate阶段都会进行对对端的证书进行校验,在tls_proces_server_certificate/tls_proces_client_certificate都调用了函数ssl_verify_cert_chain,在ssl_verify_cert_chain中调用了X509_verify_cert,后面的验证内容与上述一致,但是服务端和客户端的返回值处理不一致

客户端

MSG_PROCESS_RETURN tls_process_server_certificate(SSL *s, PACKET *pkt){......i = ssl_verify_cert_chain(s, sk);if (s->verify_mode != SSL_VERIFY_NONE && i <= 0) {al = ssl_verify_alarm_type(s->verify_result);SSLerr(SSL_F_TLS_PROCESS_SERVER_CERTIFICATE,SSL_R_CERTIFICATE_VERIFY_FAILED);goto f_err;}......
}

根据上面代码,在tls_process_server_certificate中,当验证证书有效期失败后,并且当s->verify_mode不为SSL_VERIFY_NONE(默认为0)的时候才会验证失败,从而连接失败,然而在自己的代码中中设置了s->verify_mode,如

SSL_CTX_set_verify(ctx_62351.g_sslCtx, SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT, verify_callback);

则证书过期才会导致;连接失败,若不设置,则客户端不进行断连操作

服务端

MSG_PROCESS_RETURN tls_process_client_certificate(SSL *s, PACKET *pkt)
{
......i = ssl_verify_cert_chain(s, sk);if (i <= 0) {al = ssl_verify_alarm_type(s->verify_result);SSLerr(SSL_F_TLS_PROCESS_CLIENT_CERTIFICATE,SSL_R_CERTIFICATE_VERIFY_FAILED);ret = SSL_R_CERTIFICATE_VERIFY_FAILED;goto f_err;}if (i > 1) {SSLerr(SSL_F_TLS_PROCESS_CLIENT_CERTIFICATE, i);al = SSL_AD_HANDSHAKE_FAILURE;goto f_err;}
......
}

若对端证书验证错误,i != 1,将会直接报错,TLS连接断开

x509证书有效期校验过程_基于TLS1.2(GmSSL)相关推荐

  1. java开发成语的过程_基于JAVA的成语词典接口调用代码实例

    基于JAVA的成语词典接口调用代码实例 接口描述:基于JA V A的成语词典接口调用代码实例 接口平台:聚合数据 import java.io.BufferedReader; import java. ...

  2. 基于采购订单的MIRO校验过程

    一.介绍 发票校验是物料管理(MM)系统的一部分.它提供物料管理部分和财务会计, 成本控制和资产管理部分的连接. 物料管理模块的发票校验为以下目的服务: 它完成物料采购的全过程 - 物料采购从采购申请 ...

  3. MM--发票校验 及基于采购订单的MIRO发票校验过程

    一.介绍 发票校验是物料管理(MM)系统的一部分.它提供物料管理部分和财务会计, 成本控制和资产管理部分的连接. 物料管理模块的发票校验为以下目的服务: 它完成物料采购的全过程 - 物料采购从采购申请 ...

  4. 证书到期什么意思_建筑资质资质证书有效期及注意事项

    建筑业资质证书是有年限的,很多刚成立建筑公司的建筑企业还不是很了解,以为办理了资质证书就可以不用管了,导致都到期了,还没有进行延期,这种情况是很严重的,有可能导致资质证书被吊销,影响建筑公司发展.下面 ...

  5. openssl命令查看证书有效期_使用 openssl 生成证书

    一.openssl 简介 openssl 是目前最流行的 SSL 密码库工具,其提供了一个通用.健壮.功能完备的工具套件,用以支持SSL/TLS 协议的实现. 官网:https://www.opens ...

  6. C++课程设计实训_基于多态书籍信息管理系统的设计与实现、附源码、有过程截图

    C++课程设计实训_基于多态书籍信息管理系统的设计与实现.附源码,有过程截图 书籍信息管理系统的设计与实现(基于多态) 学生姓名: 学 号: 指导老师: 所 在 系: 专 业: 班 级: C++课程设 ...

  7. 验证证书有效期_笔试查分前,教资考试4个有效期你到底弄清楚了没?逾期不补!...

    小四最近一直提醒大家:一定要提前准备面试! 已经有很多童鞋开始看教案.练习试讲了,你呢? 今天不跟大家唠面试,笔试成绩查询前的这几个"有效期",非常非常重要! 别忘了重视起来! 单 ...

  8. 怎样获得python证书_如何从python中的x509证书中提取公钥?

    已解决 密码学Python 如何从python中的x509证书中提取公钥?10 下面显示了我遵循的代码示例,但是我得到了错误响应 - "无法加载证书".from cryptogra ...

  9. [macOS]_[Shell]_[获取App的签名证书有效期]

    场景 在进行 macOS开发时, 一般都需要对 app 进行签名,而我们发布产品后,测试如何查看app是否已经签名,证书是否正确,证书的有效期到哪天?我们右键app的Get Info并没有签名和证书信 ...

  10. php获取x509证书信息,创建X509证书,并获取证书密钥的一点研究

    作者:肖波 背景 服务器SSL数字证书和客户端单位数字证书的格式遵循X.509标准.X.509是由国际电信联盟(ITU-T)制定的数字证书标准.为了提供公用网络用户目录信息服务,ITU于1988年制定 ...

最新文章

  1. 为什么写博客?如何在博客中更好的分享?
  2. python快速编程入门飞机大战_少儿编程:使用python完成飞机大战游戏(一)
  3. python 导入numpy 导致多进程绑定同一个CPU问题解决方法
  4. 【记录保存】批量删除进程
  5. php判断是否为手机设备
  6. VMware运行Ubuntu 三种网络连接方式:bridge、NAT、Host-Only的区别
  7. gephi导入边数据时,和节点数据不在一个工作区
  8. 激活层是每一层都有吗_每一个日出日落,都是岁月痕迹。这些在东软的第一次,你还记得吗...
  9. vue-cli3 按需加载loading,服务的方式调用
  10. java中servlet的请求范围_java – 如何设置servlet中并发请求数的限制?
  11. NB-IoT独立式烟感在出租屋的防火安全应用
  12. 一条龙教程:Matlab下使用yalmip(工具箱)+cplex(求解器)
  13. 实现支付代付批量转账接口
  14. android字体右对齐,Android TextView将文本右对齐和左对齐
  15. 湖南省第1届职业技能大赛(经历、总结)
  16. linux mame 游戏下载,mame街机模拟器游戏 for linux
  17. 深入浅出matplotlib(9):知道两点坐标画直线
  18. 802.11ac深度技术分析
  19. LightOJ1197 Help Hanzo 区间筛
  20. 【5年Android从零复盘系列之十七】Android自定义View(12):手势绘制及GestureOverlayView事件详解(图文)

热门文章

  1. 老师必备的数学几何绘图软件,赶紧收藏!
  2. layui框架引入TP教程
  3. python是一门胶水语言_Python是唯一被称为“胶水语言”的编程语言?事实并非如此!...
  4. 手把手教你用Python操纵Word自动编写离职报告!
  5. 75道经典逻辑思维题及答案
  6. matlab 求矩阵各行的平均值
  7. C 语言编码规范(MISRA-C-:2004)
  8. 〖EXP〗NSA MS17010永恒之蓝漏洞一键工具
  9. 系统篇: fstab 文件详解
  10. 横向合计代码 锐浪报表_巧用锐浪报表:用报表脚本实现动态显示小数位数