sign_target_files_apks revision ad44c07ac7c505a7b6b5ef14c7234c58e3b919d0
1#!/usr/bin/env python 2# 3# Copyright (C) 2008 The Android Open Source Project 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16 17""" 18Signs all the APK files in a target-files zipfile, producing a new 19target-files zip. 20 21Usage: sign_target_files_apks [flags] input_target_files output_target_files 22 23 -s (--signapk_jar) <path> 24 Path of the signapks.jar file used to sign an individual APK 25 file. 26 27 -e (--extra_apks) <name,name,...=key> 28 Add extra APK name/key pairs as though they appeared in 29 apkcerts.zip. Option may be repeated to give multiple extra 30 packages. 31 32 -k (--key_mapping) <src_key=dest_key> 33 Add a mapping from the key name as specified in apkcerts.txt (the 34 src_key) to the real key you wish to sign the package with 35 (dest_key). Option may be repeated to give multiple key 36 mappings. 37 38 -d (--default_key_mappings) <dir> 39 Set up the following key mappings: 40 41 build/target/product/security/testkey ==> $dir/releasekey 42 build/target/product/security/media ==> $dir/media 43 build/target/product/security/shared ==> $dir/shared 44 build/target/product/security/platform ==> $dir/platform 45 46 -d and -k options are added to the set of mappings in the order 47 in which they appear on the command line. 48""" 49 50import sys 51 52if sys.hexversion < 0x02040000: 53 print >> sys.stderr, "Python 2.4 or newer is required." 54 sys.exit(1) 55 56import os 57import re 58import subprocess 59import tempfile 60import zipfile 61 62import common 63 64OPTIONS = common.OPTIONS 65 66OPTIONS.extra_apks = {} 67OPTIONS.key_map = {} 68 69 70def GetApkCerts(tf_zip): 71 certmap = OPTIONS.extra_apks.copy() 72 for line in tf_zip.read("META/apkcerts.txt").split("\n"): 73 line = line.strip() 74 if not line: continue 75 m = re.match(r'^name="(.*)"\s+certificate="(.*)\.x509\.pem"\s+' 76 r'private_key="\2\.pk8"$', line) 77 if not m: 78 raise SigningError("failed to parse line from apkcerts.txt:\n" + line) 79 certmap[m.group(1)] = OPTIONS.key_map.get(m.group(2), m.group(2)) 80 return certmap 81 82 83def SignApk(data, keyname, pw): 84 unsigned = tempfile.NamedTemporaryFile() 85 unsigned.write(data) 86 unsigned.flush() 87 88 signed = tempfile.NamedTemporaryFile() 89 90 common.SignFile(unsigned.name, signed.name, keyname, pw, align=4) 91 92 data = signed.read() 93 unsigned.close() 94 signed.close() 95 96 return data 97 98 99def SignApks(input_tf_zip, output_tf_zip): 100 apk_key_map = GetApkCerts(input_tf_zip) 101 102 key_passwords = common.GetKeyPasswords(set(apk_key_map.values())) 103 104 maxsize = max([len(os.path.basename(i.filename)) 105 for i in input_tf_zip.infolist() 106 if i.filename.endswith('.apk')]) 107 108 for info in input_tf_zip.infolist(): 109 data = input_tf_zip.read(info.filename) 110 if info.filename.endswith(".apk"): 111 name = os.path.basename(info.filename) 112 key = apk_key_map.get(name, None) 113 if key is not None: 114 print "signing: %-*s (%s)" % (maxsize, name, key) 115 signed_data = SignApk(data, key, key_passwords[key]) 116 output_tf_zip.writestr(info, signed_data) 117 else: 118 # an APK we're not supposed to sign. 119 print "skipping: %s" % (name,) 120 output_tf_zip.writestr(info, data) 121 elif info.filename == "SYSTEM/build.prop": 122 # Change build fingerprint to reflect the fact that apps are signed. 123 m = re.search(r"ro\.build\.fingerprint=.*\b(test-keys)\b.*", data) 124 if not m: 125 print 'WARNING: ro.build.fingerprint does not contain "test-keys"' 126 else: 127 data = data[:m.start(1)] + "release-keys" + data[m.end(1):] 128 m = re.search(r"ro\.build\.description=.*\b(test-keys)\b.*", data) 129 if not m: 130 print 'WARNING: ro.build.description does not contain "test-keys"' 131 else: 132 data = data[:m.start(1)] + "release-keys" + data[m.end(1):] 133 output_tf_zip.writestr(info, data) 134 else: 135 # a non-APK file; copy it verbatim 136 output_tf_zip.writestr(info, data) 137 138 139def main(argv): 140 141 def option_handler(o, a): 142 if o in ("-s", "--signapk_jar"): 143 OPTIONS.signapk_jar = a 144 elif o in ("-e", "--extra_apks"): 145 names, key = a.split("=") 146 names = names.split(",") 147 for n in names: 148 OPTIONS.extra_apks[n] = key 149 elif o in ("-d", "--default_key_mappings"): 150 OPTIONS.key_map.update({ 151 "build/target/product/security/testkey": "%s/releasekey" % (a,), 152 "build/target/product/security/media": "%s/media" % (a,), 153 "build/target/product/security/shared": "%s/shared" % (a,), 154 "build/target/product/security/platform": "%s/platform" % (a,), 155 }) 156 elif o in ("-k", "--key_mapping"): 157 s, d = a.split("=") 158 OPTIONS.key_map[s] = d 159 else: 160 return False 161 return True 162 163 args = common.ParseOptions(argv, __doc__, 164 extra_opts="s:e:d:k:", 165 extra_long_opts=["signapk_jar=", 166 "extra_apks=", 167 "default_key_mappings=", 168 "key_mapping="], 169 extra_option_handler=option_handler) 170 171 if len(args) != 2: 172 common.Usage(__doc__) 173 sys.exit(1) 174 175 input_zip = zipfile.ZipFile(args[0], "r") 176 output_zip = zipfile.ZipFile(args[1], "w") 177 178 SignApks(input_zip, output_zip) 179 180 input_zip.close() 181 output_zip.close() 182 183 print "done." 184 185 186if __name__ == '__main__': 187 try: 188 main(sys.argv[1:]) 189 except common.ExternalError, e: 190 print 191 print " ERROR: %s" % (e,) 192 print 193 sys.exit(1) 194