publish: always use apksigner to sign APKs, closes #880

This commit is contained in:
Hans-Christoph Steiner 2021-03-19 18:25:34 +01:00
parent 08cde5c2e6
commit 50f0534d87
1 changed files with 38 additions and 73 deletions

View File

@ -3001,7 +3001,7 @@ def _zipalign(unsigned_apk, aligned_apk):
def apk_implant_signatures(apkpath, signaturefile, signedfile, manifest):
"""Implats a signature from metadata into an APK.
"""Implants a signature from metadata into an APK.
Note: this changes there supplied APK in place. So copy it if you
need the original to be preserved.
@ -3061,82 +3061,47 @@ def get_min_sdk_version(apk):
def sign_apk(unsigned_path, signed_path, keyalias):
"""Sign and zipalign an unsigned APK, then save to a new file, deleting the unsigned
Use apksigner for making v2 and v3 signature for apks with targetSDK >=30 as
otherwise they won't be installable on Android 11/R.
NONE is a Java keyword used to configure smartcards as the
keystore. Otherwise, the keystore is a local file.
https://docs.oracle.com/javase/7/docs/technotes/guides/security/p11guide.html#KeyToolJarSigner
Otherwise use jarsigner for v1 only signatures until we have apksig v2/v3
signature transplantig support.
When using jarsigner we need to manually select the hash algorithm,
apksigner does this automatically. Apksigner also does the zipalign for us.
SHA-256 support was added in android-18 (4.3), before then, the only options were MD5
and SHA1. This aims to use SHA-256 when the APK does not target
older Android versions, and is therefore safe to do so.
https://issuetracker.google.com/issues/36956587
https://android-review.googlesource.com/c/platform/libcore/+/44491
When using smartcards, apksigner does not use the same options has
Java/keytool/jarsigner (-providerName, -providerClass,
-providerArg, -storetype). apksigner documents the options as
--ks-provider-class and --ks-provider-arg. Those seem to be
accepted but fail when actually making a signature with weird
internal exceptions. We use the options that actually work. From:
https://geoffreymetais.github.io/code/key-signing/#scripting
"""
apk = _get_androguard_APK(unsigned_path)
if apk.get_effective_target_sdk_version() >= 30:
if config['keystore'] == 'NONE':
# NOTE: apksigner doesn't like -providerName/--provider-name at all, don't use that.
# apksigner documents the options as --ks-provider-class and --ks-provider-arg
# those seem to be accepted but fail when actually making a signature with
# weird internal exceptions. Those options actually work.
# From: https://geoffreymetais.github.io/code/key-signing/#scripting
apksigner_smartcardoptions = config['smartcardoptions'].copy()
if '-providerName' in apksigner_smartcardoptions:
pos = config['smartcardoptions'].index('-providerName')
# remove -providerName and it's argument
del apksigner_smartcardoptions[pos]
del apksigner_smartcardoptions[pos]
replacements = {'-storetype': '--ks-type',
'-providerClass': '--provider-class',
'-providerArg': '--provider-arg'}
signing_args = [replacements.get(n, n) for n in apksigner_smartcardoptions]
else:
signing_args = ['--key-pass', 'env:FDROID_KEY_PASS']
if not find_apksigner():
raise BuildException(_("apksigner not found, it's required for signing!"))
cmd = [find_apksigner(), 'sign',
'--ks', config['keystore'],
'--ks-pass', 'env:FDROID_KEY_STORE_PASS']
cmd += signing_args
cmd += ['--ks-key-alias', keyalias,
'--in', unsigned_path,
'--out', signed_path]
p = FDroidPopen(cmd, envs={
'FDROID_KEY_STORE_PASS': config['keystorepass'],
'FDROID_KEY_PASS': config.get('keypass', "")})
if p.returncode != 0:
raise BuildException(_("Failed to sign application"), p.output)
os.remove(unsigned_path)
if config['keystore'] == 'NONE':
apksigner_smartcardoptions = config['smartcardoptions'].copy()
if '-providerName' in apksigner_smartcardoptions:
pos = config['smartcardoptions'].index('-providerName')
# remove -providerName and it's argument
del apksigner_smartcardoptions[pos]
del apksigner_smartcardoptions[pos]
replacements = {'-storetype': '--ks-type',
'-providerClass': '--provider-class',
'-providerArg': '--provider-arg'}
signing_args = [replacements.get(n, n) for n in apksigner_smartcardoptions]
else:
if get_min_sdk_version(apk) < 18:
signature_algorithm = ['-sigalg', 'SHA1withRSA', '-digestalg', 'SHA1']
else:
signature_algorithm = ['-sigalg', 'SHA256withRSA', '-digestalg', 'SHA-256']
if config['keystore'] == 'NONE':
signing_args = config['smartcardoptions']
else:
signing_args = ['-keypass:env', 'FDROID_KEY_PASS']
cmd = [config['jarsigner'], '-keystore', config['keystore'],
'-storepass:env', 'FDROID_KEY_STORE_PASS']
cmd += signing_args
cmd += signature_algorithm
cmd += [unsigned_path, keyalias]
p = FDroidPopen(cmd, envs={
'FDROID_KEY_STORE_PASS': config['keystorepass'],
'FDROID_KEY_PASS': config.get('keypass', "")})
if p.returncode != 0:
raise BuildException(_("Failed to sign application"), p.output)
_zipalign(unsigned_path, signed_path)
os.remove(unsigned_path)
signing_args = ['--key-pass', 'env:FDROID_KEY_PASS']
if not find_apksigner():
raise BuildException(_("apksigner not found, it's required for signing!"))
cmd = [find_apksigner(), 'sign',
'--ks', config['keystore'],
'--ks-pass', 'env:FDROID_KEY_STORE_PASS']
cmd += signing_args
cmd += ['--ks-key-alias', keyalias,
'--in', unsigned_path,
'--out', signed_path]
p = FDroidPopen(cmd, envs={
'FDROID_KEY_STORE_PASS': config['keystorepass'],
'FDROID_KEY_PASS': config.get('keypass', "")})
if p.returncode != 0:
raise BuildException(_("Failed to sign application"), p.output)
os.remove(unsigned_path)
def verify_apks(signed_apk, unsigned_apk, tmp_dir):