Created
January 20, 2022 06:13
-
-
Save kode54/44902da999955d2dd74a5a918f848d70 to your computer and use it in GitHub Desktop.
Recursively code sign on macOS. Useful for signing complex binaries outside of Xcode.
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
#!/usr/bin/env python2 | |
''' | |
Code-signs all nested binaries inside an app bundle (excluding the app itself). | |
''' | |
import os, sys, re | |
import subprocess as sp | |
CODE_SIGN_OPTS = ['--verbose', '--force', '--deep', '--options=runtime', '--sign'] | |
ROOT_SIGN_OPTS = ['--verbose', '--force', '--options=runtime', '--sign']; | |
def is_definitely_binary(path): | |
return 'Mach-O' in sp.check_output(['file', '--brief', path]) | |
def get_signing_path(path): | |
m = re.match('.*/(.*)\.framework/Versions/./(.*)', path) | |
if m and (m.lastindex==2) and m.group(1)==m.group(2): | |
#This is the main binary of a framework. Sign the framework version instead. | |
return [path, path[:-(len(m.group(1))+12)]] | |
m = re.match('.*/(.*)\.app/Contents/MacOS/(.*)', path) | |
if m and (m.lastindex==2) and m.group(1)==m.group(2): | |
# This is the main binary of the app bundle. Exclude it. | |
path = None | |
return path | |
def get_signable_binaries(path): | |
all_files = [os.path.join(root, fn) for root, dirs, names in os.walk(path) for fn in names] | |
bins = filter(is_definitely_binary, all_files) | |
return sorted(filter(None, map(get_signing_path, bins)), reverse=True) | |
def code_sign_nested(identity, path, dryrun): | |
signables = get_signable_binaries(path) | |
if len(signables)==0: | |
print "No signable binaries found." | |
return False | |
cmd = sp.check_output if not dryrun else lambda x: ' '.join(x) | |
try: | |
for bin in signables: | |
if type(bin) == list: | |
_tmp_path = bin[0]+'_' | |
print cmd(['rm', '-rf', os.path.join(bin[1], '_CodeSignature')]) | |
print cmd(['cp', bin[0], _tmp_path]) | |
print cmd(['rm', bin[0]]) | |
print cmd(['mv', _tmp_path, bin[0]]) | |
print cmd(['codesign', '--remove-signature', bin[0]]) | |
print cmd(['codesign']+CODE_SIGN_OPTS+[identity, bin[0]]) | |
print cmd(['codesign']+CODE_SIGN_OPTS+[identity, bin[1]]) | |
else: | |
print cmd(['codesign', '--remove-signature']+[bin]) | |
print cmd(['codesign']+CODE_SIGN_OPTS+[identity, bin]) | |
print cmd(['codesign']+ROOT_SIGN_OPTS+[identity, path]) | |
except sp.CalledProcessError: | |
print 'Code signing failed.' | |
exit(1) | |
print '%s successfully complete.'%('Code signing' if not dryrun else 'Dry run') | |
def main(): | |
if (len(sys.argv)!=4) or (sys.argv[1] not in ('sign', 'list')): | |
print 'Usage: %s sign/list signing_identity app_path'%os.path.basename(__file__) | |
exit(1) | |
cs_identity, app_path = sys.argv[2:] | |
code_sign_nested(cs_identity, app_path, dryrun=(sys.argv[1]=='list')) | |
if __name__=='__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment