From 6128f93d26f81157f8e4ba5dc6497d47b1e51105 Mon Sep 17 00:00:00 2001 From: Marcus Hoffmann Date: Fri, 14 Aug 2020 15:06:33 +0200 Subject: [PATCH 1/4] publish: keystore "NONE" is a special case and doesn't need to exist --- fdroidserver/publish.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fdroidserver/publish.py b/fdroidserver/publish.py index d69e3656..914afb1d 100644 --- a/fdroidserver/publish.py +++ b/fdroidserver/publish.py @@ -196,7 +196,7 @@ def main(): sys.exit(1) binaries_dir = os.path.join(unsigned_dir, 'binaries') - if not os.path.exists(config['keystore']): + if not config['keystore'] == "NONE" and not os.path.exists(config['keystore']): logging.error("Config error - missing '{0}'".format(config['keystore'])) sys.exit(1) From 066978cbcfc8bc388e8749ba5d1ef82a10cb576b Mon Sep 17 00:00:00 2001 From: Marcus Hoffmann Date: Sat, 11 Apr 2020 23:04:43 +0200 Subject: [PATCH 2/4] publish: use common signing method This is currently still jarsigner based but will at least use sha256 when possible --- fdroidserver/common.py | 2 +- fdroidserver/publish.py | 17 +++-------------- 2 files changed, 4 insertions(+), 15 deletions(-) diff --git a/fdroidserver/common.py b/fdroidserver/common.py index 1f3a483c..26bb7e82 100644 --- a/fdroidserver/common.py +++ b/fdroidserver/common.py @@ -3064,7 +3064,7 @@ def verify_apks(signed_apk, unsigned_apk, tmp_dir): One of the inputs is signed, the other is unsigned. The signature metadata is transferred from the signed to the unsigned apk, and then jarsigner is - used to verify that the signature from the signed apk is also varlid for + used to verify that the signature from the signed apk is also valid for the unsigned one. If the APK given as unsigned actually does have a signature, it will be stripped out and ignored. diff --git a/fdroidserver/publish.py b/fdroidserver/publish.py index 914afb1d..b1009dc7 100644 --- a/fdroidserver/publish.py +++ b/fdroidserver/publish.py @@ -319,7 +319,7 @@ def main(): # characters are significant, so we'll use the first 8 from # the MD5 of the app's ID and hope there are no collisions. # If a collision does occur later, we're going to have to - # come up with a new alogrithm, AND rename all existing keys + # come up with a new algorithm, AND rename all existing keys # in the keystore! if not skipsigning: if appid in config['keyaliases']: @@ -367,23 +367,12 @@ 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', - '-keypass:env', 'FDROID_KEY_PASS', '-sigalg', - 'SHA1withRSA', '-digestalg', 'SHA1', - apkfile, keyalias], envs=env_vars) - if p.returncode != 0: - raise BuildException(_("Failed to sign application"), p.output) + # Sign and zipalign the application... + common.sign_apk(apkfile, signed_apk_path, keyalias) if appid not in signed_apks: signed_apks[appid] = [] signed_apks[appid].append(apkfile) - # Zipalign it... - common._zipalign(apkfile, os.path.join(output_dir, apkfilename)) - os.remove(apkfile) - publish_source_tarball(apkfilename, unsigned_dir, output_dir) logging.info('Published ' + apkfilename) From 004d13a48a112c46e0f791aa93ddbbbac5346071 Mon Sep 17 00:00:00 2001 From: Marcus Hoffmann Date: Fri, 14 Aug 2020 15:44:34 +0200 Subject: [PATCH 3/4] make publish and update work with a smartcard HSM Followup to fdroid/fdroidserver!779. We need to add smartcardoptions to every call to keytool and jarsigner as well as handle when keypass not being required and not allowed for pkcs11 keystores. --- fdroidserver/common.py | 13 ++++++----- fdroidserver/publish.py | 46 +++++++++++++++++++++++---------------- fdroidserver/signindex.py | 2 +- 3 files changed, 36 insertions(+), 25 deletions(-) diff --git a/fdroidserver/common.py b/fdroidserver/common.py index 26bb7e82..be426d1b 100644 --- a/fdroidserver/common.py +++ b/fdroidserver/common.py @@ -3045,13 +3045,16 @@ def sign_apk(unsigned_path, signed_path, keyalias): else: signature_algorithm = ['-sigalg', 'SHA256withRSA', '-digestalg', 'SHA-256'] - p = FDroidPopen([config['jarsigner'], '-keystore', config['keystore'], - '-storepass:env', 'FDROID_KEY_STORE_PASS', - '-keypass:env', 'FDROID_KEY_PASS'] - + signature_algorithm + [unsigned_path, keyalias], + cmd = [config['jarsigner'], '-keystore', config['keystore'], + '-storepass:env', 'FDROID_KEY_STORE_PASS'] + if config['keystore'] == 'NONE': + cmd += config['smartcardoptions'] + else: + cmd += '-keypass:env', 'FDROID_KEY_PASS' + p = FDroidPopen(cmd + signature_algorithm + [unsigned_path, keyalias], envs={ 'FDROID_KEY_STORE_PASS': config['keystorepass'], - 'FDROID_KEY_PASS': config['keypass'], }) + 'FDROID_KEY_PASS': config.get('keypass', "")}) if p.returncode != 0: raise BuildException(_("Failed to sign application"), p.output) diff --git a/fdroidserver/publish.py b/fdroidserver/publish.py index b1009dc7..425cba43 100644 --- a/fdroidserver/publish.py +++ b/fdroidserver/publish.py @@ -77,12 +77,13 @@ def read_fingerprints_from_keystore(): are managed by F-Droid, grouped by appid. """ env_vars = {'LC_ALL': 'C.UTF-8', - 'FDROID_KEY_STORE_PASS': config['keystorepass'], - 'FDROID_KEY_PASS': config['keypass']} - p = FDroidPopen([config['keytool'], '-list', - '-v', '-keystore', config['keystore'], - '-storepass:env', 'FDROID_KEY_STORE_PASS'], - envs=env_vars, output=False) + 'FDROID_KEY_STORE_PASS': config['keystorepass']} + cmd = [config['keytool'], '-list', + '-v', '-keystore', config['keystore'], + '-storepass:env', 'FDROID_KEY_STORE_PASS'] + if config['keystore'] == 'NONE': + cmd += config['smartcardoptions'] + p = FDroidPopen(cmd, envs=env_vars, output=False) if p.returncode != 0: raise FDroidException('could not read keystore {}'.format(config['keystore'])) @@ -115,7 +116,7 @@ def sign_sig_key_fingerprint_list(jar_file): else: # smardcards never use -keypass cmd += '-keypass:env', 'FDROID_KEY_PASS' env_vars = {'FDROID_KEY_STORE_PASS': config['keystorepass'], - 'FDROID_KEY_PASS': config['keypass']} + 'FDROID_KEY_PASS': config.get('keypass', "")} p = common.FDroidPopen(cmd, envs=env_vars) if p.returncode != 0: raise FDroidException("Failed to sign '{}'!".format(jar_file)) @@ -340,20 +341,27 @@ def main(): # if not generate one... env_vars = {'LC_ALL': 'C.UTF-8', 'FDROID_KEY_STORE_PASS': config['keystorepass'], - 'FDROID_KEY_PASS': config['keypass']} - p = FDroidPopen([config['keytool'], '-list', - '-alias', keyalias, '-keystore', config['keystore'], - '-storepass:env', 'FDROID_KEY_STORE_PASS'], envs=env_vars) + '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...") - p = FDroidPopen([config['keytool'], '-genkey', - '-keystore', config['keystore'], - '-alias', keyalias, - '-keyalg', 'RSA', '-keysize', '2048', - '-validity', '10000', - '-storepass:env', 'FDROID_KEY_STORE_PASS', - '-keypass:env', 'FDROID_KEY_PASS', - '-dname', config['keydname']], envs=env_vars) + 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 appid not in generated_keys: diff --git a/fdroidserver/signindex.py b/fdroidserver/signindex.py index 1f02d0f9..693b127e 100644 --- a/fdroidserver/signindex.py +++ b/fdroidserver/signindex.py @@ -52,7 +52,7 @@ def sign_jar(jar): args += ['-keypass:env', 'FDROID_KEY_PASS'] env_vars = { 'FDROID_KEY_STORE_PASS': config['keystorepass'], - 'FDROID_KEY_PASS': config['keypass'], + 'FDROID_KEY_PASS': config.get('keypass', ""), } p = common.FDroidPopen(args, envs=env_vars) if p.returncode != 0: From a8e9653b961d4904f2a96055c0c11621401ab72f Mon Sep 17 00:00:00 2001 From: Marcus Hoffmann Date: Fri, 14 Aug 2020 17:27:08 +0200 Subject: [PATCH 4/4] update: make --create-key work with a HSM --- fdroidserver/common.py | 33 ++++++++++++++++++++------------- fdroidserver/init.py | 3 +++ fdroidserver/update.py | 2 +- 3 files changed, 24 insertions(+), 14 deletions(-) diff --git a/fdroidserver/common.py b/fdroidserver/common.py index be426d1b..86097a27 100644 --- a/fdroidserver/common.py +++ b/fdroidserver/common.py @@ -3345,26 +3345,33 @@ def genkeystore(localconfig): env_vars = {'LC_ALL': 'C.UTF-8', 'FDROID_KEY_STORE_PASS': localconfig['keystorepass'], - 'FDROID_KEY_PASS': localconfig['keypass']} - p = FDroidPopen([config['keytool'], '-genkey', - '-keystore', localconfig['keystore'], - '-alias', localconfig['repo_keyalias'], - '-keyalg', 'RSA', '-keysize', '4096', - '-sigalg', 'SHA256withRSA', - '-validity', '10000', - '-storepass:env', 'FDROID_KEY_STORE_PASS', - '-keypass:env', 'FDROID_KEY_PASS', - '-dname', localconfig['keydname'], - '-J-Duser.language=en'], envs=env_vars) + 'FDROID_KEY_PASS': localconfig.get('keypass', "")} + + cmd = [config['keytool'], '-genkey', + '-keystore', localconfig['keystore'], + '-alias', localconfig['repo_keyalias'], + '-keyalg', 'RSA', '-keysize', '4096', + '-sigalg', 'SHA256withRSA', + '-validity', '10000', + '-storepass:env', 'FDROID_KEY_STORE_PASS', + '-dname', localconfig['keydname'], + '-J-Duser.language=en'] + if localconfig['keystore'] == "NONE": + cmd += localconfig['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) - os.chmod(localconfig['keystore'], 0o0600) + if localconfig['keystore'] != "NONE": + os.chmod(localconfig['keystore'], 0o0600) if not options.quiet: # now show the lovely key that was just generated p = FDroidPopen([config['keytool'], '-list', '-v', '-keystore', localconfig['keystore'], '-alias', localconfig['repo_keyalias'], - '-storepass:env', 'FDROID_KEY_STORE_PASS', '-J-Duser.language=en'], envs=env_vars) + '-storepass:env', 'FDROID_KEY_STORE_PASS', '-J-Duser.language=en'] + + config['smartcardoptions'], envs=env_vars) logging.info(p.output.strip() + '\n\n') # get the public key p = FDroidPopenBytes([config['keytool'], '-exportcert', diff --git a/fdroidserver/init.py b/fdroidserver/init.py index c0c23207..ff2f7075 100644 --- a/fdroidserver/init.py +++ b/fdroidserver/init.py @@ -215,6 +215,9 @@ def main(): f.write('name = OpenSC\nlibrary = ') f.write(opensc_so) f.write('\n') + logging.info("Repo setup using a smartcard HSM. Please edit keystorepass and repo_keyalias in config.py.") + logging.info("If you want to generate a new repo signing key in the HSM you can do that with 'fdroid update " + "--create-key'.") elif os.path.exists(keystore): to_set = ['keystorepass', 'keypass', 'repo_keyalias', 'keydname'] if repo_keyalias: diff --git a/fdroidserver/update.py b/fdroidserver/update.py index babc3254..18ec1fe9 100644 --- a/fdroidserver/update.py +++ b/fdroidserver/update.py @@ -2323,7 +2323,7 @@ def main(): if 'keystorepass' not in config: config['keystorepass'] = password common.write_to_config(config, 'keystorepass', config['keystorepass']) - if 'keypass' not in config: + if 'keypass' not in config and not config['keystore'] == "NONE": config['keypass'] = password common.write_to_config(config, 'keypass', config['keypass']) common.genkeystore(config)