• iOS的cer、p12格式证书解析监控


    之前博客写过直接解析ipa包获取mobileprovision文件来监控APP是否过期来,但APP的推送证书还没有做,

    大家都知道,iOS的推送证书不会放到ipa包里,只能通过直接解析p12或cer、crt格式。

    解析p12格式的话,需要证书导出的密码,p12证书好处是可以授权到多台电脑。解析cer则不需要密码。

    1.可以写个函数同时解析p12和cer文件:

    # -*- coding = utf-8 -*-
    # ------------------------------
    # @time: 2021/3/29 5:18 PM
    # @Author: drew_gg
    # @File: certificate_parsing.py
    # @Software: cover_ios_api
    # ------------------------------
    
    import os
    from OpenSSL import crypto
    import datetime
    import time
    from dateutil import parser
    from pkg_common.ipa_monitor import calculation_month as cm
    
    
    def deal_utc(utc_time):
        """
        UTC时间转换
        :param utc_time:
        :return:
        """
        # UTC时间格式
        utc_format = "%Y-%m-%dT%H:%M:%SZ"
        sta_time = datetime.datetime.strptime(utc_time, utc_format) + datetime.timedelta(hours=8)
        time_array = time.strptime(str(sta_time), "%Y-%m-%d %H:%M:%S")
        time_stamp = int(time.mktime(time_array))
        return sta_time, time_stamp
    
    
    def parsing_p12(file, f_type, password=''):
        """
        解析p12、cer证书
        :param file:
        :param f_type:
        :param password:
        :return:
        """
        cer = ''
        if f_type == 'p12':
            p12 = crypto.load_pkcs12(open(file, 'rb').read(), password)
            cer = p12.get_certificate()
        if f_type == 'cer':
            crt_f = file.split('.cer')[0] + '.crt'
            # cer转换crt格式
            crt_cmd = "OpenSSL x509 -inform DER -in %s -out %s" % (file, crt_f)
            os.system(crt_cmd)
            with open(crt_f, "r", encoding='ISO-8859-1') as fp:
                crt_data = fp.read()
            cer = crypto.load_certificate(crypto.FILETYPE_PEM, crt_data)
            del_cmd = "rm -rf %s" % crt_f
            os.system(del_cmd)
        cer_dic = {}
    
        # 解析时间
        z_be_time = parser.parse(cer.get_notBefore().decode("UTF-8")).strftime('%Y-%m-%d %H:%M:%S')
        # 生成UTC时间格式并转换成北京时间
        before_time = deal_utc(z_be_time.split(' ')[0] + 'T' + z_be_time.split(' ')[1] + 'Z')[0]
        # 解析时间
        z_af_time = parser.parse(cer.get_notAfter().decode("UTF-8")).strftime('%Y-%m-%d %H:%M:%S')
        after_time = deal_utc(z_af_time.split(' ')[0] + 'T' + z_af_time.split(' ')[1] + 'Z')[0]
        # 获取时间月数差
        remaining_time = cm.cal_months(datetime.date.today(), after_time)
    
        cer_dic['before_time'] = before_time
        cer_dic['after_time'] = after_time
        cer_dic['remaining_time'] = remaining_time
    
        subject = cer.get_subject()
        s_components = subject.get_components()
        # 解析证书相关名称
        for (key, value) in s_components:
            if str(key, encoding='utf-8') == 'CN':
                cer_dic['user_id'] = str(value, encoding='utf-8')
            if str(key, encoding='utf-8') == 'OU':
                cer_dic['group'] = str(value, encoding='utf-8')
            if str(key, encoding='utf-8') == 'O':
                cer_dic['company'] = str(value, encoding='utf-8')
        if 'company' not in cer_dic.keys():
            cer_dic['company'] = ''
        if 'user_id' not in cer_dic.keys():
            cer_dic['user_id'] = ''
        if 'group' not in cer_dic.keys():
            cer_dic['group'] = ''
        return cer_dic
    
    
    if __name__ == '__main__':
    
        f_p121 = "/Users/Work/package/notice_montor/xxx推送证书.p12"
        f_p122 = "/Users/Work/package/notice_montor/xx推送证书.p12"
        f_cer = "/Users/Work/package/notice_montor/xxx证书.cer"
        pa = '123456'
        a = parsing_p12(f_p122, 'p12', "123456")
        print(a)

    需要注意的地方:

    1>.证书解析出的时间是utc格式,需要转换成北京时间,和直接解析ipa获取到的格式不一样,格式里不带“T”,需要自己构建格式后再转换。

    2>.cer格式不能直接解析,直接解析会报格式错误,需要先通过命令:"OpenSSL x509 -inform DER -in %s -out %s" % (file, crt_f)转换成crt文件,解析完crt文件后再删除即可

    3>.可能有的证书没有‘O’项,需要自己处理。

    2.监控时候可以把p12和cer都放一个目录下,写个方法遍历即可:

    # -*- coding = utf-8 -*-
    # ------------------------------
    # @time: 2021/3/29 5:18 PM
    # @Author: drew_gg
    # @File: notice_monitor.py
    # @Software: cover_ios_api
    # ------------------------------
    
    import datetime
    from pkg_dao import read_sql as rs
    from pkg_dao import flask_mysql as fm
    from pkg_common.handle_file import find_file as find
    from pkg_common.ipa_monitor import common_mail as mail
    from pkg_common.ipa_monitor import certificate_parsing as cp
    from pkg_common.ipa_monitor import notice_monitor_html as html
    
    
    def get_certificate_detail(to_find_path, password):
        """
        获取ipa的证书时间与描述文件时间
        :return:
        """
        company_name = ''
        to_file = ["'*.p12'", "'*.cer'"]
        f, f_l = find.find_file_more(to_file, to_find_path)
        all_dic = []
        cer_pa = {}
        for p in f_l:
            if p.split('.')[1] == 'p12':
                company_name = p.split('/')[-1].split('.p12')[0]
                cer_pa = cp.parsing_p12(p, 'p12', password)
            if p.split('.')[1] == 'cer':
                company_name = p.split('/')[-1].split('.cer')[0]
                cer_pa = cp.parsing_p12(p, 'cer')
            cer_pa['company_name'] = company_name
            all_dic.append(cer_pa)
        return all_dic
    
    
    if __name__ == '__main__':
    
        # 需要遍历ipa文件等目录
        find_path = '/Users/Work/package/notice_montor'
        pw = 'xxxxx'
        cer_all = get_certificate_detail(find_path, pw)
        # 升序排个序列
        cer_all = sorted(cer_all, key=lambda k: k['remaining_time'])
        for i in cer_all:
            sql_result = rs.deal_mysql("get_certificate_id.sql", list([i['user_id']]))
            update_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
            if sql_result:
                up_sal = """
                            UPDATE certificate_parsing 
                            SET `name` = '%s', 
                              company = '%s', 
                              `group` = '%s', 
                              remaining_time = '%s', 
                              before_time = '%s',
                              after_time = '%s', 
                              upload_time = '%s' 
                            WHERE 
                              is_delete = 0 
                            AND user_id = '%s' 
                            LIMIT 1;
                         """ % (i['company_name'], i['company'], i['group'], i['remaining_time'], i['before_time'], i['after_time'],
                                 update_time, i['user_id'])
                d = fm.Database()
                d.exec_no_query(up_sal)
            else:
                in_sql = """
                              INSERT INTO `certificate_parsing` (
                                  `name`, 
                                  `company`, 
                                  `group`, 
                                  `user_id`, 
                                  `remaining_time`, 
                                  `before_time`, 
                                  `after_time`,
                                  `upload_time`
                                  )
                              VALUES
                                  ( 
                                    '%s', 
                                    '%s', 
                                    '%s', 
                                    '%s', 
                                    '%s', 
                                    '%s', 
                                    '%s', 
                                    '%s'
                                    );
                            """ % (i['company_name'], i['company'], i['group'], i['user_id'], i['remaining_time'], i['before_time'],
                                   i['after_time'], update_time)
                d = fm.Database()
                d.exec_no_query(in_sql)
    
        subject, html = html.deal_html(cer_all)
        try:
            if mail.cs_mail_send(subject, html, 'iOS'):
                print('Send success')
            else:
                print('Send failure')
        except Exception as ex:
            print(ex)

    find_file_more方法和简单:

    def find_file_more(file_l, path):
        """
        :param file_l:
        :param path:
        :return:
        """
        f = []
        f_a = []
        find_cmd = ''
        for index, f_t in enumerate(file_l):
            if index == 0:
                find_cmd = 'find . -iname %s' % f_t
            else:
                find_cmd += " -o -iname %s" % f_t
    
        file_list = cmd.run_cmd(find_cmd, path).read().split('./')
        for i in range(len(file_list)):
            if i != 0:
                file_all = path + '/' + file_list[i].strip()
                f_a.append(file_all)
                file = file_list[i].split('/')[-1].split('.')[0].strip()
                f.append(file)
        return f, f_a

    拼接find . -iname ** -o -iname ** -o -iname ** 命令。

    然后把获取到的证书信息写入数据库和生成邮件告警出来就行,也可以定时每周五执行一次就好。

    计算月数的话,之前给的那个比较复杂,不容易懂,新写个方法,比较简单粗暴:

    # -*- coding = utf-8 -*-
    # ------------------------------
    # @time: 2021/2/1 4:13 PM
    # @Author: drew_gg
    # @File: calculation_month.py
    # @Software: Build_Packaging
    # ------------------------------
    
    
    # coding = utf-8
    # 计算日期的月份差
    
    
    import datetime
    
    
    def cal_months(start_date, end_date):
        """
        计算两个日期的月份差,精确到小数位
        :param start_date: 日期必须为date格式
        :param end_date: 日期必须为date格式
        :return:
        """
    
        # 计算两个日期相隔月差
        ey = end_date.year
        em = end_date.month
        ed = end_date.day
        sy = start_date.year
        sm = start_date.month
        sd = start_date.day
    
        if ey < sy:
            raise AssertionError('被减日期大了!')
        elif ey == sy and em < sm:
            raise AssertionError('被减日期大了!')
        elif ey == sy and em == sm and ed < sd:
            raise AssertionError('被减日期大了!')
        else:
            aed = round((ey - sy) * 12 + (em - sm) + (ed - sd)/30.5, 2)
        return aed
    
    
    if __name__ == '__main__':
        sd = datetime.date(2021, 3, 29)
        ed = datetime.date(2022, 1, 22)
        a = cal_months(sd, ed)

    年月平均天数30.5天,按照这个来算月数,是不是简单粗暴,反正相差不大,就这样了。

  • 相关阅读:
    Blender第5个模型
    Blender 积木下落物理效果
    Blender 积木彩色渲染加下落物理效果
    Blender第7个模型
    Blender 积木学习快捷键与软件配置(基础课程完结撒花)
    Blender 第14个模型
    Blender 积木学习快捷键与软件配置(进阶版持续更新)
    【数据库】Mysql表字段转Java实体(sql语句)
    【Java】MybatisPlus获取不到数据库表中的部分字段(带下划线_的字段)
    【Python】字符串字典 转 字典/json字符串
  • 原文地址:https://www.cnblogs.com/drewgg/p/14611718.html
Copyright © 2020-2023  润新知