SAGIO KMS APK Signing Handoff¶
Context¶
The SAGIO Android terminal (apps/terminal, package io.sagio.merchant) was
failing on a Motorola test device with payment failed unlock fail.
The API-side FASSTAP credential path was checked first:
- Local database contains
merchant_worldline_onboardingandmerchant_worldline_terminals. - Prod merchant app auth succeeds for merchant
d365d71d-170d-4ad0-ba5e-ed549b33dc17. - Prod
/api/merchant-app/fasspay-configreturns all required fields.
That moved the active blocker from API config to Android signing / Soft Space Play Integrity allowlisting.
Signing Findings¶
The Soft Space bundle contained public signing material for SAGIO:
| Artifact | Meaning |
|---|---|
sagio_app_final.cert |
Public X.509 app-signing certificate Soft Space whitelisted |
sagio_pk.pem |
Public key matching the certificate |
sagio_application_id.txt |
Application ID: io.sagio.merchant |
*.asc files |
PGP exchange artifacts, not Android private keys |
Soft Space whitelisted certificate SHA-256:
The local debug APK from ./gradlew assembleDebug was signed with:
So a debug install does not match the Soft Space whitelist.
The 62:B4... certificate public key matches:
apps/terminal/signing/sagio-app-signer.public.pem- Google Cloud KMS key version:
projects/sagio-484122/locations/australia-southeast1/keyRings/sagio-production-ring/cryptoKeys/sagio-app-signer/cryptoKeyVersions/1
The private key is HSM-backed and cannot be exported. It can only be used by
calling Cloud KMS asymmetricSign.
Implemented Local KMS Signer¶
The custom signer lets us produce a direct-install APK signed with the
Soft Space-whitelisted 62:B4... certificate without exporting private key
material.
Files:
apps/terminal/tools/kms-apk-signer/src/main/java/io/sagio/tools/kmsapksigner/KmsApkSigner.javaapps/terminal/tools/kms-apk-signer/src/main/resources/META-INF/services/com.android.apksig.kms.KmsSignerEngineProviderapps/terminal/scripts/sign-release-with-kms.sh
Local-only file:
sagio_app_final.cert, placed atapps/terminal/signing/sagio_app_final.cert,~/wl/sagio_app_final.cert, or any path pointed to bySAGIO_APP_SIGNING_CERT
The certificate file is intentionally ignored by git.
How it works:
- Builds/uses an unsigned release APK.
- Uses Android
apksigwith a custom KMSSignerEngine. - Sends SHA-256 digests to Google Cloud KMS
asymmetricSign. - Embeds
sagio_app_final.certin the APK signing block. - Verifies the output APK with
apksigner.
Prerequisites¶
Install Google Cloud CLI:
Authenticate and select the project:
The signing principal needs one of these roles on the KMS key:
Cloud KMS CryptoKey SignerCloud KMS CryptoKey Signer/Verifier
In the current setup, nzsagio@gmail.com has Cloud KMS CryptoKey
Signer/Verifier.
Place the public app-signing certificate from the Soft Space exchange at one of these local paths:
mkdir -p apps/terminal/signing
cp /path/to/sagio_app_final.cert apps/terminal/signing/sagio_app_final.cert
or:
Build And Sign¶
From the repo:
Default input:
Default output:
Absolute output path from this workstation:
/Users/lulzx/work/sagio/apps/terminal/app/build/outputs/apk/release/app-arm64-v8a-release-kms-signed.apk
Successful signing output includes:
Signed APK: .../app-arm64-v8a-release-kms-signed.apk
Certificate SHA-256: 62:B4:BA:90:BF:83:83:47:8A:F2:B4:02:19:1E:DE:92:50:CE:35:C8:68:83:B4:D5:5D:57:12:24:BB:40:48:F7
apksigner verification should include:
Verifies
Verified using v3 scheme (APK Signature Scheme v3): true
Number of signers: 1
Signer #1 certificate DN: CN=SAGIO
Signer #1 certificate SHA-256 digest: 62b4ba90bf8383478af2b402191ede9250ce35c86883b4d55d571224bb4048f7
v3: true is acceptable because apps/terminal has minSdk = 29, and APK
Signature Scheme v3 is supported on API 28+.
Install On Motorola¶
If a debug-signed build is already installed, uninstall first because Android will reject an update signed by a different cert:
adb uninstall io.sagio.merchant || true
adb install app/build/outputs/apk/release/app-arm64-v8a-release-kms-signed.apk
Then:
- Open SAGIO.
- Sign in.
- Go to Settings -> Refresh SDK Credentials.
- Try Charge again.
Troubleshooting¶
Cannot run program "gcloud":
- Install Google Cloud CLI, or set
KMS_SIGNER_ACCESS_TOKENto a short-lived token from an authenticated environment.
export KMS_SIGNER_ACCESS_TOKEN="<token from: gcloud auth print-access-token>"
./scripts/sign-release-with-kms.sh
KMS returns 403:
- The active Google account lacks
cloudkms.cryptoKeyVersions.useToSign. - Grant
Cloud KMS CryptoKey SignerorCloud KMS CryptoKey Signer/Verifieron thesagio-app-signerkey.
INSTALL_FAILED_UPDATE_INCOMPATIBLE:
- A differently signed APK is installed. Run:
adb uninstall io.sagio.merchant
adb install app/build/outputs/apk/release/app-arm64-v8a-release-kms-signed.apk
Soft Space still reports unlock fail:
- Capture a fresh logcat from the signed APK:
adb logcat -c
# trigger the Charge failure
adb logcat -d | grep -iE "FasstapManager|SSPOG|unlock|SDK initialized|RefreshToken|integrity|attestation|softspace|ssmpos"
If Settings -> Refresh SDK Credentials succeeds and this APK is signed with
62:B4..., the remaining blocker is likely Soft Space-side provisioning,
device/network policy, or MUID/SSO alignment rather than Sagio API config.