From 50f0534d876bb42b23f6bce8dffe4295bbcd2d3a Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Fri, 19 Mar 2021 18:25:34 +0100 Subject: [PATCH] publish: always use apksigner to sign APKs, closes #880 --- fdroidserver/common.py | 111 ++++++++++++++--------------------------- 1 file changed, 38 insertions(+), 73 deletions(-) diff --git a/fdroidserver/common.py b/fdroidserver/common.py index 9184b2c4..70eebcb5 100644 --- a/fdroidserver/common.py +++ b/fdroidserver/common.py @@ -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):