mobile/android/debug_sign_tool.py

Wed, 31 Dec 2014 07:22:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:22:50 +0100
branch
TOR_BUG_3246
changeset 4
fc2d59ddac77
permissions
-rwxr-xr-x

Correct previous dual key logic pending first delivery installment.

michael@0 1 #!/usr/bin/env python
michael@0 2
michael@0 3 # This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 # License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
michael@0 6
michael@0 7 """
michael@0 8 Sign Android packages using an Android debug keystore, creating the
michael@0 9 keystore if it does not exist.
michael@0 10
michael@0 11 This and |zip| can be combined to replace the Android |apkbuilder|
michael@0 12 tool, which was deprecated in SDK r22.
michael@0 13
michael@0 14 Exits with code 0 if creating the keystore and every signing succeeded,
michael@0 15 or with code 1 if any creation or signing failed.
michael@0 16 """
michael@0 17
michael@0 18 from argparse import ArgumentParser
michael@0 19 import errno
michael@0 20 import logging
michael@0 21 import os
michael@0 22 import subprocess
michael@0 23 import sys
michael@0 24
michael@0 25
michael@0 26 log = logging.getLogger(os.path.basename(__file__))
michael@0 27 log.setLevel(logging.INFO)
michael@0 28 sh = logging.StreamHandler(stream=sys.stdout)
michael@0 29 sh.setFormatter(logging.Formatter('%(name)s: %(message)s'))
michael@0 30 log.addHandler(sh)
michael@0 31
michael@0 32
michael@0 33 class DebugKeystore:
michael@0 34 """
michael@0 35 A thin abstraction on top of an Android debug key store.
michael@0 36 """
michael@0 37 def __init__(self, keystore):
michael@0 38 self._keystore = os.path.abspath(os.path.expanduser(keystore))
michael@0 39 self._alias = 'androiddebugkey'
michael@0 40 self.verbose = False
michael@0 41 self.keytool = 'keytool'
michael@0 42 self.jarsigner = 'jarsigner'
michael@0 43
michael@0 44 @property
michael@0 45 def keystore(self):
michael@0 46 return self._keystore
michael@0 47
michael@0 48 @property
michael@0 49 def alias(self):
michael@0 50 return self._alias
michael@0 51
michael@0 52 def _check(self, args):
michael@0 53 try:
michael@0 54 if self.verbose:
michael@0 55 subprocess.check_call(args)
michael@0 56 else:
michael@0 57 subprocess.check_output(args)
michael@0 58 except OSError as ex:
michael@0 59 if ex.errno != errno.ENOENT:
michael@0 60 raise
michael@0 61 raise Exception("Could not find executable '%s'" % args[0])
michael@0 62
michael@0 63 def keystore_contains_alias(self):
michael@0 64 args = [ self.keytool,
michael@0 65 '-list',
michael@0 66 '-keystore', self.keystore,
michael@0 67 '-storepass', 'android',
michael@0 68 '-alias', self.alias,
michael@0 69 ]
michael@0 70 if self.verbose:
michael@0 71 args.append('-v')
michael@0 72 contains = True
michael@0 73 try:
michael@0 74 self._check(args)
michael@0 75 except subprocess.CalledProcessError as e:
michael@0 76 contains = False
michael@0 77 if self.verbose:
michael@0 78 log.info('Keystore %s %s alias %s' %
michael@0 79 (self.keystore,
michael@0 80 'contains' if contains else 'does not contain',
michael@0 81 self.alias))
michael@0 82 return contains
michael@0 83
michael@0 84 def create_alias_in_keystore(self):
michael@0 85 try:
michael@0 86 path = os.path.dirname(self.keystore)
michael@0 87 os.makedirs(path)
michael@0 88 except OSError as exception:
michael@0 89 if exception.errno != errno.EEXIST:
michael@0 90 raise
michael@0 91
michael@0 92 args = [ self.keytool,
michael@0 93 '-genkeypair',
michael@0 94 '-keystore', self.keystore,
michael@0 95 '-storepass', 'android',
michael@0 96 '-alias', self.alias,
michael@0 97 '-keypass', 'android',
michael@0 98 '-dname', 'CN=Android Debug,O=Android,C=US',
michael@0 99 '-keyalg', 'RSA',
michael@0 100 '-validity', '365',
michael@0 101 ]
michael@0 102 if self.verbose:
michael@0 103 args.append('-v')
michael@0 104 self._check(args)
michael@0 105 if self.verbose:
michael@0 106 log.info('Created alias %s in keystore %s' %
michael@0 107 (self.alias, self.keystore))
michael@0 108
michael@0 109 def sign(self, apk):
michael@0 110 if not self.keystore_contains_alias():
michael@0 111 self.create_alias_in_keystore()
michael@0 112
michael@0 113 args = [ self.jarsigner,
michael@0 114 '-digestalg', 'SHA1',
michael@0 115 '-sigalg', 'MD5withRSA',
michael@0 116 '-keystore', self.keystore,
michael@0 117 '-storepass', 'android',
michael@0 118 apk,
michael@0 119 self.alias,
michael@0 120 ]
michael@0 121 if self.verbose:
michael@0 122 args.append('-verbose')
michael@0 123 self._check(args)
michael@0 124 if self.verbose:
michael@0 125 log.info('Signed %s with alias %s from keystore %s' %
michael@0 126 (apk, self.alias, self.keystore))
michael@0 127
michael@0 128
michael@0 129 def parse_args(argv):
michael@0 130 parser = ArgumentParser(description='Sign Android packages using an Android debug keystore.')
michael@0 131 parser.add_argument('apks', nargs='+',
michael@0 132 metavar='APK',
michael@0 133 help='Android packages to be signed')
michael@0 134 parser.add_argument('-v', '--verbose',
michael@0 135 dest='verbose',
michael@0 136 default=False,
michael@0 137 action='store_true',
michael@0 138 help='verbose output')
michael@0 139 parser.add_argument('--keytool',
michael@0 140 metavar='PATH',
michael@0 141 default='keytool',
michael@0 142 help='path to Java keytool')
michael@0 143 parser.add_argument('--jarsigner',
michael@0 144 metavar='PATH',
michael@0 145 default='jarsigner',
michael@0 146 help='path to Java jarsigner')
michael@0 147 parser.add_argument('--keystore',
michael@0 148 metavar='PATH',
michael@0 149 default='~/.android/debug.keystore',
michael@0 150 help='path to keystore (default: ~/.android/debug.keystore)')
michael@0 151 parser.add_argument('-f', '--force-create-keystore',
michael@0 152 dest='force',
michael@0 153 default=False,
michael@0 154 action='store_true',
michael@0 155 help='force creating keystore')
michael@0 156 return parser.parse_args(argv)
michael@0 157
michael@0 158
michael@0 159 def main():
michael@0 160 args = parse_args(sys.argv[1:])
michael@0 161
michael@0 162 keystore = DebugKeystore(args.keystore)
michael@0 163 keystore.verbose = args.verbose
michael@0 164 keystore.keytool = args.keytool
michael@0 165 keystore.jarsigner = args.jarsigner
michael@0 166
michael@0 167 if args.force:
michael@0 168 try:
michael@0 169 keystore.create_alias_in_keystore()
michael@0 170 except subprocess.CalledProcessError as e:
michael@0 171 log.error('Failed to force-create alias %s in keystore %s' %
michael@0 172 (keystore.alias, keystore.keystore))
michael@0 173 log.error(e)
michael@0 174 return 1
michael@0 175
michael@0 176 for apk in args.apks:
michael@0 177 try:
michael@0 178 keystore.sign(apk)
michael@0 179 except subprocess.CalledProcessError as e:
michael@0 180 log.error('Failed to sign %s', apk)
michael@0 181 log.error(e)
michael@0 182 return 1
michael@0 183
michael@0 184 return 0
michael@0 185
michael@0 186 if __name__ == '__main__':
michael@0 187 sys.exit(main())

mercurial