之前博客寫過直接解析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天,按照這個來算月數,是不是簡單粗暴,反正相差不大,就這樣了。