Signing android releases with yubikey

3 minute read

At VideoLAN, we recently changed our signing procedure to leverage our security keys. As explained on Yubico website, Android signing is quite easy.

With this method, I can now sign the VLC releases on any of my computers without duplicating the keystore file. And keystore password is replaced by my Yubikey PIN.

Here is the precise process we went through to get this done.


Install dependencies

I am considering a Debian based distribution with open JDK 8 installed for this post.

First of all, we need to install the pkcs11 opensc lib and the zipalign tool

sudo apt-get install opensc-pkcs11 zipalign

zipalign can also be found in android_sdk_path/build-tools/version/

Prepare configuration file

Then, we prepare the pkcs11 configuration. Let’s create file pkcs11_java.cfg and fill it with:

name = OpenSC-PKCS11
description = SunPKCS11 via OpenSC
library = /usr/lib/x86_64-linux-gnu/
slotListIndex = 0

Yubico How-To advises slotListIndex = 1, but I had to set it to 0 to make it work with my Yubikey 4.

Let’s assume we save it in: ~/.pkcs11_java.cfg

Set up your own management key

If you did not set your management key, you have to do it now:

key=`dd if=/dev/random bs=1 count=24 2>/dev/null | hexdump -v -e '/1 "%02X"'`
echo $key
yubico-piv-tool -a set-mgm-key -n $key

Change your PIN

Same for PIN setting, default one is 123456

yubico-piv-tool -a change-pin -P 123456 -N <NEW PIN>

Keystore import

Now it’s time to import our keystore to the key PIV slot.

keytool -importkeystore -srckeystore mykeystore.keystore -destkeystore mykeystorey.p12 -srcstoretype jks -deststoretype pkcs12
yubico-piv-tool -s 9a -a import-key -a import-cert -i mykeystorey.p12 -K PKCS12 -k

You will be asked to type in the keystore password, then the certificate management key.

Starting from now, you won’t have to type the keystore password anymore but your Yubikey PIN.

We can check that our key is ready to sign apps:

keytool -providerClass -providerArg ~/.pkcs11_java.cfg -keystore NONE -storetype PKCS11 -list

This is the Yubikey PIN you have to type-in now.
And don’t forget to touch it if you enabled the ‘touch-to-sign’ option.

App signature

In build tools 24.0.3 Google has released apksigner, a new signature tool with convenient arguments like --min-sdk-version to get sure the application signature is correct.

Until release (26.0.1) apksigner doesn’t handle pkcs11 protocol correctly. So, you need to use build-tools 26.0.1+

We now have to get an unsigned apk, so we must tell gradle to not apply any signing config for release builds

 buildTypes {
        release {
            signingConfig null

Finally we can sign an apk without our keystore, we just need the Yubikey to be plugged and fire up apksigner

ANDROID_SDK_PATH/build-tools/BUILD_TOOLS_VERSION/apksigner sign --ks NONE --ks-pass "pass:$YUBI_PIN" \
--min-sdk-version 9 --provider-class \
--provider-arg pkcs11_java.cfg --ks-type PKCS11 app.apk

We can now verify the package is signed:

apksigner verify --verbose app.apk

verify accepts --min-sdk-version and --max-sdk-version to ensure your users won’t get 103 Play Store error code once the app is released.


Here is the full bash script I use to sign all my apks at once:

#! /bin/sh

echo "Please enter Yubikey PIN code "
stty -echo
trap 'stty echo' EXIT
read -p 'PIN: ' YUBI_PIN
stty echo
trap - EXIT


echo "\nSigning apks\n"
for i in `ls *.apk`;
$ANDROID_SDK/build-tools/$BT_VERSION/zipalign 4 $i $i.tmp && mv -vf $i.tmp $i
$ANDROID_SDK/build-tools/$BT_VERSION/apksigner sign --ks NONE \
        --ks-pass "pass:$YUBI_PIN" --min-sdk-version 9 \
        --max-sdk-version 26 --provider-class \
        --provider-arg ~/.pkcs11_java.cfg --ks-type PKCS11 $i
unset YUBI_PIN


We can also use jarsigner to sign your apk:

jarsigner -providerClass -providerArg ~/.pkcs11_java.cfg \
  -keystore NONE -storetype PKCS11 -sigalg SHA1withRSA -digestalg SHA1 \
  app.apk "Certificate for PIV Authentication"

The -sigalg SHA1withRSA -digestalg SHA1 parameters are needed because we support old devices. If you don’t support Android 4.2 and older you can rip it off.

With jarsigner, we need to zipalign the apk after signing them.

And verify the package is signed:

jarsigner -verify app.apk

Leave a comment