iOS企业证书开发的APP证书过期时间监控

时间:2024-03-10 13:35:04

大家都知道iOS的企业证书开发的APP,证书都是一年有效期,满一年得新建证书重新打包,否则无法继续使用。

我们一个企业账号下有几十个APP,一个个去看也很麻烦~~搞个监控呗!!!

 

写个脚本放Jenkins上定时跑就行,跑完发布邮件:

 

 

 

 

1.邮件包含信息:APP名称,APP相关的bundle id、证书名称、签名时间、团队id,过期时间,以及剩余有效的月数。

 

common_mail.py: 发布邮件,邮件发送网上多,自己找,不累述

copy_ipa_monitor.py: 把打好的ipa复制到指定的目录,这个目录给监控的扫描用

date_month.py: 专门计算月份差,Python没有直接计算月份差的模块,自己写,考虑下各种条件计算

ipa_monitor.py: 获取ipa的有效时间以及ipa包里描述文件里的有效时间

ipa_monitor_html.py: 生成邮件发送的html模版

 

贴代码:

  1 ## ipa_monitor.py
  2 # coding = utf-8
  3 # 读取配置证书
  4 
  5 import os
  6 import time
  7 import shutil
  8 import datetime
  9 from pkg_common.cmd import common_run_cmd as cmd
 10 from pkg_common.handle_file import find_file as find
 11 from pkg_common.ipa_monitor import common_mail as mail
 12 from pkg_common.ipa_monitor import ipa_monitor_html as html
 13 
 14 
 15 to_find_path = \'/Users/Work/package/ipa_monitor\'
 16 
 17 
 18 # 获取证书时间一些命令 
 19 codesign_cmd = \'codesign -d --extract-certificates Payload/*.app\'
 20 openssl_cmd = \'openssl x509 -inform DER -in codesign0 -noout -nameopt -oneline -dates\'
 21 codesign_get_cmd = \'codesign -dv --verbose=4 Payload/*.app\'
 22 mobile_provision_cmd = \'more Payload/Runner.app/embedded.mobileprovision\'
 23 
 24 
 25 def get_ipa_time():
 26     f, f_l = find.find_file("\'*.ipa\'", to_find_path)
 27     all_dic = []
 28     for i in f_l:
 29         dic = {}
 30         dir_name = os.path.dirname(i)
 31         app_name = dir_name.split(\'/\')[-1]
 32         dic[\'app_name\'] = app_name
 33         base_name = os.path.basename(i)
 34         un_zip_cmd = \'unzip -q %s\' % base_name
 35         # 解压ipa文件
 36         cmd.run_cmd(un_zip_cmd, dir_name)
 37         time.sleep(2)
 38         cmd.run_commands(codesign_cmd, dir_name)
 39         time.sleep(2)
 40         not_time = cmd.run_commands(openssl_cmd, dir_name)[1]
 41         not_after, not_stamp = deal_gmt(not_time.split(\'\n\')[1].split(\'=\')[1])
 42         f, f_l = find.find_file("\'*.mobileprovision\'", dir_name)
 43         ipa_no_time = \'\'
 44         ipa_no_stamp = 0
 45         # 获取描述文件里的过期时间,这里解码格式一定要用ISO-8859-1
 46         with open(f_l[0], \'r\', encoding=\'ISO-8859-1\') as f_w:
 47             lines = f_w.readlines()
 48             for index, l in enumerate(lines):
 49                 if \'ExpirationDate\' in l:
 50                     ipa_no_time = lines[index+1].split(\'>\')[1].split(\'<\')[0]
 51                     ipa_no_time, ipa_no_stamp = deal_utc(ipa_no_time)
 52                     break
 53         if not_stamp <= ipa_no_stamp:
 54             expired_time = not_after
 55         else:
 56             expired_time = ipa_no_time
 57         dic[\'expired_time\'] = expired_time
 58         pro_info = cmd.run_commands(codesign_get_cmd, dir_name)[1].split(\'\n\')
 59         delete_file(dir_name)
 60 
 61         for x in pro_info:
 62             if \'Identifier\' == x.split(\'=\')[0]:
 63                 dic[\'Identifier\'] = x.split(\'=\')[1]
 64             if \'Authority\' in x:
 65                 if \'Authority\' not in dic:
 66                     dic[\'Authority\'] = x.split(\'=\')[1]
 67             if \'Signed Time\' in x:
 68                 dic[\'Signed_time\'] = x.split(\'=\')[1]
 69             if \'TeamIdentifier\' in x:
 70                 dic[\'TeamIdentifier\'] = x.split(\'=\')[1]
 71         all_dic.append(dic)
 72     return all_dic
 73 
 74 
 75 def deal_gmt(gmt_time):
 76     """
 77     GMT时间转换
 78     :param gmt_time:
 79     :return:
 80     """
 81     gmt_format = \'%b %d %H:%M:%S %Y GMT\'
 82     # GMT时间与北京时间相差8小时
 83     sta_time = datetime.datetime.strptime(gmt_time, gmt_format) + datetime.timedelta(hours=8)
 84     time_array = time.strptime(str(sta_time), "%Y-%m-%d %H:%M:%S")
 85     time_stamp = int(time.mktime(time_array))
 86     return sta_time, time_stamp
 87 
 88 
 89 def deal_utc(utc_time):
 90     utc_format = "%Y-%m-%dT%H:%M:%SZ"
 91     sta_time = datetime.datetime.strptime(utc_time, utc_format) + datetime.timedelta(hours=8)
 92     time_array = time.strptime(str(sta_time), "%Y-%m-%d %H:%M:%S")
 93     time_stamp = int(time.mktime(time_array))
 94     return sta_time, time_stamp
 95 
 96 
 97 def delete_file(dir_name):
 98     """
 99     删除多余文件
100     :param dir_name:
101     :return:
102     """
103     os.remove(dir_name + \'/codesign0\')
104     os.remove(dir_name + \'/codesign1\')
105     os.remove(dir_name + \'/codesign2\')
106     shutil.rmtree(dir_name + \'/Payload\')
107 
108 
109 if __name__ == \'__main__\':
110     ipa_info = get_ipa_time()
111     subject, html = html.deal_html(ipa_info)
112     try:
113         if mail.cs_mail_send(subject, html, \'c\'):
114             print(\'Send success\')
115         else:
116             print(\'Send failure\')
117     except Exception as e:
118         raise e
119         

 

## date_month.py

import datetime
import calendar as c


def cal_months(start_date, end_date):

    # 计算两个日期相隔月差
    try:
        same_month_date = datetime.date(end_date.year, end_date.month, start_date.day)
    except Exception as e:
        print(e)
        same_month_date = datetime.date(end_date.year, end_date.month, c.monthrange(end_date.year, end_date.month)[1])
    decimal_month = 0.0
    if same_month_date > end_date:
        try:
            pre_date = datetime.date(end_date.year, end_date.month - 1, start_date.day)
        except Exception as e:
            print(e)
            pre_date = datetime.date(end_date.year, end_date.month - 1, c.monthrange(end_date.year, end_date.month - 1)[1])
        curr_month_days = (same_month_date - pre_date).days
        hold_months = (pre_date.year - start_date.year) * 12 + pre_date.month - start_date.month
        decimal_month = round((end_date - pre_date).days / curr_month_days, 1)

    elif same_month_date < end_date:
        try:
            next_month_date = datetime.date(end_date.year, end_date.month + 1, start_date.day)
        except Exception as e:
            print(e)
            next_month_date = datetime.date(end_date.year, end_date.month + 1, c.monthrange(end_date.year, end_date.month + 1)[1])
        curr_month_days = (next_month_date - same_month_date).days
        hold_months = (same_month_date.year - start_date.year) * 12 + same_month_date.month - start_date.month
        decimal_month = round((end_date - same_month_date).days / curr_month_days, 1)

    else:
        hold_months = (end_date.year - start_date.year) * 12 + end_date.month - start_date.month

    return hold_months + decimal_month

 

## ipa_monitor_html.py
# coding = utf-8
# ipa过期时间监控

import datetime
from pkg_common.ipa_monitor import date_month as dm


# 处理html
def deal_html(data):
    tl = deal_tl(data)
    subject = \'企业证书iOS应用过期时间监控\'
    html = """
        <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <title>企业证书iOS应用过期时间监控</title>
        <body>
        <div id="container">
         <center>
        <strong>汇总时间: """ + str(datetime.datetime.now().strftime(\'%Y-%m-%d %H:%M:%S\')) + """</strong>
        <p><strong></strong></p>
        <div id="content">
         <table border="1" cellpadding="2" style="border-collapse:collapse;margin-top:10px">
         <tr>
           <td colspan="4" align="center" height="35px"><font size="4"><strong>只针对企业证书打包的应用</strong>[<font color="red">有效期小于3月的红色加粗显示</font>]</font></td> 
         </tr>
         <tr height="28px" bgcolor="#f0f8ff">
          <td width="120" align="center"><font size="3"><strong>应用名称</strong></font></td>
          <td width="520" align="center"><font size="3"><strong>APP参数</strong></font></td>
          <td width="180" align="center"><font size="3"><strong>过期时间</strong></font></td>
          <td width="80" align="center"><font size="3"><strong>剩余月数</strong></font></td>
         </tr>              
        """ + tl + """
        </tr>
        </table>
        <p><font size="3" ><center>--------------------<strong><a href="http://chandao.thecover.cn/">汇总数据源于测试监控系统</a></strong>--------------------</center></font>
        </center>
        </div>
        </div>
        </div>
        </body>
        </html>
              """
    return subject, html


def deal_tl(data):

    tl_a = \'\'
    for i in data:

        dt1 = i[\'expired_time\'].date()
        dt2 = datetime.date.today()
        dt = dm.cal_months(dt2, dt1)

        a = """
        <tr>
            <td rowspan="4" align="center"> %s </td>
            <td align="left"> Identifier:%s</td> 
            <td rowspan="4" align="center"> %s</td>
        """ % (i[\'app_name\'], i[\'Identifier\'], i[\'expired_time\'])

        if dt > 3:
            b = """ <td rowspan="4" align="center"> %s</td> """ % dt
        else:
            b = """ <td rowspan="4" align="center"> <strong><font color="red"> %s </font></strong></td> """ % dt

        c = """
        </tr>
        <tr>
            <td align="left"> Authority:%s</td>
        </tr>
        <tr>
            <td align="left"> Signed_time:%s </td>
        </tr>
        <tr>
            <td align="left"> TeamIdentifier:%s </td>
        </tr>
        <tr>
            <td colspan="4" align="center" height="0px" bgcolor="#a9a9a9"><font size="4"> </td>
        </tr>
        """ % (i[\'Authority\'], i[\'Signed_time\'], i[\'TeamIdentifier\'])

        tl_a = tl_a + a + b + c

    return tl_a

 

其他py文件很简单,就不贴了,若遇到问题,可留言交流~~