Skip to content

Instantly share code, notes, and snippets.

@jeanregisser
Created September 28, 2010 20:39
Show Gist options
  • Save jeanregisser/601735 to your computer and use it in GitHub Desktop.
Save jeanregisser/601735 to your computer and use it in GitHub Desktop.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright © 2010 Jean Regisser
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Inspired by http://github.com/appcelerator/titanium_mobile/blob/master/support/iphone/builder.py
#
usage = """usage: %prog [options] app
Re-sign an application using the provided provisioning profile and identity.
The provisioning profile should have a generic App ID (XXXXX.*)"""
import os, sys, optparse, subprocess, tempfile, shutil, codecs, plistlib, copy
from os.path import join, splitext, split, exists
def read_provisioning_profile(filename):
f = open(filename,'rb').read()
b = f.index('<?xml')
e = f.index('</plist>')
xml_content = f[b:e+8]
return plistlib.readPlistFromString(xml_content)
def get_application_identifier(provisioning_profile):
return provisioning_profile['Entitlements']['application-identifier']
def get_task_allow(provisioning_profile):
return provisioning_profile['Entitlements']['get-task-allow']
def get_appid_prefix(provisioning_profile):
return provisioning_profile['ApplicationIdentifierPrefix'][0]
def get_profile_uuid(provisioning_profile):
return provisioning_profile['UUID']
def generate_customized_entitlements(provisioning_profile, bundleid):
appid_prefix = get_appid_prefix(provisioning_profile)
entitlements_dict = copy.deepcopy(provisioning_profile['Entitlements'])
entitlements_dict['application-identifier'] = "%s.%s" % (appid_prefix, bundleid)
entitlements_dict['get-task-allow'] = get_task_allow(provisioning_profile)
entitlements_dict['keychain-access-groups'] = ["%s.%s" % (appid_prefix, bundleid)]
return plistlib.writePlistToString(entitlements_dict)
def generate_standard_resource_rules():
root = {'rules':{'.*':True, 'Info.plist':{'omit':True, 'weight':10.0}, 'ResourceRules.plist':{'omit':True, 'weight':100.0}}}
return plistlib.writePlistToString(root)
def parse_options():
parser = optparse.OptionParser(usage)
parser.set_defaults(provisioning_profile_name=None, signing_identity="iPhone Distribution", dryrun=False, verbose=False)
parser.add_option("-p", "--provisioning-profile-name", dest="provisioning_profile_name", type="str",
help = "The filename of the provisioning profile (without the path and extension).")
parser.add_option("-s", "--signing-identity", dest="signing_identity", type="str",
help = """Sign the app given using this identity. The default is "iPhone Distribution".""")
parser.add_option("-n", "--dryrun", dest="dryrun", action="store_true",
help = "During signing, performs almost all signing operations, but does not actually write the result anywhere.")
parser.add_option("-v", "--verbose", dest="verbose", action="store_true",
help = "Verbose reporting of activity.")
options, args = parser.parse_args()
if not options.provisioning_profile_name:
parser.error("provisioning profile name is missing")
if len(args) != 1:
parser.error("incorrect number of arguments")
return options, args
def main():
options, args = parse_options()
provisioning_profile_name = options.provisioning_profile_name
signing_identity = options.signing_identity
app = args[0]
infoplist = os.path.join(app,'Info.plist')
resource_rules = os.path.join(app,'ResourceRules.plist')
# convert and read Info.plist from the original app
plutil = subprocess.Popen("""/usr/bin/plutil -convert xml1 -o - "%s" """ % os.path.join(app, 'Info.plist'), shell=True, stdout=subprocess.PIPE)
plutil_output = plutil.communicate()[0]
p = plistlib.readPlistFromString(plutil_output)
bundleid = p["CFBundleIdentifier"]
print("* app bundle identifier: %s" % bundleid)
# read provisioning profile
provisioning_profile_filename = os.path.expanduser("~/Library/MobileDevice/Provisioning Profiles/%s.mobileprovision" % provisioning_profile_name)
print("* using new provisioning profile: %s" % provisioning_profile_filename)
provisioning_profile = read_provisioning_profile(provisioning_profile_filename)
print(" app id prefix: %s" % get_appid_prefix(provisioning_profile))
print(" application identifier: %s" % get_application_identifier(provisioning_profile))
# generate new entitlements for the new provisioning profile and existing bundleid
entitlements_contents = generate_customized_entitlements(provisioning_profile, bundleid)
if options.verbose:
print("* generated the following entitlements:\n%s" % entitlements_contents)
entitlements = tempfile.NamedTemporaryFile(suffix=".plist", delete=False)
entitlements.write(entitlements_contents)
entitlements.close()
# put the new embedded.mobileprovision
shutil.copyfile(provisioning_profile_filename, os.path.join(app, "embedded.mobileprovision"))
print("* embedded provisioning profile")
# Create ResourceRules.plist if it doesn't exist
resource_rules_filename = os.path.join(app, 'ResourceRules.plist')
if not os.path.exists(resource_rules_filename):
with open(resource_rules_filename, 'w+b') as f:
f.write(generate_standard_resource_rules())
print("* created %s" % resource_rules_filename)
# now codesign!
print("* now signing...")
os.environ["PATH"] = "/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin:" + os.environ["PATH"]
os.environ["_CODESIGN_ALLOCATE_"] = "/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/codesign_allocate"
os.system("""/usr/bin/codesign \
%(dryrun)s \
%(verbose)s \
-f -s "%(signing_identity)s" \
--preserve-metadata \
--resource-rules "%(resource_rules)s" \
--entitlements "%(entitlements)s" \
"%(app)s" """ %
{'dryrun': '--dryrun' if options.dryrun else '',
'verbose': '--verbose' if options.verbose else '',
'signing_identity': signing_identity,
'resource_rules': resource_rules,
'entitlements': entitlements.name,
'app': app})
# now cleanup
os.unlink(entitlements.name)
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment