Repackaging detection
Repackaging detection is a security feature that detects if the application was modified and resigned with a different signing certificate.
To properly configure the repackaging detection, you need to provide the SHA-1 hash of the signing certificate.
The process of obtaining the hash differs based on the way you sign and publish your app.
The following documentation only addresses publishing to the official Android store - Google Play.
There are two basic ways how apps can be signed and published:
- Legacy Apk Signing
- Google Play Signing
Legacy Apk Signing
What we call Legacy Apk Signing has been the standard way of app singing and publishing from the dawn of the Android platform. It’s still used both for signing development builds and for signing published builds.
Nevertheless, Google recently announced that Legacy Apk Signing will be discontinued for publishing new apps to Google Play.
Legacy Apk Signing works this way:
- Developer creates a keystore with a release key (done only once).
- App is built and signed with the release key.
- App is uploaded to Google Play.
- Google Play store distrubutes the app as is.
Obtaining Signature Hash When Using Legacy Apk Signing
When Legacy Apk Signing is used you can obtain the correct value two ways:
- From the keystore
- From a signed APK
Obtaining the hash from the keystore
Use this command on Linux or Mac to get the hash.
keytool -list -v -keystore ${SOME_KEYSTORE} -alias ${MY_ALIAS} | grep "SHA1" | sed "s/.*(SHA1): //" | sed "s/://g" | tr "[A-Z]" "[a-z]"
Obtaining the hash from a signed APK
apksigner verify --print-certs ${SOME_APK} | grep "SHA-1" | sed "s/.*: //"
Google Play Signing
Google Play Signing is a bit more complicated signing process. It was designed to allow splitting apps into parts (app bundles) and distributing to the target devices only the parts that match those devices’ features and configuration.
Originally Google Play Signing allowed uploading signed apks. Starting August 2021 new apps can be distributed only as app bundles.
With app bundles Google Play Signing for new apps works this way:
- Developer creates a keystore with an upload key (done only once).
- App bundles are built and signed with the upload key.
- Opt in to Google Play App Signing (done only once).
- Signed app bundles are uploaded to Google Play.
- Google Play store resigns them with a release key.
- Google Play stores distributes app bundles necessary for creating the app to each user device.
Obtaining Signature Hash When Using Google Play Signing
When using Google Play Signing you can obtain the correct value two ways:
- From Google Play Console
- From a signed APK that was installed from Play Store to a device
Obtaining the hash from Google Play Console.
Go to Google Play Console and then go to Setup > App integrity. Find section called App signing key certificate.
Copy the value of SHA-1 certificate fingerprint
and remove colons from the string.
On a Linux or Mac, you can use.
pbpaste | sed "s/://g" | tr "[A-Z]" "[a-z]"
Obtaining the hash from a device.
When Google Play Signing is used obtaining the correct app signature from the app on device is a bit harder. It’s necessary to get the signed apk with the right signature - the app has to be installed from Google Play store. You can’t use development builds any more (they are signed with the upload key).
To get the signature hash you have to pull the apk from an Android device first. You can find the location of the apk on a device by running:
adb shell pm list packages -f | grep "$PACKAGE$" | sed "s/package://" | sed "s/=$PACKAGE//"
Then you can obtain the apk from the device by running:
adb pull "$APK_LOCATION"
The last step is obtaining the signature hash from the pulled apk:
apksigner verify --print-certs ${SOME_APK} | grep "SHA-1" | sed "s/.*: //"
Setting Signature Hash in RaspConfig
Use the obtained value of SHA-1 hash of signing certificate in RaspConfig
this way:
val raspConfig = RaspConfig.Builder()
.signatureHash(CERT_HASH) // SHA-1 of signing certificate(s)
.build()
The expected values is lowercase hex string without any byte separators.
Testing Correct Repackaging Config
The complexity of testing the repackaging config depends on usage of Google Play Signing.
When Legacy Apk Signing is used you can test the correct signature locally without uploading the app to Play store.
When Google Play Signing is used you have to go through a development cycle including upload to Google Play store.
Testing When Google Play Signing is Used
To test repackaging configuration when Google Play Signing is used one have to distribute a build of an app through Google Play. That’s a necessary step to test the app with the correct signature.
Beside distribution of production apps Google Play offers several kinds of testing distribution:
- Open testing - Anyone can join as tester on Google Play.
- Closed testing - Testing with specific users.
- Internal testing - For initial quality assurance check by a list invited testers.
We recommend to perform Internal testing on Google Play Console for testing your repackaging configuration.
The testing cycle will be following:
- Build the app and sign it with the upload key.
- Create an Internal testing release (go to Testing > Internal testing in Google Play Console).
- If you haven’t setup internal testing before, add some testers (emails have to be provided).
- If you haven’t setup internal testing before, send your internal testers link to join the testing. You can copy the link in How testers join your test section on the page.
- Testers have to open the link. After they accept the testing invitation on the page there will appear a Google Play link for installing the app.
- After installation, a version of the app that is signed with the release key is present on the device.
- Test the app. If repackaging detection is triggered, wrong certificate is present in
RaspConfig.signatureHash
. In such case do not release to production. Obtain the correct signature hash, update the app and perform another testing cycle(s) until you are sure your app works flawlessly.