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