From a114c73c2d4f7f0117407e9fca43a2d5b221dd70 Mon Sep 17 00:00:00 2001 From: Marcus Hoffmann Date: Mon, 24 Aug 2020 19:29:57 +0200 Subject: [PATCH] publish: factor out the signing key creation into a method --- fdroidserver/publish.py | 65 ++++++++++++++++++++++++----------------- tests/publish.TestCase | 27 +++++++++++++++++ 2 files changed, 65 insertions(+), 27 deletions(-) diff --git a/fdroidserver/publish.py b/fdroidserver/publish.py index 8a6a3e81..50492fe2 100644 --- a/fdroidserver/publish.py +++ b/fdroidserver/publish.py @@ -180,6 +180,43 @@ def check_for_key_collisions(allapps): return allaliases +def create_key_if_not_existing(keyalias): + """ + Ensures a signing key with the given keyalias exists + :return: boolean, True if a new key was created, false otherwise + """ + # See if we already have a key for this application, and + # if not generate one... + env_vars = {'LC_ALL': 'C.UTF-8', + 'FDROID_KEY_STORE_PASS': config['keystorepass'], + 'FDROID_KEY_PASS': config.get('keypass', "")} + cmd = [config['keytool'], '-list', + '-alias', keyalias, '-keystore', config['keystore'], + '-storepass:env', 'FDROID_KEY_STORE_PASS'] + if config['keystore'] == 'NONE': + cmd += config['smartcardoptions'] + p = FDroidPopen(cmd, envs=env_vars) + if p.returncode != 0: + logging.info("Key does not exist - generating...") + cmd = [config['keytool'], '-genkey', + '-keystore', config['keystore'], + '-alias', keyalias, + '-keyalg', 'RSA', '-keysize', '2048', + '-validity', '10000', + '-storepass:env', 'FDROID_KEY_STORE_PASS', + '-dname', config['keydname']] + if config['keystore'] == 'NONE': + cmd += config['smartcardoptions'] + else: + cmd += '-keypass:env', 'FDROID_KEY_PASS' + p = FDroidPopen(cmd, envs=env_vars) + if p.returncode != 0: + raise BuildException("Failed to generate key", p.output) + return True + else: + return False + + def main(): global config, options @@ -326,33 +363,7 @@ def main(): keyalias = key_alias(appid) logging.info("Key alias: " + keyalias) - # See if we already have a key for this application, and - # if not generate one... - env_vars = {'LC_ALL': 'C.UTF-8', - 'FDROID_KEY_STORE_PASS': config['keystorepass'], - 'FDROID_KEY_PASS': config.get('keypass', "")} - cmd = [config['keytool'], '-list', - '-alias', keyalias, '-keystore', config['keystore'], - '-storepass:env', 'FDROID_KEY_STORE_PASS'] - if config['keystore'] == 'NONE': - cmd += config['smartcardoptions'] - p = FDroidPopen(cmd, envs=env_vars) - if p.returncode != 0: - logging.info("Key does not exist - generating...") - cmd = [config['keytool'], '-genkey', - '-keystore', config['keystore'], - '-alias', keyalias, - '-keyalg', 'RSA', '-keysize', '2048', - '-validity', '10000', - '-storepass:env', 'FDROID_KEY_STORE_PASS', - '-dname', config['keydname']] - if config['keystore'] == 'NONE': - cmd += config['smartcardoptions'] - else: - cmd += '-keypass:env', 'FDROID_KEY_PASS' - p = FDroidPopen(cmd, envs=env_vars) - if p.returncode != 0: - raise BuildException("Failed to generate key", p.output) + if create_key_if_not_existing(keyalias): generated_keys[appid] = keyalias signed_apk_path = os.path.join(output_dir, apkfilename) diff --git a/tests/publish.TestCase b/tests/publish.TestCase index 41863482..acd2996f 100755 --- a/tests/publish.TestCase +++ b/tests/publish.TestCase @@ -11,6 +11,7 @@ # import inspect +import jks, jks.util import logging import optparse import os @@ -190,6 +191,32 @@ class PublishTest(unittest.TestCase): allaliases = publish.check_for_key_collisions(allapps) self.assertEqual(len(randomappids), len(allaliases)) + def test_create_key_if_not_existing(self): + common.config = {} + common.fill_config_defaults(common.config) + publish.config = common.config + publish.config['keystorepass'] = '123456' + publish.config['keypass'] = '654321' + publish.config['keystore'] = "keystore.jks" + publish.config['keydname'] = 'CN=Birdman, OU=Cell, O=Alcatraz, L=Alcatraz, S=California, C=US' + testdir = tempfile.mkdtemp(prefix=inspect.currentframe().f_code.co_name, dir=self.tmpdir) + os.chdir(testdir) + keystore = jks.KeyStore.new("jks", []) + keystore.save(publish.config['keystore'], publish.config['keystorepass']) + + self.assertTrue(publish.create_key_if_not_existing("newalias")) + # The second time we try that, a new key should not be created + self.assertFalse(publish.create_key_if_not_existing("newalias")) + self.assertTrue(publish.create_key_if_not_existing("anotheralias")) + + keystore = jks.KeyStore.load(publish.config['keystore'], publish.config['keystorepass']) + self.assertCountEqual(keystore.private_keys, ["newalias", "anotheralias"]) + for alias, pk in keystore.private_keys.items(): + self.assertFalse(pk.is_decrypted()) + pk.decrypt(publish.config['keypass']) + self.assertTrue(pk.is_decrypted()) + self.assertEqual(jks.util.RSA_ENCRYPTION_OID, pk.algorithm_oid) + if __name__ == "__main__": os.chdir(os.path.dirname(__file__))