blob: b63292436fef3722dfa36639494e96e6629e3510 [file] [log] [blame]
Doug Zongkereef39442009-04-02 12:14:19 -07001#!/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
Doug Zongkerad88c7c2009-04-14 12:34:27 -070029 apkcerts.txt (so mappings specified by -k and -d are applied).
30 Keys specified in -e override any value for that app contained
31 in the apkcerts.txt file. Option may be repeated to give
32 multiple extra packages.
Doug Zongkereef39442009-04-02 12:14:19 -070033
34 -k (--key_mapping) <src_key=dest_key>
35 Add a mapping from the key name as specified in apkcerts.txt (the
36 src_key) to the real key you wish to sign the package with
37 (dest_key). Option may be repeated to give multiple key
38 mappings.
39
40 -d (--default_key_mappings) <dir>
41 Set up the following key mappings:
42
43 build/target/product/security/testkey ==> $dir/releasekey
44 build/target/product/security/media ==> $dir/media
45 build/target/product/security/shared ==> $dir/shared
46 build/target/product/security/platform ==> $dir/platform
47
48 -d and -k options are added to the set of mappings in the order
49 in which they appear on the command line.
50"""
51
52import sys
53
54if sys.hexversion < 0x02040000:
55 print >> sys.stderr, "Python 2.4 or newer is required."
56 sys.exit(1)
57
58import os
59import re
60import subprocess
61import tempfile
62import zipfile
63
64import common
65
66OPTIONS = common.OPTIONS
67
68OPTIONS.extra_apks = {}
69OPTIONS.key_map = {}
70
71
72def GetApkCerts(tf_zip):
Doug Zongkerad88c7c2009-04-14 12:34:27 -070073 certmap = {}
Doug Zongkereef39442009-04-02 12:14:19 -070074 for line in tf_zip.read("META/apkcerts.txt").split("\n"):
75 line = line.strip()
76 if not line: continue
77 m = re.match(r'^name="(.*)"\s+certificate="(.*)\.x509\.pem"\s+'
78 r'private_key="\2\.pk8"$', line)
79 if not m:
80 raise SigningError("failed to parse line from apkcerts.txt:\n" + line)
81 certmap[m.group(1)] = OPTIONS.key_map.get(m.group(2), m.group(2))
Doug Zongkerad88c7c2009-04-14 12:34:27 -070082 for apk, cert in OPTIONS.extra_apks.iteritems():
83 certmap[apk] = OPTIONS.key_map.get(cert, cert)
Doug Zongkereef39442009-04-02 12:14:19 -070084 return certmap
85
86
87def SignApk(data, keyname, pw):
88 unsigned = tempfile.NamedTemporaryFile()
89 unsigned.write(data)
90 unsigned.flush()
91
92 signed = tempfile.NamedTemporaryFile()
93
94 common.SignFile(unsigned.name, signed.name, keyname, pw, align=4)
95
96 data = signed.read()
97 unsigned.close()
98 signed.close()
99
100 return data
101
102
103def SignApks(input_tf_zip, output_tf_zip):
104 apk_key_map = GetApkCerts(input_tf_zip)
105
106 key_passwords = common.GetKeyPasswords(set(apk_key_map.values()))
107
108 maxsize = max([len(os.path.basename(i.filename))
109 for i in input_tf_zip.infolist()
110 if i.filename.endswith('.apk')])
111
112 for info in input_tf_zip.infolist():
113 data = input_tf_zip.read(info.filename)
114 if info.filename.endswith(".apk"):
115 name = os.path.basename(info.filename)
116 key = apk_key_map.get(name, None)
117 if key is not None:
118 print "signing: %-*s (%s)" % (maxsize, name, key)
119 signed_data = SignApk(data, key, key_passwords[key])
120 output_tf_zip.writestr(info, signed_data)
121 else:
122 # an APK we're not supposed to sign.
123 print "skipping: %s" % (name,)
124 output_tf_zip.writestr(info, data)
125 elif info.filename == "SYSTEM/build.prop":
126 # Change build fingerprint to reflect the fact that apps are signed.
127 m = re.search(r"ro\.build\.fingerprint=.*\b(test-keys)\b.*", data)
128 if not m:
129 print 'WARNING: ro.build.fingerprint does not contain "test-keys"'
130 else:
131 data = data[:m.start(1)] + "release-keys" + data[m.end(1):]
132 m = re.search(r"ro\.build\.description=.*\b(test-keys)\b.*", data)
133 if not m:
134 print 'WARNING: ro.build.description does not contain "test-keys"'
135 else:
136 data = data[:m.start(1)] + "release-keys" + data[m.end(1):]
137 output_tf_zip.writestr(info, data)
138 else:
139 # a non-APK file; copy it verbatim
140 output_tf_zip.writestr(info, data)
141
142
143def main(argv):
144
145 def option_handler(o, a):
146 if o in ("-s", "--signapk_jar"):
147 OPTIONS.signapk_jar = a
148 elif o in ("-e", "--extra_apks"):
149 names, key = a.split("=")
150 names = names.split(",")
151 for n in names:
152 OPTIONS.extra_apks[n] = key
153 elif o in ("-d", "--default_key_mappings"):
154 OPTIONS.key_map.update({
155 "build/target/product/security/testkey": "%s/releasekey" % (a,),
156 "build/target/product/security/media": "%s/media" % (a,),
157 "build/target/product/security/shared": "%s/shared" % (a,),
158 "build/target/product/security/platform": "%s/platform" % (a,),
159 })
160 elif o in ("-k", "--key_mapping"):
161 s, d = a.split("=")
162 OPTIONS.key_map[s] = d
163 else:
164 return False
165 return True
166
167 args = common.ParseOptions(argv, __doc__,
168 extra_opts="s:e:d:k:",
169 extra_long_opts=["signapk_jar=",
170 "extra_apks=",
171 "default_key_mappings=",
172 "key_mapping="],
173 extra_option_handler=option_handler)
174
175 if len(args) != 2:
176 common.Usage(__doc__)
177 sys.exit(1)
178
179 input_zip = zipfile.ZipFile(args[0], "r")
180 output_zip = zipfile.ZipFile(args[1], "w")
181
182 SignApks(input_zip, output_zip)
183
184 input_zip.close()
185 output_zip.close()
186
187 print "done."
188
189
190if __name__ == '__main__':
191 try:
192 main(sys.argv[1:])
193 except common.ExternalError, e:
194 print
195 print " ERROR: %s" % (e,)
196 print
197 sys.exit(1)