1
0
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:
Ciaran Gultnieks 2014-04-08 08:26:37 +00:00
commit e8c47765ae
7 changed files with 149 additions and 45 deletions

View File

@ -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

View File

@ -0,0 +1,4 @@
name = OpenSC
description = SunPKCS11 w/ OpenSC Smart card Framework
library = /usr/lib/opensc-pkcs11.so
slotListIndex = 1

View File

@ -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"]):

View File

@ -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

View File

@ -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")

View File

@ -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'])
],

View File

@ -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