mirror of
https://gitlab.com/fdroid/fdroidserver.git
synced 2024-09-17 10:40:12 +02:00
Merge branch 'master' into 'master'
implemeted support for using a smartcard for the repo signing key This changeset implements using a smartcard (HSM) as the keystore for the signing key. It also fixes lots of little bugs in the `fdroid init` process.
This commit is contained in:
commit
e8c47765ae
@ -54,13 +54,24 @@ of applications from the main repository.
|
||||
"""
|
||||
|
||||
|
||||
#The key (from the keystore defined below) to be used for signing the
|
||||
#repository itself. Can be None for an unsigned repository.
|
||||
repo_keyalias = None
|
||||
# The key (from the keystore defined below) to be used for signing the
|
||||
# repository itself. This is the same name you would give to keytool or
|
||||
# jarsigner using -alias. (Not needed in an unsigned repository).
|
||||
#repo_keyalias = "fdroidrepo"
|
||||
|
||||
#The keystore to use for release keys when building. This needs to be
|
||||
#somewhere safe and secure, and backed up!
|
||||
#keystore = "/home/me/.local/share/fdroidserver/keystore.jks"
|
||||
# The keystore to use for release keys when building. This needs to be
|
||||
# somewhere safe and secure, and backed up! The best way to manage these
|
||||
# sensitive keys is to use a "smartcard" (aka Hardware Security Module). To
|
||||
# configure FDroid to use a smartcard, set the keystore file using the keyword
|
||||
# "NONE" (i.e. keystore = "NONE"). That makes Java find the keystore on the
|
||||
# smartcard based on 'smartcardoptions' below.
|
||||
#keystore = "~/.local/share/fdroidserver/keystore.jks"
|
||||
|
||||
# You should not need to change these at all, unless you have a very
|
||||
# customized setup for using smartcards in Java with keytool/jarsigner
|
||||
#smartcardoptions = "-storetype PKCS11 -providerName SunPKCS11-OpenSC \
|
||||
# -providerClass sun.security.pkcs11.SunPKCS11 \
|
||||
# -providerArg opensc-fdroid.cfg"
|
||||
|
||||
# The password for the keystore (at least 6 characters). If this password is
|
||||
# different than the keypass below, it can be OK to store the password in this
|
||||
|
4
examples/opensc-fdroid.cfg
Normal file
4
examples/opensc-fdroid.cfg
Normal file
@ -0,0 +1,4 @@
|
||||
name = OpenSC
|
||||
description = SunPKCS11 w/ OpenSC Smart card Framework
|
||||
library = /usr/lib/opensc-pkcs11.so
|
||||
slotListIndex = 1
|
@ -54,6 +54,16 @@ def read_config(opts, config_file='config.py'):
|
||||
logging.debug("Reading %s" % config_file)
|
||||
execfile(config_file, config)
|
||||
|
||||
# smartcardoptions must be a list since its command line args for Popen
|
||||
if 'smartcardoptions' in config:
|
||||
config['smartcardoptions'] = config['smartcardoptions'].split(' ')
|
||||
elif 'keystore' in config and config['keystore'] == 'NONE':
|
||||
# keystore='NONE' means use smartcard, these are required defaults
|
||||
config['smartcardoptions'] = ['-storetype', 'PKCS11', '-providerName',
|
||||
'SunPKCS11-OpenSC', '-providerClass',
|
||||
'sun.security.pkcs11.SunPKCS11',
|
||||
'-providerArg', 'opensc-fdroid.cfg']
|
||||
|
||||
defconfig = {
|
||||
'sdk_path': "$ANDROID_HOME",
|
||||
'ndk_path': "$ANDROID_NDK",
|
||||
@ -66,8 +76,8 @@ def read_config(opts, config_file='config.py'):
|
||||
'stats_to_carbon': False,
|
||||
'repo_maxage': 0,
|
||||
'build_server_always': False,
|
||||
'keystore': os.path.join(os.getenv('HOME'),
|
||||
'.local', 'share', 'fdroidserver', 'keystore.jks'),
|
||||
'keystore': '$HOME/.local/share/fdroidserver/keystore.jks',
|
||||
'smartcardoptions': [],
|
||||
'char_limits': {
|
||||
'Summary' : 50,
|
||||
'Description' : 1500
|
||||
@ -86,10 +96,13 @@ def read_config(opts, config_file='config.py'):
|
||||
config[k] = os.path.expandvars(v)
|
||||
|
||||
if not config['sdk_path']:
|
||||
logging.critical("$ANDROID_HOME is not set!")
|
||||
logging.critical("Neither $ANDROID_HOME nor sdk_path is set, no Android SDK found!")
|
||||
sys.exit(3)
|
||||
if not os.path.exists(config['sdk_path']):
|
||||
logging.critical('Android SDK path "' + config['sdk_path'] + '" does not exist!')
|
||||
sys.exit(3)
|
||||
if not os.path.isdir(config['sdk_path']):
|
||||
logging.critical("$ANDROID_HOME points to a non-existing directory!")
|
||||
logging.critical('Android SDK path "' + config['sdk_path'] + '" is not a directory!')
|
||||
sys.exit(3)
|
||||
|
||||
if any(k in config for k in ["keystore", "keystorepass", "keypass"]):
|
||||
|
@ -19,6 +19,7 @@
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import glob
|
||||
import hashlib
|
||||
import os
|
||||
import re
|
||||
@ -38,8 +39,18 @@ def write_to_config(key, value):
|
||||
'''write a key/value to the local config.py'''
|
||||
with open('config.py', 'r') as f:
|
||||
data = f.read()
|
||||
pattern = key + '\s*=.*'
|
||||
repl = key + ' = "' + value + '"'
|
||||
pattern = '\n[\s#]*' + key + '\s*=\s*"[^"]*"'
|
||||
repl = '\n' + key + ' = "' + value + '"'
|
||||
data = re.sub(pattern, repl, data)
|
||||
with open('config.py', 'w') as f:
|
||||
f.writelines(data)
|
||||
|
||||
def disable_in_config(key, value):
|
||||
'''write a key/value to the local config.py, then comment it out'''
|
||||
with open('config.py', 'r') as f:
|
||||
data = f.read()
|
||||
pattern = '\n[\s#]*' + key + '\s*=\s*"[^"]*"'
|
||||
repl = '\n#' + key + ' = "' + value + '"'
|
||||
data = re.sub(pattern, repl, data)
|
||||
with open('config.py', 'w') as f:
|
||||
f.writelines(data)
|
||||
@ -71,8 +82,8 @@ def genkey(keystore, repo_keyalias, password, keydname):
|
||||
raise BuildException("Failed to generate key", p.stdout)
|
||||
# now show the lovely key that was just generated
|
||||
p = FDroidPopen(['keytool', '-list', '-v',
|
||||
'-keystore', keystore, '-alias', repo_keyalias],
|
||||
'-storepass:file', config['keystorepassfile'])
|
||||
'-keystore', keystore, '-alias', repo_keyalias,
|
||||
'-storepass:file', config['keystorepassfile']])
|
||||
logging.info(p.stdout.strip() + '\n\n')
|
||||
|
||||
|
||||
@ -175,33 +186,63 @@ def main():
|
||||
# find or generate the keystore for the repo signing key. First try the
|
||||
# path written in the default config.py. Then check if the user has
|
||||
# specified a path from the command line, which will trump all others.
|
||||
# Otherwise, create ~/.local/share/fdroidserver and stick it in there.
|
||||
# Otherwise, create ~/.local/share/fdroidserver and stick it in there. If
|
||||
# keystore is set to NONE, that means that Java will look for keys in a
|
||||
# Hardware Security Module aka Smartcard.
|
||||
keystore = config['keystore']
|
||||
if options.keystore:
|
||||
if os.path.isfile(options.keystore):
|
||||
keystore = os.path.abspath(options.keystore)
|
||||
if options.keystore == 'NONE':
|
||||
keystore = options.keystore
|
||||
write_to_config('keystore', keystore)
|
||||
else:
|
||||
logging.info('"' + options.keystore + '" does not exist or is not a file!')
|
||||
sys.exit(1)
|
||||
keystore = os.path.abspath(options.keystore)
|
||||
if not os.path.exists(keystore):
|
||||
logging.info('"' + keystore
|
||||
+ '" does not exist, creating a new keystore there.')
|
||||
write_to_config('keystore', keystore)
|
||||
repo_keyalias = None
|
||||
if options.repo_keyalias:
|
||||
repo_keyalias = options.repo_keyalias
|
||||
write_to_config('repo_keyalias', repo_keyalias)
|
||||
if options.distinguished_name:
|
||||
keydname = options.distinguished_name
|
||||
write_to_config('keydname', keydname)
|
||||
if not os.path.isfile(keystore):
|
||||
if keystore == 'NONE': # we're using a smartcard
|
||||
write_to_config('repo_keyalias', '1') # seems to be the default
|
||||
disable_in_config('keypass', 'never used with smartcard')
|
||||
write_to_config('smartcardoptions',
|
||||
('-storetype PKCS11 -providerName SunPKCS11-OpenSC '
|
||||
+ '-providerClass sun.security.pkcs11.SunPKCS11 '
|
||||
+ '-providerArg opensc-fdroid.cfg'))
|
||||
# find opensc-pkcs11.so
|
||||
if not os.path.exists('opensc-fdroid.cfg'):
|
||||
if os.path.exists('/usr/lib/opensc-pkcs11.so'):
|
||||
opensc_so = '/usr/lib/opensc-pkcs11.so'
|
||||
elif os.path.exists('/usr/lib64/opensc-pkcs11.so'):
|
||||
opensc_so = '/usr/lib64/opensc-pkcs11.so'
|
||||
else:
|
||||
files = glob.glob('/usr/lib/' + os.uname()[4] + '-*-gnu/opensc-pkcs11.so')
|
||||
if len(files) > 0:
|
||||
opensc_so = files[0]
|
||||
else:
|
||||
opensc_so = '/usr/lib/opensc-pkcs11.so'
|
||||
logging.warn('No OpenSC PKCS#11 module found, ' +
|
||||
'install OpenSC then edit "opensc-fdroid.cfg"!')
|
||||
with open(os.path.join(examplesdir, 'opensc-fdroid.cfg'), 'r') as f:
|
||||
opensc_fdroid = f.read()
|
||||
opensc_fdroid = re.sub('^library.*', 'library = ' + opensc_so, opensc_fdroid,
|
||||
flags=re.MULTILINE)
|
||||
with open('opensc-fdroid.cfg', 'w') as f:
|
||||
f.write(opensc_fdroid)
|
||||
elif not os.path.exists(keystore):
|
||||
# no existing or specified keystore, generate the whole thing
|
||||
keystoredir = os.path.join(os.getenv('HOME'),
|
||||
'.local', 'share', 'fdroidserver')
|
||||
keystoredir = os.path.dirname(keystore)
|
||||
if not os.path.exists(keystoredir):
|
||||
os.makedirs(keystoredir, mode=0o700)
|
||||
keystore = os.path.join(keystoredir, 'keystore.jks')
|
||||
write_to_config('keystore', keystore)
|
||||
password = genpassword()
|
||||
write_to_config('keystorepass', password)
|
||||
write_to_config('keypass', password)
|
||||
if not options.repo_keyalias:
|
||||
if options.repo_keyalias == None:
|
||||
repo_keyalias = socket.getfqdn()
|
||||
write_to_config('repo_keyalias', repo_keyalias)
|
||||
if not options.distinguished_name:
|
||||
@ -215,12 +256,14 @@ def main():
|
||||
logging.info(' Android SDK Build Tools:\t' + os.path.dirname(aapt))
|
||||
logging.info(' Android NDK (optional):\t' + ndk_path)
|
||||
logging.info(' Keystore for signing key:\t' + keystore)
|
||||
if repo_keyalias != None:
|
||||
logging.info(' Alias for key in store:\t' + repo_keyalias)
|
||||
logging.info('\nTo complete the setup, add your APKs to "' +
|
||||
os.path.join(fdroiddir, 'repo') + '"' +
|
||||
'''
|
||||
then run "fdroid update -c; fdroid update". You might also want to edit
|
||||
"config.py" to set the URL, repo name, and more. You should also set up
|
||||
a signing key.
|
||||
a signing key (a temporary one might have been automatically generated).
|
||||
|
||||
For more info: https://f-droid.org/manual/fdroid.html#Simple-Binary-Repository
|
||||
and https://f-droid.org/manual/fdroid.html#Signing
|
||||
|
@ -627,7 +627,7 @@ def make_index(apps, apks, repodir, archive, categories):
|
||||
repoel.setAttribute("version", "12")
|
||||
repoel.setAttribute("timestamp", str(int(time.time())))
|
||||
|
||||
if config['repo_keyalias']:
|
||||
if 'repo_keyalias' in config:
|
||||
|
||||
# Generate a certificate fingerprint the same way keytool does it
|
||||
# (but with slightly different formatting)
|
||||
@ -642,7 +642,8 @@ def make_index(apps, apks, repodir, archive, categories):
|
||||
p = FDroidPopen(['keytool', '-exportcert',
|
||||
'-alias', config['repo_keyalias'],
|
||||
'-keystore', config['keystore'],
|
||||
'-storepass:file', config['keystorepassfile']])
|
||||
'-storepass:file', config['keystorepassfile']]
|
||||
+ config['smartcardoptions'])
|
||||
if p.returncode != 0:
|
||||
logging.critical("Failed to get repo pubkey")
|
||||
sys.exit(1)
|
||||
@ -783,7 +784,7 @@ def make_index(apps, apks, repodir, archive, categories):
|
||||
of.write(output)
|
||||
of.close()
|
||||
|
||||
if config['repo_keyalias'] is not None:
|
||||
if 'repo_keyalias' in config:
|
||||
|
||||
logging.info("Creating signed index.")
|
||||
logging.info("Key fingerprint: %s" % repo_pubkey_fingerprint)
|
||||
@ -795,11 +796,15 @@ def make_index(apps, apks, repodir, archive, categories):
|
||||
sys.exit(1)
|
||||
|
||||
# Sign the index...
|
||||
p = FDroidPopen(['jarsigner', '-keystore', config['keystore'],
|
||||
'-storepass:file', config['keystorepassfile'],
|
||||
'-keypass:file', config['keypassfile'],
|
||||
'-digestalg', 'SHA1', '-sigalg', 'MD5withRSA',
|
||||
os.path.join(repodir, 'index.jar') , config['repo_keyalias']])
|
||||
args = ['jarsigner', '-keystore', config['keystore'],
|
||||
'-storepass:file', config['keystorepassfile'],
|
||||
'-digestalg', 'SHA1', '-sigalg', 'MD5withRSA',
|
||||
os.path.join(repodir, 'index.jar'), config['repo_keyalias']]
|
||||
if config['keystore'] == 'NONE':
|
||||
args += config['smartcardoptions']
|
||||
else: # smardcards never use -keypass
|
||||
args += ['-keypass:file', config['keypassfile']]
|
||||
p = FDroidPopen(args)
|
||||
# TODO keypass should be sent via stdin
|
||||
if p.returncode != 0:
|
||||
logging.info("Failed to sign index")
|
||||
|
1
setup.py
1
setup.py
@ -23,6 +23,7 @@ setup(name='fdroidserver',
|
||||
[ 'buildserver/config.buildserver.py',
|
||||
'examples/config.py',
|
||||
'examples/makebs.config.py',
|
||||
'examples/opensc-fdroid.cfg',
|
||||
'examples/fdroid-icon.png']),
|
||||
('fdroidserver/getsig', ['fdroidserver/getsig/getsig.class'])
|
||||
],
|
||||
|
@ -3,6 +3,18 @@
|
||||
set -e
|
||||
set -x
|
||||
|
||||
copy_apks_into_repo() {
|
||||
for f in `ls -1 ../../*/bin/*.apk`; do
|
||||
name=$(basename $(dirname `dirname $f`))
|
||||
echo "name $name"
|
||||
apk=${name}_`basename $f`
|
||||
echo "apk $apk"
|
||||
cp $f $1/repo/$apk
|
||||
done
|
||||
# delete any 'unaligned' duplicates
|
||||
rm -f $1/repo/*unaligned*.apk
|
||||
}
|
||||
|
||||
if [ -z $WORKSPACE ]; then
|
||||
WORKSPACE=`dirname $(pwd)`
|
||||
echo "Setting Workspace to $WORKSPACE"
|
||||
@ -19,16 +31,31 @@ fi
|
||||
REPOROOT=`mktemp --directory --tmpdir=$WORKSPACE`
|
||||
cd $REPOROOT
|
||||
$fdroid init
|
||||
for f in `ls -1 ../../*/bin/*.apk`; do
|
||||
name=$(basename $(dirname `dirname $f`))
|
||||
echo "name $name"
|
||||
apk=${name}_`basename $f`
|
||||
echo "apk $apk"
|
||||
cp $f $REPOROOT/repo/$apk
|
||||
done
|
||||
# delete any 'unaligned' duplicates
|
||||
rm -f $REPOROOT/repo/*unaligned*.apk
|
||||
|
||||
|
||||
copy_apks_into_repo $REPOROOT
|
||||
$fdroid update -c
|
||||
$fdroid update
|
||||
|
||||
|
||||
#------------------------------------------------------------------------------#
|
||||
# setup a new repo from scratch and generate a keystore
|
||||
|
||||
REPOROOT=`mktemp --directory --tmpdir=$WORKSPACE`
|
||||
KEYSTORE=$REPOROOT/keystore.jks
|
||||
cd $REPOROOT
|
||||
$fdroid init --keystore $KEYSTORE
|
||||
test -e $KEYSTORE
|
||||
copy_apks_into_repo $REPOROOT
|
||||
$fdroid update -c
|
||||
$fdroid update
|
||||
test -e repo/index.xml
|
||||
test -e repo/index.jar
|
||||
|
||||
|
||||
#------------------------------------------------------------------------------#
|
||||
# setup a new repo from scratch with a HSM/smartcard
|
||||
|
||||
REPOROOT=`mktemp --directory --tmpdir=$WORKSPACE`
|
||||
cd $REPOROOT
|
||||
$fdroid init --keystore NONE
|
||||
test -e opensc-fdroid.cfg
|
||||
test ! -e NONE
|
||||
|
Loading…
Reference in New Issue
Block a user