mirror of
https://gitlab.com/fdroid/fdroidserver.git
synced 2024-11-19 21:30:10 +01:00
add common.sign_apk() for nighly as test for using in publish
Since the MD5 migration was quite a bit of work, it makes sense to start on moving away from SHA1 as much as possible while it is easy to do. SHA256 will only work in APK signatures on android-18 (4.3) or newer. So if an APK has a minSdkVersion of 18 or newer, then sign with SHA256. https://issuetracker.google.com/issues/36956587 https://android-review.googlesource.com/c/platform/libcore/+/44491
This commit is contained in:
parent
7da0854fa1
commit
1c3a4479ab
@ -1939,6 +1939,22 @@ def get_apk_id_aapt(apkfile):
|
||||
.format(apkfilename=apkfile))
|
||||
|
||||
|
||||
def get_minSdkVersion_aapt(apkfile):
|
||||
"""Extract the minimum supported Android SDK from an APK using aapt
|
||||
|
||||
:param apkfile: path to an APK file.
|
||||
:returns: the integer representing the SDK version
|
||||
"""
|
||||
r = re.compile(r"^sdkVersion:'([0-9]+)'")
|
||||
p = SdkToolsPopen(['aapt', 'dump', 'badging', apkfile], output=False)
|
||||
for line in p.output.splitlines():
|
||||
m = r.match(line)
|
||||
if m:
|
||||
return int(m.group(1))
|
||||
raise FDroidException(_('Reading minSdkVersion failed: "{apkfilename}"')
|
||||
.format(apkfilename=apkfile))
|
||||
|
||||
|
||||
class PopenResult:
|
||||
def __init__(self):
|
||||
self.returncode = None
|
||||
@ -2413,6 +2429,40 @@ def apk_extract_signatures(apkpath, outdir, manifest=True):
|
||||
out_file.write(in_apk.read(f.filename))
|
||||
|
||||
|
||||
def sign_apk(unsigned_path, signed_path, keyalias):
|
||||
"""Sign and zipalign an unsigned APK, then save to a new file, deleting the unsigned
|
||||
|
||||
android-18 (4.3) finally added support for reasonable hash
|
||||
algorithms, like SHA-256, 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
|
||||
|
||||
"""
|
||||
|
||||
if get_minSdkVersion_aapt(unsigned_path) < 18:
|
||||
signature_algorithm = ['-sigalg', 'SHA1withRSA', '-digestalg', 'SHA1']
|
||||
else:
|
||||
signature_algorithm = ['-sigalg', 'SHA256withRSA', '-digestalg', 'SHA256']
|
||||
|
||||
p = FDroidPopen([config['jarsigner'], '-keystore', config['keystore'],
|
||||
'-storepass:env', 'FDROID_KEY_STORE_PASS',
|
||||
'-keypass:env', 'FDROID_KEY_PASS']
|
||||
+ signature_algorithm + [unsigned_path, keyalias],
|
||||
envs={
|
||||
'FDROID_KEY_STORE_PASS': config['keystorepass'],
|
||||
'FDROID_KEY_PASS': config['keypass'], })
|
||||
if p.returncode != 0:
|
||||
raise BuildException(_("Failed to sign application"), p.output)
|
||||
|
||||
p = SdkToolsPopen(['zipalign', '-v', '4', unsigned_path, signed_path])
|
||||
if p.returncode != 0:
|
||||
raise BuildException(_("Failed to zipalign application"))
|
||||
os.remove(unsigned_path)
|
||||
|
||||
|
||||
def verify_apks(signed_apk, unsigned_apk, tmp_dir):
|
||||
"""Verify that two apks are the same
|
||||
|
||||
|
@ -339,6 +339,7 @@ def main():
|
||||
unsigned_dir,
|
||||
output_dir))
|
||||
|
||||
# TODO replace below with common.sign_apk() once it has proven stable
|
||||
# Sign the application...
|
||||
p = FDroidPopen([config['jarsigner'], '-keystore', config['keystore'],
|
||||
'-storepass:env', 'FDROID_KEY_STORE_PASS',
|
||||
|
@ -456,6 +456,29 @@ class CommonTest(unittest.TestCase):
|
||||
self.assertEqual(keytoolcertfingerprint,
|
||||
fdroidserver.common.apk_signer_fingerprint_short(apkfile))
|
||||
|
||||
def test_sign_apk(self):
|
||||
fdroidserver.common.config = None
|
||||
config = fdroidserver.common.read_config(fdroidserver.common.options)
|
||||
config['jarsigner'] = fdroidserver.common.find_sdk_tools_cmd('jarsigner')
|
||||
config['keyalias'] = 'sova'
|
||||
config['keystorepass'] = 'r9aquRHYoI8+dYz6jKrLntQ5/NJNASFBacJh7Jv2BlI='
|
||||
config['keypass'] = 'r9aquRHYoI8+dYz6jKrLntQ5/NJNASFBacJh7Jv2BlI='
|
||||
config['keystore'] = os.path.join(self.basedir, 'keystore.jks')
|
||||
fdroidserver.common.config = config
|
||||
fdroidserver.signindex.config = config
|
||||
|
||||
testdir = tempfile.mkdtemp(prefix=inspect.currentframe().f_code.co_name, dir=self.tmpdir)
|
||||
unsigned = os.path.join(testdir, 'urzip-release-unsigned.apk')
|
||||
signed = os.path.join(testdir, 'urzip-release.apk')
|
||||
|
||||
self.assertFalse(fdroidserver.common.verify_apk_signature(unsigned))
|
||||
|
||||
shutil.copy(os.path.join(self.basedir, 'urzip-release-unsigned.apk'), testdir)
|
||||
fdroidserver.common.sign_apk(unsigned, signed, config['keyalias'])
|
||||
self.assertTrue(os.path.isfile(signed))
|
||||
self.assertFalse(os.path.isfile(unsigned))
|
||||
self.assertTrue(fdroidserver.common.verify_apk_signature(signed))
|
||||
|
||||
def test_get_api_id_aapt(self):
|
||||
|
||||
config = dict()
|
||||
@ -472,6 +495,61 @@ class CommonTest(unittest.TestCase):
|
||||
with self.assertRaises(FDroidException):
|
||||
fdroidserver.common.get_apk_id_aapt('nope')
|
||||
|
||||
def test_get_minSdkVersion_aapt(self):
|
||||
|
||||
config = dict()
|
||||
fdroidserver.common.fill_config_defaults(config)
|
||||
fdroidserver.common.config = config
|
||||
self._set_build_tools()
|
||||
config['aapt'] = fdroidserver.common.find_sdk_tools_cmd('aapt')
|
||||
|
||||
minSdkVersion = fdroidserver.common.get_minSdkVersion_aapt('bad-unicode-πÇÇ现代通用字-български-عربي1.apk')
|
||||
self.assertEqual(4, minSdkVersion)
|
||||
minSdkVersion = fdroidserver.common.get_minSdkVersion_aapt('org.bitbucket.tickytacky.mirrormirror_1.apk')
|
||||
self.assertEqual(14, minSdkVersion)
|
||||
minSdkVersion = fdroidserver.common.get_minSdkVersion_aapt('org.bitbucket.tickytacky.mirrormirror_2.apk')
|
||||
self.assertEqual(14, minSdkVersion)
|
||||
minSdkVersion = fdroidserver.common.get_minSdkVersion_aapt('org.bitbucket.tickytacky.mirrormirror_3.apk')
|
||||
self.assertEqual(14, minSdkVersion)
|
||||
minSdkVersion = fdroidserver.common.get_minSdkVersion_aapt('org.bitbucket.tickytacky.mirrormirror_4.apk')
|
||||
self.assertEqual(14, minSdkVersion)
|
||||
minSdkVersion = fdroidserver.common.get_minSdkVersion_aapt('org.dyndns.fules.ck_20.apk')
|
||||
self.assertEqual(7, minSdkVersion)
|
||||
minSdkVersion = fdroidserver.common.get_minSdkVersion_aapt('urzip.apk')
|
||||
self.assertEqual(4, minSdkVersion)
|
||||
minSdkVersion = fdroidserver.common.get_minSdkVersion_aapt('urzip-badcert.apk')
|
||||
self.assertEqual(4, minSdkVersion)
|
||||
minSdkVersion = fdroidserver.common.get_minSdkVersion_aapt('urzip-badsig.apk')
|
||||
self.assertEqual(4, minSdkVersion)
|
||||
minSdkVersion = fdroidserver.common.get_minSdkVersion_aapt('urzip-release.apk')
|
||||
self.assertEqual(4, minSdkVersion)
|
||||
minSdkVersion = fdroidserver.common.get_minSdkVersion_aapt('urzip-release-unsigned.apk')
|
||||
self.assertEqual(4, minSdkVersion)
|
||||
minSdkVersion = fdroidserver.common.get_minSdkVersion_aapt('repo/com.politedroid_3.apk')
|
||||
self.assertEqual(3, minSdkVersion)
|
||||
minSdkVersion = fdroidserver.common.get_minSdkVersion_aapt('repo/com.politedroid_4.apk')
|
||||
self.assertEqual(3, minSdkVersion)
|
||||
minSdkVersion = fdroidserver.common.get_minSdkVersion_aapt('repo/com.politedroid_5.apk')
|
||||
self.assertEqual(3, minSdkVersion)
|
||||
minSdkVersion = fdroidserver.common.get_minSdkVersion_aapt('repo/com.politedroid_6.apk')
|
||||
self.assertEqual(14, minSdkVersion)
|
||||
minSdkVersion = fdroidserver.common.get_minSdkVersion_aapt('repo/obb.main.oldversion_1444412523.apk')
|
||||
self.assertEqual(4, minSdkVersion)
|
||||
minSdkVersion = fdroidserver.common.get_minSdkVersion_aapt('repo/obb.mainpatch.current_1619_another-release-key.apk')
|
||||
self.assertEqual(4, minSdkVersion)
|
||||
minSdkVersion = fdroidserver.common.get_minSdkVersion_aapt('repo/obb.mainpatch.current_1619.apk')
|
||||
self.assertEqual(4, minSdkVersion)
|
||||
minSdkVersion = fdroidserver.common.get_minSdkVersion_aapt('repo/obb.main.twoversions_1101613.apk')
|
||||
self.assertEqual(4, minSdkVersion)
|
||||
minSdkVersion = fdroidserver.common.get_minSdkVersion_aapt('repo/obb.main.twoversions_1101615.apk')
|
||||
self.assertEqual(4, minSdkVersion)
|
||||
minSdkVersion = fdroidserver.common.get_minSdkVersion_aapt('repo/obb.main.twoversions_1101617.apk')
|
||||
self.assertEqual(4, minSdkVersion)
|
||||
minSdkVersion = fdroidserver.common.get_minSdkVersion_aapt('repo/urzip-; Рахма́нинов, [rɐxˈmanʲɪnəf] سيرجي_رخمانينوف 谢尔盖·.apk')
|
||||
|
||||
with self.assertRaises(FDroidException):
|
||||
fdroidserver.common.get_minSdkVersion_aapt('nope')
|
||||
|
||||
def test_apk_release_name(self):
|
||||
appid, vercode, sigfp = fdroidserver.common.apk_parse_release_filename('com.serwylo.lexica_905.apk')
|
||||
self.assertEqual(appid, 'com.serwylo.lexica')
|
||||
|
Loading…
Reference in New Issue
Block a user