blob: a87d19809575bc4f7dae1206e1932bb75639202e [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
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)