[HOWTO] verify Apk signatures and reflect on Publisher trust

I wanted to know more about apk signature verification and took to the MagicEarth App as an example as the App came up in a forum question. I fetched one of the prebuilt apks in the commit history and run

~$ apksigner verify --verbose --print-certs magicearth.apk
Verifies
...
Signer #1 certificate DN: CN=ROUTE 66, OU=ROUTE 66 Switzerland GmbH, O=ROUTE 66 Switzerland GmbH, L=Brasov, ST=Romania, C=RO
Signer #1 certificate SHA-1 digest: 3705ba93d86f9566cdb440977e65c8df660514ae

In the first line apksigner verfies successfully the integrity of the apk, attesting that the checksums kept in summary files that the signature covers are indeed the same. If a file in the archive is altered and the attacker is not in the possession of the original signing private key and uses another one, the signed certificate will have another fingerprint. Also, if an update Apk doesn’t match the signature fingerprint of the already installed app ID, an installation is denied. So while there’s no protection for the first time install, there is for the update.

The next thing I want to know is if the signing certificate really does belong to the Organisation it claims to come from. Without an authority like the Google Play Publisher API telling me or the publisher itself doing it (as Signal.org or F-Droid Repos do), some mirrors are looking at past certificates used for the App. This can be a valid heuristic to decide on the trustworthiness, but obviously has shortcomings for releases after a private key has been compromised. A public notary would be preferable.

Services like androidobservatory.org check over time what certificate is used for a specifid app id. You can compare this across mirrors for the MagicEarth sha1 fingerprint (3705B[...]14AE) they’re identical for this current sample: aapks.com, apkpure.com (info icon), apkmirror.com (click “verified safe to install”). And Cleanapk:

curl "https://api.cleanapk.org/v2/apps?action=search&keyword=com.generalmagic.magicearth&by=package_name" | jq # look for an object to search for
curl -s --globoff  "https://api.cleanapk.org/v2/apps?action=app_detail&exact_arch=false&id=5bef39e54ecab403b15e9cb7&architectures=[arm64-v8a]" | jq | grep signature
      "signature": "3705ba93d86f9566cdb440977e65c8df660514ae",

To have some assurance after the fact one can also compare signing certificate fingerprints of installed packages with Checkey. It will show a sha1 3705B[...]14AE for the Magicearth App.

1 Like

there are other projects actively looking at apk signatures, namely ScatterScam (example), presented on Exodus blog “Have you heard about app counterfeiting?”.

A project in its start and going way beyond certificate introspection is Pithus. It targets app behaviour / code structure+classes, app similarity.

I’d be interested in surfacing some signature assurance to the /e/-Apps UI.

the postscript in 2025 is the v3 apk signature scheme having introduced key rotation, and v3.1 minSdkVersion: for com.whatsapp you’ll look at two signing certificates with the version split

Signer (minSdkVersion=33, maxSdkVersion=2147483647) certificate DN: CN=WhatsApp LLC, O=WhatsApp LLC, L=Menlo Park, ST=California, C=US
Signer (minSdkVersion=33, maxSdkVersion=2147483647) certificate SHA-256 digest: fb920d381bee1b2093f27dc8f13d994da629dc91887d0529b35c9a2dc4f4a6c2
Signer (minSdkVersion=33, maxSdkVersion=2147483647) certificate SHA-1 digest: 8b0debf9516af037c9be2f539584b97fe9781764
...
Signer (minSdkVersion=24, maxSdkVersion=32) certificate DN: CN=Brian Acton, OU=Engineering, O=WhatsApp Inc., L=Santa Clara, ST=California, C=US
Signer (minSdkVersion=24, maxSdkVersion=32) certificate SHA-256 digest: 3987d043d10aefaf5a8710b3671418fe57e0e19b653c9df82558feb5ffce5d44
Signer (minSdkVersion=24, maxSdkVersion=32) certificate SHA-1 digest: 38a0f7d505fe18fec64fbf343ecaaaf310dbd799

funny: that maxSdkVersion=32 signing cert is 15+ years old used since at least 2010 in v1

1 Like