花式获取ssl证书有效期

2023-11-19

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

在网络通信过程中,为了数据在传输过程中保持私密,就要用到了数据加密认证的过程,加密证书就诞生了,今天主要分析有关pem类型加密证书的解析,读取证书里的有效期,。

一、.pem是什么?

加密证书有两种格式,pem和key 这两种格式分别存储的是证书base64加密和私钥base64加密还有格式分割符,也就是说pem存的是证书,key存的是私钥。

二、读取pem证书有效期

1. 命令读取

代码如下(示例):

openssl x509 -in cert.pem -noout -dates

输出打印:

notBefore=Jan 4 04:18:30 2021 GMT
notAfter=Dec 30 04:18:30 2036 GMT

2. C/C++读取

代码如下(示例):

#include <openssl/pem.h>
#include <openssl/asn1.h>


static int ossl_ascii_isdigit(const char inchar) {
    if (inchar > 0x2F && inchar < 0x3A)
        return 1;
    return 0;
}

static int leap_year(const int year)
{
    if (year % 400 == 0 || (year % 100 != 0 && year % 4 == 0))
        return 1;
    return 0;
}

static void determine_days(struct tm *tm)
{
    static const int ydays[12] = {
        0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
    };
    int y = tm->tm_year + 1900;
    int m = tm->tm_mon;
    int d = tm->tm_mday;
    int c;

    tm->tm_yday = ydays[m] + d - 1;
    if (m >= 2) {
        /* March and onwards can be one day further into the year */
        tm->tm_yday += leap_year(y);
        m += 2;
    } else {
        /* Treat January and February as part of the previous year */
        m += 14;
        y--;
    }
    c = y / 100;
    y %= 100;
    /* Zeller's congruence */
    tm->tm_wday = (d + (13 * m) / 5 + y + y / 4 + c / 4 + 5 * c + 6) % 7;
}

