Created
June 30, 2014 13:22
-
-
Save seaders/806d1a7ec47eceea1e1b to your computer and use it in GitHub Desktop.
Python ipa resigner script
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import argparse | |
import sys | |
import os | |
import shutil | |
import re | |
import shlex | |
import subprocess | |
import plistlib | |
from functools import wraps | |
ENTITLEMENTS_NAME = 'Entitlements.plist' | |
EMBEDDED_PROV_NAME = 'embedded.mobileprovision' | |
PAYLOAD_KEY = 'Payload' | |
APP_ID_KEY = 'application-identifier' | |
APP_EXT = '.app' | |
PLIST_START = '<?xml' | |
PLIST_END = '</plist>' | |
def rm(path): | |
shutil.rmtree(path, True) | |
try: | |
os.unlink(path) | |
except: | |
pass | |
def execCommand(cmd, cwd=None, exit_on_fail=True, shout=False, | |
ignoreErrors=False, ignoreError='', path=None, home=None): | |
path = path or execCommand.path | |
home = home or execCommand.home | |
cwd = cwd or execCommand.cwd | |
print '({}) {}'.format(cwd, cmd) | |
args = shlex.split(cmd) | |
try: | |
process = subprocess.Popen(args, stdout=subprocess.PIPE, | |
stderr=subprocess.PIPE, cwd=cwd, | |
env=dict(PATH=path, HOME=home)) | |
except OSError as(_, strerror): | |
if not ignoreErrors: | |
print ' !!! Error executing command >' + cmd + ': ' + strerror | |
if exit_on_fail: | |
sys.exit(1) | |
output, error = process.communicate() | |
if ignoreError == error: | |
output += ignoreError | |
error = '' | |
if '' != error and not ignoreErrors: | |
print '!!! Error executing command\n\t{}\n\t\'{}\'\n\t\'{}\''.\ | |
format(cmd, error, output) | |
if exit_on_fail: | |
sys.exit(1) | |
output = output.strip() | |
if shout: | |
print '{} {}\n\t{}'.format(output, process.returncode, error) | |
return output, error, process.returncode | |
execCommand.path = '/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin' | |
execCommand.home = '/var/lib/jenkins' | |
execCommand.cwd = '.' | |
def parseArgs(fn): | |
@wraps(fn) | |
def wrapper(*args, **kwargs): | |
# Get command line args | |
parser = argparse.ArgumentParser(description='Resign IPA') | |
parser.add_argument('--input', '-i', help='Input IPA') | |
parser.add_argument('--output', '-o', help='Output IPA') | |
parser.add_argument('--mobile_provision', '-mp', | |
help='Mobile Provision file') | |
parser.add_argument('--dev_cert', '-dc', | |
help='Developer Certificate Name') | |
parser.add_argument('--user_home', '-uh', | |
help='Home dir for keychain user') | |
if 11 != len(sys.argv): | |
parser.print_help() | |
sys.exit(1) | |
args = parser.parse_args() | |
execCommand.home = args.user_home | |
fn(args.input, args.output, args.dev_cert, args.mobile_provision) | |
return wrapper | |
def entitlementsFromProvision(path): | |
with open(path) as f: | |
data = f.read() | |
xml = data[data.find(PLIST_START):data.find(PLIST_END) + len(PLIST_END)] | |
pl = plistlib.readPlistFromString(xml) | |
return pl['Entitlements'] | |
@parseArgs | |
def main(ipaInput, ipaOutput, dev_cert, mobile_provision): | |
ipaInput, ipaOutput = (os.path.abspath(a) for a in (ipaInput, ipaOutput)) | |
output_dir = os.path.dirname(ipaOutput) | |
payload_dir = os.path.join(output_dir, PAYLOAD_KEY) | |
entitlement_file = os.path.join(output_dir, ENTITLEMENTS_NAME) | |
execCommand.cwd = output_dir | |
def cleanup(initial=False): | |
rm(payload_dir) | |
rm(entitlement_file) | |
if initial: | |
rm(ipaOutput) | |
cleanup(True) | |
execCommand('unzip {}'.format(ipaInput), output_dir) | |
for _, folders, _ in os.walk(payload_dir): | |
appName = re.search(r'^(.*)\.app$', folders[0]).group(1) | |
break | |
appDir = os.path.join(payload_dir, appName + APP_EXT) | |
embedded_prov = os.path.join(appDir, EMBEDDED_PROV_NAME) | |
rm(os.path.join(appDir, '_CodeSignature')) | |
rm(os.path.join(appDir, 'CodeResources')) | |
theirEntitlements = entitlementsFromProvision(embedded_prov) | |
ourEntitlements = entitlementsFromProvision(mobile_provision) | |
ourEntitlements['com.apple.developer.team-identifier'] = \ | |
ourEntitlements['application-identifier'].partition('.')[0] | |
shutil.copy(mobile_provision, embedded_prov) | |
plistlib.writePlist(ourEntitlements, entitlement_file) | |
execCommand( | |
'/usr/bin/codesign -f -s "{}" --entitlements {} --resource-rules ' | |
'"{}/ResourceRules.plist" "{}"'. | |
format(dev_cert, entitlement_file, appDir, appDir), | |
output_dir, | |
ignoreError='{}: replacing existing signature\n'.format(appDir) | |
) | |
execCommand('zip -qr {} {}'.format(ipaOutput, PAYLOAD_KEY), output_dir) | |
print '\naltering their entitlements\n\t{}\nto our entitlements\n\t{}'.\ | |
format(theirEntitlements, ourEntitlements) | |
cleanup() | |
if __name__ == '__main__': | |
main() | |
# |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment