From 559e1bf2ce851e50a104a0fe34b9f5d9eed1e418 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Janiszewski?= Date: Thu, 31 Jul 2025 22:48:28 +0200 Subject: [PATCH] Use static keystore for signing Android builds Fixes #22861 GitHub Actions CI uses ephemeral debug keystore resulting in APKs being signed differently each time. This results in user not having trust in where the builds come from and Android rejecting the update due to mismatched keys. This commit introduces a script to (re)create a keystore to be used for signing APKs in GitHub Actions, sets environment variables for CI job with generated key and modifies gradle project to consume those variables. I have generated the keystore with aforementioned script and set secrects in the main repository with both the keystore password and keystore contents. --- .github/workflows/ci.yml | 5 +++ scripts/create-android-keystore | 48 +++++++++++++++++++++++++++ src/openrct2-android/app/build.gradle | 14 +++++++- 3 files changed, 66 insertions(+), 1 deletion(-) create mode 100755 scripts/create-android-keystore diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d8d906ad8d..926c350cac 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -613,6 +613,11 @@ jobs: uses: hendrikmuhs/ccache-action@v1.2.18 with: key: android + - name: Setup keystore + run: | + echo "${{ secrets.OPENRCT2_KEYSTORE_CONTENTS }}" | base64 -d > keystore.jks + echo "OPENRCT2_KEYSTORE_FILE=${{ github.workspace }}/keystore.jks" >> $GITHUB_ENV + echo "OPENRCT2_KEYSTORE_PASSWORD=${{ secrets.OPENRCT2_KEYSTORE_PASSWORD }}" >> $GITHUB_ENV - name: Install GCC problem matcher uses: ammaraskar/gcc-problem-matcher@master - name: Build OpenRCT2 diff --git a/scripts/create-android-keystore b/scripts/create-android-keystore new file mode 100755 index 0000000000..2bc47634a2 --- /dev/null +++ b/scripts/create-android-keystore @@ -0,0 +1,48 @@ +#!/bin/bash +# OpenRCT2 Android Keystore Creation Script +# This script creates a sample keystore for signing Android APKs + +set -e + +# Configuration - modify these values as needed +KEYSTORE_FILE="openrct2-release-key.keystore" +KEY_ALIAS="openrct2" +KEY_ALGORITHM="RSA" +KEY_SIZE="2048" +VALIDITY_DAYS="10950" # 30 years + +# Certificate details +CERT_DNAME="CN=OpenRCT2 Team, OU=Development, O=OpenRCT2 Team" + +if [ -z "$KEYSTORE_PASSWORD" ]; then + echo "Error: KEYSTORE_PASSWORD environment variable must be set" + echo "Usage: KEYSTORE_PASSWORD='your_secure_password' $0" + exit 1 +fi + +echo "Creating OpenRCT2 release keystore..." +echo "File: $KEYSTORE_FILE" +echo "Alias: $KEY_ALIAS" +echo "Algorithm: $KEY_ALGORITHM $KEY_SIZE" +echo "Validity: $VALIDITY_DAYS days" +echo "DN: $CERT_DNAME" + +# Create the keystore +keytool -genkeypair \ + -keystore "$KEYSTORE_FILE" \ + -alias "$KEY_ALIAS" \ + -keyalg "$KEY_ALGORITHM" \ + -keysize "$KEY_SIZE" \ + -validity "$VALIDITY_DAYS" \ + -dname "$CERT_DNAME" \ + -storetype PKCS12 \ + -storepass "$KEYSTORE_PASSWORD" \ + -keypass "$KEYSTORE_PASSWORD" \ + -noprompt + +echo "Keystore created successfully: $KEYSTORE_FILE" + +# Verify the keystore +echo "" +echo "Keystore information:" +keytool -list -v -keystore "$KEYSTORE_FILE" -storepass "$KEYSTORE_PASSWORD" diff --git a/src/openrct2-android/app/build.gradle b/src/openrct2-android/app/build.gradle index a541437327..eb5fa96ac2 100644 --- a/src/openrct2-android/app/build.gradle +++ b/src/openrct2-android/app/build.gradle @@ -4,6 +4,18 @@ android { compileSdk 36 ndkVersion "27.3.13750724" // Latest r27d (LTS), to be synced with CI container image namespace "io.openrct2" + + signingConfigs { + release { + if (System.getenv('OPENRCT2_KEYSTORE_FILE') && System.getenv('OPENRCT2_KEYSTORE_PASSWORD')) { + storeFile file(System.getenv('OPENRCT2_KEYSTORE_FILE')) + storePassword System.getenv('OPENRCT2_KEYSTORE_PASSWORD') + keyAlias "openrct2" + keyPassword System.getenv('OPENRCT2_KEYSTORE_PASSWORD') // Same as keystore password in PKCS12 + } + } + } + defaultConfig { applicationId 'io.openrct2' minSdkVersion 24 @@ -22,7 +34,7 @@ android { } buildTypes { release { - signingConfig signingConfigs.debug + signingConfig signingConfigs.release } }