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.

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

mercurial