Merge "Sign APKs using SHA-256 instead of SHA-1 when possible."
This commit is contained in:
commit
b763b29267
4 changed files with 155 additions and 8 deletions
|
@ -2213,11 +2213,21 @@ define add-carried-jack-resources
|
|||
fi
|
||||
endef
|
||||
|
||||
# Returns the minSdkVersion of the specified APK as a decimal number. If the
|
||||
# version is a codename, returns the current platform SDK version (always a
|
||||
# decimal number) instead.
|
||||
#
|
||||
define get-package-min-sdk-version-int
|
||||
$$($(AAPT) dump badging $(1) 2>&1 | grep '^sdkVersion' | cut -d"'" -f2 | \
|
||||
sed -e s/^$(PLATFORM_VERSION_CODENAME)$$/$(PLATFORM_SDK_VERSION)/)
|
||||
endef
|
||||
|
||||
# Sign a package using the specified key/cert.
|
||||
#
|
||||
define sign-package
|
||||
$(hide) mv $@ $@.unsigned
|
||||
$(hide) java -Djava.library.path=$(SIGNAPK_JNI_LIBRARY_PATH) -jar $(SIGNAPK_JAR) \
|
||||
--min-sdk-version $(call get-package-min-sdk-version-int,$@.unsigned) \
|
||||
$(PRIVATE_CERTIFICATE) $(PRIVATE_PRIVATE_KEY) \
|
||||
$(PRIVATE_ADDITIONAL_CERTIFICATES) $@.unsigned $@.signed
|
||||
$(hide) mv $@.signed $@
|
||||
|
|
|
@ -213,7 +213,7 @@ embedded_prebuilt_jni_libs := 'lib/*.so'
|
|||
endif
|
||||
$(built_module): PRIVATE_EMBEDDED_JNI_LIBS := $(embedded_prebuilt_jni_libs)
|
||||
|
||||
$(built_module) : $(my_prebuilt_src_file) | $(ACP) $(ZIPALIGN) $(SIGNAPK_JAR)
|
||||
$(built_module) : $(my_prebuilt_src_file) | $(ACP) $(ZIPALIGN) $(SIGNAPK_JAR) $(AAPT)
|
||||
$(transform-prebuilt-to-target)
|
||||
$(uncompress-shared-libs)
|
||||
ifneq ($(LOCAL_CERTIFICATE),PRESIGNED)
|
||||
|
@ -252,7 +252,7 @@ my_src_dir := $(LOCAL_PATH)/$(my_src_dir)
|
|||
|
||||
$(built_apk_splits) : PRIVATE_PRIVATE_KEY := $(LOCAL_CERTIFICATE).pk8
|
||||
$(built_apk_splits) : PRIVATE_CERTIFICATE := $(LOCAL_CERTIFICATE).x509.pem
|
||||
$(built_apk_splits) : $(built_module_path)/%.apk : $(my_src_dir)/%.apk | $(ACP)
|
||||
$(built_apk_splits) : $(built_module_path)/%.apk : $(my_src_dir)/%.apk | $(ACP) $(AAPT)
|
||||
$(copy-file-to-new-target)
|
||||
$(sign-package)
|
||||
|
||||
|
|
|
@ -592,7 +592,46 @@ def GetKeyPasswords(keylist):
|
|||
return key_passwords
|
||||
|
||||
|
||||
def SignFile(input_name, output_name, key, password, whole_file=False):
|
||||
def GetMinSdkVersion(apk_name):
|
||||
"""Get the minSdkVersion delared in the APK. This can be both a decimal number
|
||||
(API Level) or a codename.
|
||||
"""
|
||||
|
||||
p = Run(["aapt", "dump", "badging", apk_name], stdout=subprocess.PIPE)
|
||||
output, err = p.communicate()
|
||||
if err:
|
||||
raise ExternalError("Failed to obtain minSdkVersion: aapt return code %s"
|
||||
% (p.returncode,))
|
||||
|
||||
for line in output.split("\n"):
|
||||
# Looking for lines such as sdkVersion:'23' or sdkVersion:'M'
|
||||
m = re.match(r'sdkVersion:\'([^\']*)\'', line)
|
||||
if m:
|
||||
return m.group(1)
|
||||
raise ExternalError("No minSdkVersion returned by aapt")
|
||||
|
||||
|
||||
def GetMinSdkVersionInt(apk_name, codename_to_api_level_map):
|
||||
"""Get the minSdkVersion declared in the APK as a number (API Level). If
|
||||
minSdkVersion is set to a codename, it is translated to a number using the
|
||||
provided map.
|
||||
"""
|
||||
|
||||
version = GetMinSdkVersion(apk_name)
|
||||
try:
|
||||
return int(version)
|
||||
except ValueError:
|
||||
# Not a decimal number. Codename?
|
||||
if version in codename_to_api_level_map:
|
||||
return codename_to_api_level_map[version]
|
||||
else:
|
||||
raise ExternalError("Unknown minSdkVersion: '%s'. Known codenames: %s"
|
||||
% (version, codename_to_api_level_map))
|
||||
|
||||
|
||||
def SignFile(input_name, output_name, key, password, min_api_level=None,
|
||||
codename_to_api_level_map=dict(),
|
||||
whole_file=False):
|
||||
"""Sign the input_name zip/jar/apk, producing output_name. Use the
|
||||
given key and password (the latter may be None if the key does not
|
||||
have a password.
|
||||
|
@ -600,6 +639,13 @@ def SignFile(input_name, output_name, key, password, whole_file=False):
|
|||
If whole_file is true, use the "-w" option to SignApk to embed a
|
||||
signature that covers the whole file in the archive comment of the
|
||||
zip file.
|
||||
|
||||
min_api_level is the API Level (int) of the oldest platform this file may end
|
||||
up on. If not specified for an APK, the API Level is obtained by interpreting
|
||||
the minSdkVersion attribute of the APK's AndroidManifest.xml.
|
||||
|
||||
codename_to_api_level_map is needed to translate the codename which may be
|
||||
encountered as the APK's minSdkVersion.
|
||||
"""
|
||||
|
||||
java_library_path = os.path.join(
|
||||
|
@ -612,6 +658,15 @@ def SignFile(input_name, output_name, key, password, whole_file=False):
|
|||
cmd.extend(OPTIONS.extra_signapk_args)
|
||||
if whole_file:
|
||||
cmd.append("-w")
|
||||
|
||||
min_sdk_version = min_api_level
|
||||
if min_sdk_version is None:
|
||||
if not whole_file:
|
||||
min_sdk_version = GetMinSdkVersionInt(
|
||||
input_name, codename_to_api_level_map)
|
||||
if min_sdk_version is not None:
|
||||
cmd.extend(["--min-sdk-version", str(min_sdk_version)])
|
||||
|
||||
cmd.extend([key + OPTIONS.public_key_suffix,
|
||||
key + OPTIONS.private_key_suffix,
|
||||
input_name, output_name])
|
||||
|
|
|
@ -127,14 +127,34 @@ def CheckAllApksSigned(input_tf_zip, apk_key_map):
|
|||
sys.exit(1)
|
||||
|
||||
|
||||
def SignApk(data, keyname, pw):
|
||||
def SignApk(data, keyname, pw, platform_api_level, codename_to_api_level_map):
|
||||
unsigned = tempfile.NamedTemporaryFile()
|
||||
unsigned.write(data)
|
||||
unsigned.flush()
|
||||
|
||||
signed = tempfile.NamedTemporaryFile()
|
||||
|
||||
common.SignFile(unsigned.name, signed.name, keyname, pw)
|
||||
# For pre-N builds, don't upgrade to SHA-256 JAR signatures based on the APK's
|
||||
# minSdkVersion to avoid increasing incremental OTA update sizes. If an APK
|
||||
# didn't change, we don't want its signature to change due to the switch
|
||||
# from SHA-1 to SHA-256.
|
||||
# By default, APK signer chooses SHA-256 signatures if the APK's minSdkVersion
|
||||
# is 18 or higher. For pre-N builds we disable this mechanism by pretending
|
||||
# that the APK's minSdkVersion is 1.
|
||||
# For N+ builds, we let APK signer rely on the APK's minSdkVersion to
|
||||
# determine whether to use SHA-256.
|
||||
min_api_level = None
|
||||
if platform_api_level > 23:
|
||||
# Let APK signer choose whether to use SHA-1 or SHA-256, based on the APK's
|
||||
# minSdkVersion attribute
|
||||
min_api_level = None
|
||||
else:
|
||||
# Force APK signer to use SHA-1
|
||||
min_api_level = 1
|
||||
|
||||
common.SignFile(unsigned.name, signed.name, keyname, pw,
|
||||
min_api_level=min_api_level,
|
||||
codename_to_api_level_map=codename_to_api_level_map)
|
||||
|
||||
data = signed.read()
|
||||
unsigned.close()
|
||||
|
@ -144,7 +164,8 @@ def SignApk(data, keyname, pw):
|
|||
|
||||
|
||||
def ProcessTargetFiles(input_tf_zip, output_tf_zip, misc_info,
|
||||
apk_key_map, key_passwords):
|
||||
apk_key_map, key_passwords, platform_api_level,
|
||||
codename_to_api_level_map):
|
||||
|
||||
maxsize = max([len(os.path.basename(i.filename))
|
||||
for i in input_tf_zip.infolist()
|
||||
|
@ -200,7 +221,8 @@ def ProcessTargetFiles(input_tf_zip, output_tf_zip, misc_info,
|
|||
key = apk_key_map[name]
|
||||
if key not in common.SPECIAL_CERT_STRINGS:
|
||||
print " signing: %-*s (%s)" % (maxsize, name, key)
|
||||
signed_data = SignApk(data, key, key_passwords[key])
|
||||
signed_data = SignApk(data, key, key_passwords[key], platform_api_level,
|
||||
codename_to_api_level_map)
|
||||
common.ZipWriteStr(output_tf_zip, out_info, signed_data)
|
||||
else:
|
||||
# an APK we're not supposed to sign.
|
||||
|
@ -440,6 +462,57 @@ def BuildKeyMap(misc_info, key_mapping_options):
|
|||
OPTIONS.key_map[s] = d
|
||||
|
||||
|
||||
def GetApiLevelAndCodename(input_tf_zip):
|
||||
data = input_tf_zip.read("SYSTEM/build.prop")
|
||||
api_level = None
|
||||
codename = None
|
||||
for line in data.split("\n"):
|
||||
line = line.strip()
|
||||
original_line = line
|
||||
if line and line[0] != '#' and "=" in line:
|
||||
key, value = line.split("=", 1)
|
||||
key = key.strip()
|
||||
if key == "ro.build.version.sdk":
|
||||
api_level = int(value.strip())
|
||||
elif key == "ro.build.version.codename":
|
||||
codename = value.strip()
|
||||
|
||||
if api_level is None:
|
||||
raise ValueError("No ro.build.version.sdk in SYSTEM/build.prop")
|
||||
if codename is None:
|
||||
raise ValueError("No ro.build.version.codename in SYSTEM/build.prop")
|
||||
|
||||
return (api_level, codename)
|
||||
|
||||
|
||||
def GetCodenameToApiLevelMap(input_tf_zip):
|
||||
data = input_tf_zip.read("SYSTEM/build.prop")
|
||||
api_level = None
|
||||
codenames = None
|
||||
for line in data.split("\n"):
|
||||
line = line.strip()
|
||||
original_line = line
|
||||
if line and line[0] != '#' and "=" in line:
|
||||
key, value = line.split("=", 1)
|
||||
key = key.strip()
|
||||
if key == "ro.build.version.sdk":
|
||||
api_level = int(value.strip())
|
||||
elif key == "ro.build.version.all_codenames":
|
||||
codenames = value.strip().split(",")
|
||||
|
||||
if api_level is None:
|
||||
raise ValueError("No ro.build.version.sdk in SYSTEM/build.prop")
|
||||
if codenames is None:
|
||||
raise ValueError("No ro.build.version.all_codenames in SYSTEM/build.prop")
|
||||
|
||||
result = dict()
|
||||
for codename in codenames:
|
||||
codename = codename.strip()
|
||||
if len(codename) > 0:
|
||||
result[codename] = api_level
|
||||
return result
|
||||
|
||||
|
||||
def main(argv):
|
||||
|
||||
key_mapping_options = []
|
||||
|
@ -498,8 +571,17 @@ def main(argv):
|
|||
CheckAllApksSigned(input_zip, apk_key_map)
|
||||
|
||||
key_passwords = common.GetKeyPasswords(set(apk_key_map.values()))
|
||||
platform_api_level, platform_codename = GetApiLevelAndCodename(input_zip)
|
||||
codename_to_api_level_map = GetCodenameToApiLevelMap(input_zip)
|
||||
# Android N will be API Level 24, but isn't yet.
|
||||
# TODO: Remove this workaround once Android N is officially API Level 24.
|
||||
if platform_api_level == 23 and platform_codename == "N":
|
||||
platform_api_level = 24
|
||||
|
||||
ProcessTargetFiles(input_zip, output_zip, misc_info,
|
||||
apk_key_map, key_passwords)
|
||||
apk_key_map, key_passwords,
|
||||
platform_api_level,
|
||||
codename_to_api_level_map)
|
||||
|
||||
common.ZipClose(input_zip)
|
||||
common.ZipClose(output_zip)
|
||||
|
|
Loading…
Reference in a new issue