summaryrefslogtreecommitdiffstats
path: root/mobile/android/debug_sign_tool.py
diff options
context:
space:
mode:
Diffstat (limited to 'mobile/android/debug_sign_tool.py')
-rwxr-xr-xmobile/android/debug_sign_tool.py187
1 files changed, 187 insertions, 0 deletions
diff --git a/mobile/android/debug_sign_tool.py b/mobile/android/debug_sign_tool.py
new file mode 100755
index 000000000..2d3e2099f
--- /dev/null
+++ b/mobile/android/debug_sign_tool.py
@@ -0,0 +1,187 @@
+#!/usr/bin/env python
+
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+"""
+Sign Android packages using an Android debug keystore, creating the
+keystore if it does not exist.
+
+This and |zip| can be combined to replace the Android |apkbuilder|
+tool, which was deprecated in SDK r22.
+
+Exits with code 0 if creating the keystore and every signing succeeded,
+or with code 1 if any creation or signing failed.
+"""
+
+from argparse import ArgumentParser
+import errno
+import logging
+import os
+import subprocess
+import sys
+
+
+log = logging.getLogger(os.path.basename(__file__))
+log.setLevel(logging.INFO)
+sh = logging.StreamHandler(stream=sys.stdout)
+sh.setFormatter(logging.Formatter('%(name)s: %(message)s'))
+log.addHandler(sh)
+
+
+class DebugKeystore:
+ """
+ A thin abstraction on top of an Android debug key store.
+ """
+ def __init__(self, keystore):
+ self._keystore = os.path.abspath(os.path.expanduser(keystore))
+ self._alias = 'androiddebugkey'
+ self.verbose = False
+ self.keytool = 'keytool'
+ self.jarsigner = 'jarsigner'
+
+ @property
+ def keystore(self):
+ return self._keystore
+
+ @property
+ def alias(self):
+ return self._alias
+
+ def _check(self, args):
+ try:
+ if self.verbose:
+ subprocess.check_call(args)
+ else:
+ subprocess.check_output(args)
+ except OSError as ex:
+ if ex.errno != errno.ENOENT:
+ raise
+ raise Exception("Could not find executable '%s'" % args[0])
+
+ def keystore_contains_alias(self):
+ args = [ self.keytool,
+ '-list',
+ '-keystore', self.keystore,
+ '-storepass', 'android',
+ '-alias', self.alias,
+ ]
+ if self.verbose:
+ args.append('-v')
+ contains = True
+ try:
+ self._check(args)
+ except subprocess.CalledProcessError as e:
+ contains = False
+ if self.verbose:
+ log.info('Keystore %s %s alias %s' %
+ (self.keystore,
+ 'contains' if contains else 'does not contain',
+ self.alias))
+ return contains
+
+ def create_alias_in_keystore(self):
+ try:
+ path = os.path.dirname(self.keystore)
+ os.makedirs(path)
+ except OSError as exception:
+ if exception.errno != errno.EEXIST:
+ raise
+
+ args = [ self.keytool,
+ '-genkeypair',
+ '-keystore', self.keystore,
+ '-storepass', 'android',
+ '-alias', self.alias,
+ '-keypass', 'android',
+ '-dname', 'CN=Android Debug,O=Android,C=US',
+ '-keyalg', 'RSA',
+ '-validity', '365',
+ ]
+ if self.verbose:
+ args.append('-v')
+ self._check(args)
+ if self.verbose:
+ log.info('Created alias %s in keystore %s' %
+ (self.alias, self.keystore))
+
+ def sign(self, apk):
+ if not self.keystore_contains_alias():
+ self.create_alias_in_keystore()
+
+ args = [ self.jarsigner,
+ '-digestalg', 'SHA1',
+ '-sigalg', 'MD5withRSA',
+ '-keystore', self.keystore,
+ '-storepass', 'android',
+ apk,
+ self.alias,
+ ]
+ if self.verbose:
+ args.append('-verbose')
+ self._check(args)
+ if self.verbose:
+ log.info('Signed %s with alias %s from keystore %s' %
+ (apk, self.alias, self.keystore))
+
+
+def parse_args(argv):
+ parser = ArgumentParser(description='Sign Android packages using an Android debug keystore.')
+ parser.add_argument('apks', nargs='+',
+ metavar='APK',
+ help='Android packages to be signed')
+ parser.add_argument('-v', '--verbose',
+ dest='verbose',
+ default=False,
+ action='store_true',
+ help='verbose output')
+ parser.add_argument('--keytool',
+ metavar='PATH',
+ default='keytool',
+ help='path to Java keytool')
+ parser.add_argument('--jarsigner',
+ metavar='PATH',
+ default='jarsigner',
+ help='path to Java jarsigner')
+ parser.add_argument('--keystore',
+ metavar='PATH',
+ default='~/.android/debug.keystore',
+ help='path to keystore (default: ~/.android/debug.keystore)')
+ parser.add_argument('-f', '--force-create-keystore',
+ dest='force',
+ default=False,
+ action='store_true',
+ help='force creating keystore')
+ return parser.parse_args(argv)
+
+
+def main():
+ args = parse_args(sys.argv[1:])
+
+ keystore = DebugKeystore(args.keystore)
+ keystore.verbose = args.verbose
+ keystore.keytool = args.keytool
+ keystore.jarsigner = args.jarsigner
+
+ if args.force:
+ try:
+ keystore.create_alias_in_keystore()
+ except subprocess.CalledProcessError as e:
+ log.error('Failed to force-create alias %s in keystore %s' %
+ (keystore.alias, keystore.keystore))
+ log.error(e)
+ return 1
+
+ for apk in args.apks:
+ try:
+ keystore.sign(apk)
+ except subprocess.CalledProcessError as e:
+ log.error('Failed to sign %s', apk)
+ log.error(e)
+ return 1
+
+ return 0
+
+if __name__ == '__main__':
+ sys.exit(main())