diff --git a/fir_ser/api/utils/app/iossignapi.py b/fir_ser/api/utils/app/iossignapi.py index 7b8bef6..d691603 100644 --- a/fir_ser/api/utils/app/iossignapi.py +++ b/fir_ser/api/utils/app/iossignapi.py @@ -9,6 +9,7 @@ from api.utils.app.shellcmds import shell_command, use_user_pass from api.utils.baseutils import get_format_time, format_apple_date from fir_ser.settings import SUPER_SIGN_ROOT import os +import re from api.utils.app.randomstrings import make_app_uuid import logging from api.utils.apple.appleapiv3 import AppStoreConnectApi @@ -49,18 +50,46 @@ class ResignApp(object): self.cmd = "zsign -c '%s' -k '%s' " % (self.app_dev_pem, self.my_local_key) @staticmethod - def sign_mobile_config(mobile_config_path, sign_mobile_config_path, ssl_pem_path, ssl_key_path): + def sign_mobile_config(sign_data, ssl_pem_path, ssl_key_path): """ - :param mobile_config_path: 描述文件绝对路径 - :param sign_mobile_config_path: 签名之后的文件绝对路径 + :param sign_data: 签名的数据 :param ssl_pem_path: pem证书的绝对路径 :param ssl_key_path: key证书的绝对路径 :return: """ - cmd = "openssl smime -sign -in %s -out %s -signer %s " \ - "-inkey %s -certfile %s -outform der -nodetach " % ( - mobile_config_path, sign_mobile_config_path, ssl_pem_path, ssl_key_path, ssl_pem_path) - return exec_shell(cmd) + # + # cmd = "openssl smime -sign -in %s -out %s -signer %s " \ + # "-inkey %s -certfile %s -outform der -nodetach " % ( + # mobile_config_path, sign_mobile_config_path, ssl_pem_path, ssl_key_path, ssl_pem_path) + # return exec_shell(cmd) + result = {} + from cryptography.hazmat.primitives import hashes, serialization + from cryptography.hazmat.primitives.serialization import pkcs7 + from cryptography import x509 + try: + cert_list = re.findall('-----BEGIN CERTIFICATE-----.*?-----END CERTIFICATE-----', + open(ssl_pem_path, 'r').read(), re.S) + if len(cert_list) == 0: + raise Exception('load cert failed') + else: + cert = x509.load_pem_x509_certificate(cert_list[0].encode('utf-8')) + cas = [cert] + if len(cert_list) > 1: + cas.extend([x509.load_pem_x509_certificate(x.encode('utf-8')) for x in cert_list[1:]]) + key = serialization.load_pem_private_key(open(ssl_key_path, 'rb').read(), None) + result['data'] = pkcs7.PKCS7SignatureBuilder( + data=sign_data, + signers=[ + (cert, key, hashes.SHA512()), + ], + additional_certs=cas, + ).sign( + serialization.Encoding.DER, options=[], + ) + except Exception as e: + result['err_info'] = str(e) + return False, result + return True, result def make_p12_from_cert(self, password): result = {} diff --git a/fir_ser/api/utils/app/supersignutils.py b/fir_ser/api/utils/app/supersignutils.py index d0a3264..fe8ee66 100644 --- a/fir_ser/api/utils/app/supersignutils.py +++ b/fir_ser/api/utils/app/supersignutils.py @@ -81,7 +81,7 @@ def udid_bytes_to_dict(xml_stream): return new_uuid_info -def make_sign_udid_mobile_config(udid_url, payload_organization, app_name): +def make_sign_udid_mobile_config(udid_url, app_id, bundle_id, app_name): if MOBILECONFIG_SIGN_SSL.get("open"): ssl_key_path = MOBILECONFIG_SIGN_SSL.get("ssl_key_path", None) ssl_pem_path = MOBILECONFIG_SIGN_SSL.get("ssl_pem_path", None) @@ -91,30 +91,31 @@ def make_sign_udid_mobile_config(udid_url, payload_organization, app_name): if not os.path.exists(mobile_config_tmp_dir): os.makedirs(mobile_config_tmp_dir) - mobile_config_filename = payload_organization + str(uuid.uuid1()) - mobile_config_path = os.path.join(mobile_config_tmp_dir, mobile_config_filename) + sign_mobile_config_path = os.path.join(mobile_config_tmp_dir, 'sign_' + app_id) + logger.info(f"make sing mobile config {sign_mobile_config_path}") + if os.path.isfile(sign_mobile_config_path): + return open(sign_mobile_config_path, 'rb') - sign_mobile_config_path = os.path.join(mobile_config_tmp_dir, 'sign_' + mobile_config_filename) - with open(mobile_config_path, "w") as f: - f.write(make_udid_mobile_config(udid_url, payload_organization, app_name)) + status, result = ResignApp.sign_mobile_config( + make_udid_mobile_config(udid_url, bundle_id, app_name), + ssl_pem_path, + ssl_key_path) - status, result = ResignApp.sign_mobile_config(mobile_config_path, sign_mobile_config_path, ssl_pem_path, - ssl_key_path) - if status: - mobile_config_body = open(sign_mobile_config_path, 'rb') + if status and result.get('data'): + with open(sign_mobile_config_path, 'wb') as f: + f.write(result.get('data')) + return open(sign_mobile_config_path, 'rb') else: logger.error( - f"{payload_organization} {app_name} sign_mobile_config failed ERROR:{result.get('err_info')}") - return make_udid_mobile_config(udid_url, payload_organization, app_name) - - return mobile_config_body + f"{bundle_id} {app_name} sign_mobile_config failed ERROR:{result.get('err_info')}") + return make_udid_mobile_config(udid_url, bundle_id, app_name) else: logger.error(f"sign_mobile_config {ssl_key_path} or {ssl_pem_path} is not exists") - return make_udid_mobile_config(udid_url, payload_organization, app_name) + return make_udid_mobile_config(udid_url, bundle_id, app_name) else: - return make_udid_mobile_config(udid_url, payload_organization, app_name) + return make_udid_mobile_config(udid_url, bundle_id, app_name) def make_udid_mobile_config(udid_url, payload_organization, app_name, payload_uuid=uuid.uuid1(), @@ -627,7 +628,6 @@ class IosUtils(object): """ 该APP为超级签,删除app的时候,需要清理一下开发者账户里面的profile 和 bundleid :param app_obj: - :param user_obj: :return: """ diff --git a/fir_ser/api/utils/crontab/ctasks.py b/fir_ser/api/utils/crontab/ctasks.py index 3388bb2..d9097f1 100644 --- a/fir_ser/api/utils/crontab/ctasks.py +++ b/fir_ser/api/utils/crontab/ctasks.py @@ -49,7 +49,7 @@ def auto_clean_upload_tmp_file(): def auto_delete_ios_mobile_tmp_file(): - mobile_config_tmp_dir = os.path.join(SUPER_SIGN_ROOT, 'tmp', 'mobileconfig') + mobile_config_tmp_dir = os.path.join(SUPER_SIGN_ROOT, 'tmp', 'mobile_config') for root, dirs, files in os.walk(mobile_config_tmp_dir, topdown=False): now_time = time.time() for name in files: diff --git a/fir_ser/api/views/download.py b/fir_ser/api/views/download.py index 3030a07..fb67126 100644 --- a/fir_ser/api/views/download.py +++ b/fir_ser/api/views/download.py @@ -72,9 +72,10 @@ class DownloadView(APIView): elif f_type == 'mobileconifg': release_obj = AppReleaseInfo.objects.filter(release_id=filename.split('.')[0]).first() if release_obj: - bundle_id = release_obj.app_id.bundle_id udid_url = get_post_udid_url(request, release_obj.app_id.short) - ios_udid_mobile_config = make_sign_udid_mobile_config(udid_url, bundle_id, release_obj.app_id.name) + app_obj = release_obj.app_id + ios_udid_mobile_config = make_sign_udid_mobile_config(udid_url, app_obj.app_id, app_obj.bundle_id, + app_obj.app_name) response = FileResponse(ios_udid_mobile_config) response['Content-Type'] = "application/x-apple-aspen-config" response['Content-Disposition'] = 'attachment; filename=' + make_random_uuid() + '.mobileconfig' diff --git a/fir_ser/api/views/receiveudids.py b/fir_ser/api/views/receiveudids.py index 0737b1b..896b59c 100644 --- a/fir_ser/api/views/receiveudids.py +++ b/fir_ser/api/views/receiveudids.py @@ -3,11 +3,12 @@ # project: 3月 # author: liuyu # date: 2020/3/6 - -from api.utils.app.supersignutils import udid_bytes_to_dict, get_redirect_server_domain +from api.utils.app.randomstrings import make_random_uuid +from api.utils.app.supersignutils import udid_bytes_to_dict, get_redirect_server_domain, make_sign_udid_mobile_config, \ + get_post_udid_url, get_http_server_domain from api.models import Apps from django.views import View -from django.http import HttpResponsePermanentRedirect +from django.http import HttpResponsePermanentRedirect, FileResponse from rest_framework.response import Response from api.tasks import run_sign_task from api.utils.response import BaseResponse @@ -89,3 +90,23 @@ class TaskView(APIView): return Response(res.dict) res.code = 1002 return Response(res.dict) + + +class ShowUdidView(View): + def get(self, request): + server_domain = get_http_server_domain(request) + path_info_lists = [server_domain, "look_udid"] + udid_url = "/".join(path_info_lists) + ios_udid_mobile_config = make_sign_udid_mobile_config(udid_url, 'show_udid_info', 'flyapps.cn', '查询设备udid') + response = FileResponse(ios_udid_mobile_config) + response['Content-Type'] = "application/x-apple-aspen-config" + response['Content-Disposition'] = 'attachment; filename=' + make_random_uuid() + '.mobileconfig' + return response + + def post(self, request): + stream_f = str(request.body) + format_udid_info = udid_bytes_to_dict(stream_f) + logger.info(f"look_udid receive new udid {format_udid_info}") + server_domain = get_redirect_server_domain(request) + return HttpResponsePermanentRedirect( + "%sudid=%s" % (server_domain, format_udid_info.get("udid"))) diff --git a/fir_ser/fir_ser/urls.py b/fir_ser/fir_ser/urls.py index 33927ce..4349dae 100644 --- a/fir_ser/fir_ser/urls.py +++ b/fir_ser/fir_ser/urls.py @@ -18,7 +18,7 @@ from django.urls import re_path, include from django.views.static import serve from fir_ser import settings from api.views.download import DownloadView, InstallView -from api.views.receiveudids import IosUDIDView +from api.views.receiveudids import IosUDIDView, ShowUdidView urlpatterns = [ re_path('fly.admin/', admin.site.urls), @@ -31,5 +31,6 @@ urlpatterns = [ re_path("download/(?P\w+\.\w+)$", DownloadView.as_view(), name="download"), re_path("install/(?P\w+)$", InstallView.as_view(), name="install"), re_path("^udid/(?P\w+)$", IosUDIDView.as_view()), + re_path("^look_udid$", ShowUdidView.as_view()), ]