Skip to content

Commit e3e9139

Browse files
committed
apksigner wrapper for Yubikey signatures
1 parent fd0b291 commit e3e9139

File tree

1 file changed

+108
-0
lines changed

1 file changed

+108
-0
lines changed

apksigner-pkcs11.sh

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
#!/bin/bash
2+
3+
# This is a simple wrapper script to sign an APK with a PKCS11 token using OpenSC (Yubikey or Nitrokey for example)
4+
# References:
5+
# - https://geoffreymetais.github.io/code/key-signing/
6+
# - https://medium.com/swlh/android-app-signing-with-a-yubikey-fdfc3d730965
7+
8+
# Environment variables that can be overridden:
9+
# - ANDROID_HOME -> the path to the Android SDK (by default $HOME/Library/Android/sdk or $HOME/Android/Sdk will be used)
10+
# - PKCS11_MODULE_PATH -> the path to the OpenSC PKCS11 module (by default brew and linux will be used)
11+
# - PKCS11_SLOT_LIST_INDEX -> the index of the slot list to use (by default 0 will be used but Yubico suggest using 1)
12+
# - MIN_SDK_VERSION -> the minimum SDK version to use (by default 30 will be used for Bugbane)
13+
# - EXTRA_FLAGS -> extra flags to pass to apksigner (by default empty)
14+
15+
check_and_set_default() {
16+
# Check if a variable is set and if not, set it to a default value
17+
local varname="$1"
18+
local default="$2"
19+
if [[ -z "${!varname+x}" || -z "${!varname}" ]]; then
20+
printf -v "$varname" "%s" "$default"
21+
fi
22+
}
23+
24+
most_recent_build_tools_version() {
25+
# Get the most recent folder in ANDROID_HOME/build-tools/
26+
if [[ -d "$ANDROID_HOME/build-tools/" ]]; then
27+
LATEST_DIR=$(ls -1d "$ANDROID_HOME"/build-tools/*/ 2>/dev/null | sort -r | head -n 1)
28+
if [[ -n "$LATEST_DIR" ]]; then
29+
export BUILD_TOOLS_VERSION=$(basename "$LATEST_DIR")
30+
if [[ "$BUILD_TOOLS_VERSION" > "35.0.0" ]]; then
31+
export EXTRA_FLAGS="--alignment-preserved"
32+
fi
33+
fi
34+
else
35+
echo "Error: ANDROID_HOME does not exist."
36+
exit 1
37+
fi
38+
}
39+
40+
# Bugbane minimum SDK version
41+
check_and_set_default MIN_SDK_VERSION 30
42+
# PKCS11 slot list index
43+
check_and_set_default PKCS11_SLOT_LIST_INDEX 0
44+
45+
if [[ "$OSTYPE" == "linux-gnu"* ]]; then
46+
check_and_set_default PKCS11_MODULE_PATH "/usr/lib64/opensc-pkcs11.so"
47+
check_and_set_default ANDROID_HOME "$HOME/Android/Sdk"
48+
most_recent_build_tools_version
49+
FLAGS="--add-exports jdk.crypto.cryptoki/sun.security.pkcs11=ALL-UNNNAMED --add-opens jdk.crypto.cryptoki/sun.security.pkcs11=ALL-UNNAMED"
50+
elif [[ "$OSTYPE" == "darwin"* ]]; then
51+
check_and_set_default PKCS11_MODULE_PATH "/opt/homebrew/lib/opensc-pkcs11.so"
52+
check_and_set_default ANDROID_HOME "$HOME/Library/Android/sdk"
53+
most_recent_build_tools_version
54+
FLAGS="--add-opens jdk.crypto.cryptoki/sun.security.pkcs11=ALL-UNNAMED --enable-native-access=ALL-UNNAMED"
55+
else
56+
echo "Error: Unsupported OS"
57+
exit 1
58+
fi
59+
60+
if [[ ! -f "$PKCS11_MODULE_PATH" ]]; then
61+
echo "Error: Failed to load PKCS11_MODULE_PATH - $PKCS11_MODULE_PATH does not exist."
62+
exit 1
63+
fi
64+
65+
# Create a temporary configuration file for the PKCS11 module
66+
TEMP_CFG_FILE=$(mktemp)
67+
cat <<EOF > "$TEMP_CFG_FILE"
68+
name = OpenSC-PKCS11
69+
description = SunPKCS11 via OpenSC
70+
library = "$PKCS11_MODULE_PATH"
71+
slotListIndex = $PKCS11_SLOT_LIST_INDEX
72+
EOF
73+
74+
# We dont check the APK path here, apksigner will do it for us
75+
APK_PATH="${1:-}"
76+
77+
java \
78+
$FLAGS \
79+
-jar "$ANDROID_HOME/build-tools/$BUILD_TOOLS_VERSION/lib/apksigner.jar" \
80+
sign \
81+
--ks NONE \
82+
--ks-type PKCS11 \
83+
--ks-pass "stdin" \
84+
--min-sdk-version $MIN_SDK_VERSION \
85+
--provider-class sun.security.pkcs11.SunPKCS11 \
86+
--provider-arg "$TEMP_CFG_FILE" \
87+
$EXTRA_FLAGS \
88+
"$APK_PATH"
89+
if [[ $? -ne 0 ]]; then
90+
echo "Error: Failed to sign APK"
91+
exit 1
92+
fi
93+
94+
java \
95+
$FLAGS \
96+
-jar "$ANDROID_HOME/build-tools/$BUILD_TOOLS_VERSION/lib/apksigner.jar" \
97+
verify \
98+
--verbose \
99+
--print-certs \
100+
--v4-signature-file "$APK_PATH.idsig" \
101+
"$APK_PATH"
102+
if [[ $? -ne 0 ]]; then
103+
echo "Error: Failed to verify APK"
104+
exit 1
105+
fi
106+
107+
rm "$TEMP_CFG_FILE"
108+
echo "APK signed and verified successfully"

0 commit comments

Comments
 (0)