#!/usr/bin/env python """Codesign utility""" import argparse import subprocess import os import time import shutil import json parser = argparse.ArgumentParser(description = 'Codesign utility for Ninja builds') parser.add_argument('file', type=str, help = 'Bundle/package to sign') parser.add_argument('--target', type=str, help = 'Target', choices = ['macos', 'ios', 'android'], default = '') parser.add_argument('--bundle', type=str, help = 'Bundle identifier (OSX/iOS)', default = '') parser.add_argument('--organisation', type=str, help = 'Organisation identifier (OSX/iOS)', default = '') parser.add_argument('--provisioning', type=str, help = 'Provisioning profile (OSX/iOS)', default = '') parser.add_argument('--builddir', type=str, help = 'Build directory (OSX/iOS)', default = '') parser.add_argument('--binname', type=str, help = 'Binary name (OSX/iOS)', default = '') parser.add_argument('--zipfile', type=str, help = 'Zip file (Android)', default = '') parser.add_argument('--tsacert', type=str, help = 'TSA cert (Android)', default = '') parser.add_argument('--tsa', type=str, help = 'TSA (Android)', default = '') parser.add_argument('--keystore', type=str, help = 'Keystore (Android)', default = '') parser.add_argument('--keystorepass', type=str, help = 'Keystore password (Android)', default = '') parser.add_argument('--keyalias', type=str, help = 'Key alias (Android)', default = '') parser.add_argument('--keypass', type=str, help = 'Key password (Android)', default = '') parser.add_argument('--jarsigner', type=str, help = 'JAR signer (Android)', default = 'jarsigner') parser.add_argument('--prefs', type=str, help = 'Preferences file', default = '') parser.add_argument('--config', type=str, help = 'Build configuration', default = '') parser.add_argument('--entitlements', type=str, help = 'Entitlements file', default = '') options = parser.parse_args() androidprefs = {} iosprefs = {} macosprefs = {} def parse_prefs( prefsfile ): global androidprefs global iosprefs global macosprefs if not os.path.isfile( prefsfile ): return file = open( prefsfile, 'r' ) prefs = json.load( file ) file.close() if 'android' in prefs: androidprefs = prefs['android'] if 'ios' in prefs: iosprefs = prefs['ios'] if 'macos' in prefs: macosprefs = prefs['macos'] def codesign_ios(): if not 'organisation' in iosprefs: iosprefs['organisation'] = options.organisation if not 'bundleidentifier' in iosprefs: iosprefs['bundleidentifier'] = options.bundle if not 'provisioning' in iosprefs: iosprefs['provisioning'] = options.provisioning sdkdir = subprocess.check_output( [ 'xcrun', '--sdk', 'iphoneos', '--show-sdk-path' ] ).decode().strip().splitlines()[-1] entitlements = os.path.join( sdkdir, 'Entitlements.plist' ) plistpath = os.path.join( options.builddir, 'Entitlements.xcent' ) platformpath = subprocess.check_output( [ 'xcrun', '--sdk', 'iphoneos', '--show-sdk-platform-path' ] ).decode().strip().splitlines()[-1] localpath = platformpath + "/Developer/usr/bin:/Applications/Xcode.app/Contents/Developer/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin" plutil = "PATH=" + localpath + " " + subprocess.check_output( [ 'xcrun', '--sdk', 'iphoneos', '-f', 'plutil' ] ).decode().strip().splitlines()[-1] shutil.copyfile( entitlements, plistpath ) os.system( plutil + ' -convert xml1 ' + plistpath ) f = open( plistpath, 'r' ) lines = [ line.strip( '\n\r' ) for line in f ] f.close() for i in range( 0, len( lines ) ): if lines[i].find( '$(AppIdentifierPrefix)' ) != -1: lines[i] = lines[i].replace( '$(AppIdentifierPrefix)', iosprefs['organisation'] + '.' ) if lines[i].find( '$(CFBundleIdentifier)' ) != -1: lines[i] = lines[i].replace( '$(CFBundleIdentifier)', iosprefs['bundleidentifier'] ) if lines[i].find( '$(binname)' ) != -1: lines[i] = lines[i].replace( '$(binname)', options.binname ) with open( plistpath, 'w' ) as plist_file: for line in lines: if options.config != 'deploy' and line == '': plist_file.write( '\tget-task-allow\n' ) plist_file.write( '\t\n' ) plist_file.write( line + '\n' ) plist_file.close() if os.path.isfile( os.path.join( options.file, '_CodeSignature', 'CodeResources' ) ): os.remove( os.path.join( options.file, '_CodeSignature', 'CodeResources' ) ) os.system( '/usr/bin/codesign --force --sign "' + iosprefs['signature'] + '" --entitlements ' + plistpath + ' ' + options.file ) if os.path.isfile( os.path.join( options.file, '_CodeSignature', 'CodeResources' ) ): os.utime( os.path.join( options.file, '_CodeSignature', 'CodeResources' ), None ) os.utime( os.path.join( options.file, '_CodeSignature' ), None ) os.utime( options.file, None ) def codesign_macos(): if not 'organisation' in macosprefs: macosprefs['organisation'] = options.organisation if not 'bundleidentifier' in macosprefs: macosprefs['bundleidentifier'] = options.bundle if not 'provisioning' in macosprefs: macosprefs['provisioning'] = options.provisioning if not 'entitlements' in macosprefs: macosprefs['entitlements'] = options.entitlements codesign_allocate = subprocess.check_output( [ 'xcrun', '--sdk', 'macosx', '-f', 'codesign_allocate' ] ).decode().strip().splitlines()[-1] sdkdir = subprocess.check_output( [ 'xcrun', '--sdk', 'macosx', '--show-sdk-path' ] ).decode().strip().splitlines()[-1] entitlements = os.path.join( sdkdir, 'Entitlements.plist' ) if os.path.isfile( os.path.join( options.file, 'Contents', '_CodeSignature', 'CodeResources' ) ): os.remove( os.path.join( options.file, 'Contents', '_CodeSignature', 'CodeResources' ) ) if 'signature' in macosprefs: command = 'export CODESIGN_ALLOCATE=' + codesign_allocate + '; /usr/bin/codesign --force --sign "' + macosprefs['signature'] + '" -o runtime ' if ('entitlements' in macosprefs) and (macosprefs['entitlements'] != '') and (macosprefs['entitlements'] != 'none'): command = command + '--entitlements ' + macosprefs['entitlements'] + ' --generate-entitlement-der ' command = command + options.file # print(command) os.system(command) if os.path.isfile( os.path.join( options.file, 'Contents', '_CodeSignature', 'CodeResources' ) ): os.utime( os.path.join( options.file, 'Contents', '_CodeSignature', 'CodeResources' ), None ) os.utime( os.path.join( options.file, 'Contents', '_CodeSignature' ), None ) os.utime( os.path.join( options.file, 'Contents' ), None ) os.utime( options.file, None ) def codesign_android(): if not 'tsacert' in androidprefs: androidprefs['tsacert'] = options.tsacert if not 'tsa' in androidprefs: androidprefs['tsa'] = options.tsa if not 'keystore' in androidprefs: androidprefs['keystore'] = options.keystore if not 'keystorepass' in androidprefs: androidprefs['keystorepass'] = options.keystorepass if not 'keyalias' in androidprefs: androidprefs['keyalias'] = options.keyalias if not 'keypass' in androidprefs: androidprefs['keypass'] = options.keypass if not 'jarsigner' in androidprefs: androidprefs['jarsigner'] = options.jarsigner timestamp = '' if androidprefs['tsacert'] != '': timestamp = '-tsacert ' + androidprefs['tsacert'] elif androidprefs['tsa'] != '': timestamp = '-tsa ' + androidprefs['tsa'] proxy = '' if 'proxy' in androidprefs and androidprefs['proxy'] != '' and androidprefs['proxy'] != 'None': proxy = androidprefs['proxy'] if proxy != '' and proxy != 'None': defstr = "-J-Dhttp.proxy" url = urlparse.urlparse(proxy) if url.scheme == 'https': defstr = "-J-Dhttps.proxy" host = url.netloc port = '' username = '' password = '' if '@' in host: username, host = host.split(':', 1) password, host = host.split('@', 1) if ':' in host: host, port = host.split(':', 1) proxy = defstr + "Host=" + host if port != '': proxy += " " + defstr + "Port=" + port if username != '': proxy += " " + defstr + "User=" + username if password != '': proxy += " " + defstr + "Password=" + password signcmd = androidprefs['jarsigner'] + ' ' + timestamp + ' -sigalg SHA1withRSA -digestalg SHA1 -keystore ' + androidprefs['keystore'] + ' -storepass ' + androidprefs['keystorepass'] + ' -keypass ' + androidprefs['keypass'] + ' -signedjar ' + options.file + ' ' + options.zipfile + ' ' + androidprefs['keyalias'] + ' ' + proxy os.system(signcmd) parse_prefs( options.prefs ) if options.target == 'ios': codesign_ios() elif options.target == 'macos': codesign_macos() elif options.target == 'android': codesign_android()