int ASN1_time_to_tm(struct tm *tm, const ASN1_TIME *d)
{
	static const int min[9] = { 0, 0, 1, 1, 0, 0, 0, 0, 0 };
    static const int max[9] = { 99, 99, 12, 31, 23, 59, 59, 12, 59 };
    static const int mdays[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
    char *a;
    int n, i, i2, l, o, min_l = 11, strict = 0, end = 6, btz = 5, md;
    struct tm tmp;
#if defined(CHARSET_EBCDIC)
    const char upper_z = 0x5A, num_zero = 0x30, period = 0x2E, minus = 0x2D, plus = 0x2B;
#else
    const char upper_z = 'Z', num_zero = '0', period = '.', minus = '-', plus = '+';
#endif
    /*
     * ASN1_STRING_FLAG_X509_TIME is used to enforce RFC 5280
     * time string format, in which:
     *
     * 1. "seconds" is a 'MUST'
     * 2. "Zulu" timezone is a 'MUST'
     * 3. "+|-" is not allowed to indicate a time zone
     */
    if (d->type == V_ASN1_UTCTIME) {
        if (d->flags & ASN1_STRING_FLAG_X509_TIME) {
            min_l = 13;
            strict = 1;
        }
    } else if (d->type == V_ASN1_GENERALIZEDTIME) {
        end = 7;
        btz = 6;
        if (d->flags & ASN1_STRING_FLAG_X509_TIME) {
            min_l = 15;
            strict = 1;
        } else {
            min_l = 13;
        }
    } else {
        return 0;
    }

    l = d->length;
    a = (char *)d->data;
    o = 0;
    memset(&tmp, 0, sizeof(tmp));

    /*
     * GENERALIZEDTIME is similar to UTCTIME except the year is represented
     * as YYYY. This stuff treats everything as a two digit field so make
     * first two fields 00 to 99
     */

    if (l < min_l)
        goto err;
    for (i = 0; i < end; i++) {
        if (!strict && (i == btz) && ((a[o] == upper_z) || (a[o] == plus) || (a[o] == minus))) {
            i++;
            break;
        }
        if (!ossl_ascii_isdigit(a[o]))
            goto err;
        n = a[o] - num_zero;
        /* incomplete 2-digital number */
        if (++o == l)
            goto err;

        if (!ossl_ascii_isdigit(a[o]))
            goto err;
        n = (n * 10) + a[o] - num_zero;
        /* no more bytes to read, but we haven't seen time-zone yet */
        if (++o == l)
            goto err;

        i2 = (d->type == V_ASN1_UTCTIME) ? i + 1 : i;

        if ((n < min[i2]) || (n > max[i2]))
            goto err;
        switch (i2) {
        case 0:
            /* UTC will never be here */
            tmp.tm_year = n * 100 - 1900;
            break;
        case 1:
            if (d->type == V_ASN1_UTCTIME)
                tmp.tm_year = n < 50 ? n + 100 : n;
            else
                tmp.tm_year += n;
            break;
        case 2:
            tmp.tm_mon = n - 1;
            break;
        case 3:
            /* check if tm_mday is valid in tm_mon */
            if (tmp.tm_mon == 1) {
                /* it's February */
                md = mdays[1] + leap_year(tmp.tm_year + 1900);
            } else {
                md = mdays[tmp.tm_mon];
            }
            if (n > md)
                goto err;
            tmp.tm_mday = n;
            determine_days(&tmp);
            break;
        case 4:
            tmp.tm_hour = n;
            break;
        case 5:
            tmp.tm_min = n;
            break;
        case 6:
            tmp.tm_sec = n;
            break;
        }
    }

    /*
     * Optional fractional seconds: decimal point followed by one or more
     * digits.
     */
    if (d->type == V_ASN1_GENERALIZEDTIME && a[o] == period) {
        if (strict)
            /* RFC 5280 forbids fractional seconds */
            goto err;
        if (++o == l)
            goto err;
        i = o;
        while ((o < l) && ossl_ascii_isdigit(a[o]))
            o++;
        /* Must have at least one digit after decimal point */
        if (i == o)
            goto err;
        /* no more bytes to read, but we haven't seen time-zone yet */
        if (o == l)
            goto err;
    }

    /*
     * 'o' will never point to '\0' at this point, the only chance
     * 'o' can point to '\0' is either the subsequent if or the first
     * else if is true.
     */
    if (a[o] == upper_z) {
        o++;
    } else if (!strict && ((a[o] == plus) || (a[o] == minus))) {
        int offsign = a[o] == minus ? 1 : -1;
        int offset = 0;

        o++;
        /*
         * if not equal, no need to do subsequent checks
         * since the following for-loop will add 'o' by 4
         * and the final return statement will check if 'l'
         * and 'o' are equal.
         */
        if (o + 4 != l)
            goto err;
        for (i = end; i < end + 2; i++) {
            if (!ossl_ascii_isdigit(a[o]))
                goto err;
            n = a[o] - num_zero;
            o++;
            if (!ossl_ascii_isdigit(a[o]))
                goto err;
            n = (n * 10) + a[o] - num_zero;
            i2 = (d->type == V_ASN1_UTCTIME) ? i + 1 : i;
            if ((n < min[i2]) || (n > max[i2]))
                goto err;
            /* if tm is NULL, no need to adjust */
            if (tm != NULL) {
                if (i == end)
                    offset = n * 3600;
                else if (i == end + 1)
                    offset += n * 60;
            }
            o++;
        }
        if (offset && !OPENSSL_gmtime_adj(&tmp, 0, offset * offsign))
            goto err;
    } else {
        /* not Z, or not +/- in non-strict mode */
        goto err;
    }
    if (o == l) {
        /* success, check if tm should be filled */
        if (tm != NULL)
            *tm = tmp;

        return 1;
    }
 err:
    return 0;
}

static int get_pem_effective_time(const char *filename, cert_times_t *pcert_times)
{
	if(pcert_times == NULL)
	{
		printf("pcert_times is null\n");
		return -1;
	}

	if(access(filename, F_OK) != 0)
	{
		printf("filename:%s not exist!\n", filename);
		return -1;
	}

	FILE *fp = NULL;
	fp = fopen(filename,"r");  
    if(fp == NULL)
	{  
        printf("open file:%s failed\n", filename);
        return -1;
    }

	X509 *cert = PEM_read_X509(fp, NULL, NULL, NULL);
	if(cert)
	{	
		struct tm stm;
		//get before time
		const ASN1_TIME *tm_before = X509_get0_notBefore(cert);
		ASN1_time_to_tm(&stm, tm_before);
		pcert_times->sec_before = mktime(&stm);

		//get after time
		const ASN1_TIME *tm_after = X509_get0_notAfter(cert);
		ASN1_time_to_tm(&stm, tm_after);
		pcert_times->sec_after = mktime(&stm);

		X509_free(cert);
	}
	else
	{
		printf("Certificate information not read!\n");
		return -1;
	}

	return 0;
}

3. 具体分析

使用openssl自带接口获取:

(1)  X509 *PEM_read_X509(FILE *out, X509 **x, pem_password_cb *cb, void *u)

该函数是根据传入证书文件的句柄读取证书内容,并返回解析后的X509结构体。

(2)  const ASN1_TIME *X509_get0_notBefore(const X509 *x)
(3)  const ASN1_TIME *X509_get0_notAfter(const X509 *x)

该函数是获取证书有效的起始时间和结束时间。

(4)  int ASN1_time_to_tm(struct tm *tm, const ASN1_TIME *d)

该函数是转换获取到的有效时间格式,这个接口openssl也有自带,但实际测试转换的时间有问题。

总结

可以结合《使用 OpenSSL 和 C 解析 X.509 证书》这篇文章了解更多内容。
以上两种方式都能够获取pem文件的有效期,如果内容帮助到了您,手留余香,点个赞

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

花式获取ssl证书有效期 的相关文章

随机推荐

  • 环境变量是如何生效的——以Linux操作系统为例

    什么是环境变量 从我们学习Java开始 就经常接触一个东西 PATH 也叫环境变量 环境变量是操作系统提供给应用程序访问的简单 key value字符串 windows linux mac都有同样的概念 环境变量的作用 当我们拥有一个可执行
  • Git版本回退并强制推送到远端

    Git版本回退并强制推送到远端 本文参考廖雪峰的Git教程 前言 本文章解决问题的前提是本人不小心修改了本地代码仓库的最外层目录权限 不知道原权限是什么 导致本地git提示几十个文件被修改过 实际内容并未修改 可能是目录权限改变被git识别
  • C++ - 继承 一些 细节 - 组合 和 继承的区别

    前言 本篇博客基于 C 继承 chihiro1122的博客 CSDN博客 之上列出一些例子 如果有需要请看以上博客 继承的例子 例1 上述例子应该选择 C 首先不用说 p3肯定是指向 d 对象的开头的 p1 也是指向 d 对象的开头的 不同
  • 机器学习即服务:关于情感分析的10个应用场景和4个服务

    情感分析是什么 用户生成内容的爆炸式增长和档案材料的数字化创造了大量的数据集 其中包含了许多人对几乎每一个主题发表的观点 在某些情况下 该数据的生成是通过用户界面构造的 例如 在电子商务网站上处理客户评论相对容易 因为用户需要在产品评论的文
  • C++模板基础(六)

    类模板与成员函数模板 使用 template 关键字引入模板 template class B 类模板的声明与定义 翻译单元的一处定义原则 template
  • html5页面刷新时显示新数据,用ajax使网页不刷新就可以显示新数据

    用AjaxPro的 1 在引用中添加引用AjaxPro dll 我用的是这个 支持asp net 1 1 和asp net 2 0 2 web config中建立HttpHandler 3 新建一个类Demo 这个类里面提供了查询数据库和输
  • 怎么在html中复制粘贴图片,如何复制其他网页上的文章和图片

    首先 我们并不建议直接复制别人的内容 以免侵权 但是不排除在某些情况下 需要将其他网页上的内容完整的复制到你的网站中 比如 将自己的微博文章复制到自己的网站 大部分人都知道这个简单的方法 先选中目标文字按CTRL C快捷键 然后再网站后台文
  • AngularJS学习之全局API(应用程序编程接口)

    1 AngularJS全局API用于执行常见任务的Javascript函数集合 比较对象 迭代对象 转换对象 2 全局API函数使用angularJS对象进行访问 以下是通用API函数 angular lowercase 转换字符串为小写
  • Unity用Vuforia做AR实现脱卡效果

    有时在识别目标丢失后我们仍希望虚拟物体能够出现在摄像机前 或者到一个特定的位置 我们能对其进行操作 这就是脱卡功能 自带的脱卡功能应该是ExtendedTracking 允许模型在识别图丢失的时候还存在 位置不变 在丢失的时候的位置 这样也
  • 用Python实现火车票查询(含票价版)

    用Python实现火车票查询 含票价版 写在前面 网上关于用Python3编写火车查询脚本的版本众多 我在前人的基础上编写了自己的这个版本 我觉得的写的这个版本有以下几个特色 1 智能引导输入 我一直比较喜欢这种方式 如果直接做成GUI的图
  • 基于分位数回归的门控循环单元QRGRU时间序列区间预测。(主要应用于风速,负荷,功率)包含评价指标R2,MAE,MBE,区间覆盖率,区间平均宽度。

    清空环境变量 warning off 关闭报警信息 close all 关闭开启的图窗 clear 清空变量 clc 清空命令行 导入数据 时间序列的单列数据 result xlsread 数据集 xlsx 数据分析 num samples
  • Sublime Text配置anaconda环境

    Ref https blog csdn net pptde article details 110791950 spm 1001 2101 3001 6661 1 utm medium distribute pc relevant t0 n
  • docker创建CentOS云主机(docker实践)

    基于Ubuntu操作系统 从零开始构建一套docker虚拟化平台 docker的产物为 容器 docker构建容器 Nginx WEB docker启动虚拟机 创建CentOS云主机 同样是容器 对之前内容的总结熟悉 要求 CentOS 7
  • Android实现裁剪

    Android自定义View实现图片缩放旋转移动裁剪 灰信网 软件开发博客聚合 freesion com SuppressLint AppCompatCustomView public class CorpToView extends Im
  • java前后端传递日期类型不一致的转换问题

    今天在做学生信息的展示时发现展示的日期和数据库中日期不同 本来最开始是用SimpleDateFormat进行转换的 但是转换之后的是字符串类型的 与date类型对不上 所以就上网查了一下 发现可以用 DateTimeFormat和 Json
  • 牛顿法

    牛顿法被称为牛顿 拉夫逊 Newton Raphson 方法 牛顿在17世纪提出用来求解方程的根 假设点x 位函数f x 的根 则f x 0 将函数f x 在点处进行一阶泰勒展开有 假设点为函数f x 的根 则有 那么可以得到 牛顿法通过迭
  • STM8的ADC的五种工作模式

    STM8的ADC的五种工作模式 STM8的ADC是10位的逐次比较型模拟数字转换器 多达16个多功能的输入通道 拥有5种转换模式 转换结束可产生中断 STM8 ADC的初始化顺序如下 1 AD输入通道对应的IO设置为上拉输入 2 配置AD参
  • JVM学习笔记(二)------Java代码编译和执行的整个过程

    http blog csdn net cutesource article details 5904542 Java代码编译是由Java源码编译器来完成 流程图如下所示 Java字节码的执行是由JVM执行引擎来完成 流程图如下所示 Java
  • JAVA实现二叉树的前、中、后序遍历(递归与非递归)

    最近在面试中遇到过问到二叉树后序遍历非递归实现的方法 之前以为会递归的解决就OK 看来还是太心存侥幸 在下一次面试之前 特地整理一下这个问题 首先二叉树的结构定义 java代码如下 public class Node private int
  • 花式获取ssl证书有效期

    提示 文章写完后 目录可以自动生成 如何生成可参考右边的帮助文档 文章目录 前言 一 pem是什么 二 读取pem证书有效期 1 命令读取 2 C C 读取 3 具体分析 总结 前言 在网络通信过程中 为了数据在传输过程中保持私密 就要用到