APK Development and Best Practices

XTD Protect for Android: Command Line Tools

This article discusses:

Java Code Best Practice

The following key factors provide one way of estimating the protection quality from security viewpoint within the defined protection scope:

  • The percentage of original bytecode methods translated to native code
  • The percentage of bytecode method invocations transformed to inlined direct C-to-C function calls bypassing the VM completely
  • The percentage of original bytecode methods completely wiped from the remaining protected bytecode.

The tool uses method inlining extensively for security and performance reasons. For each identified inlining opportunity, the compiled target method is invoked directly at C level instead of the standard JNI method invocation. Thus it is highly beneficial to write the Java code in such a way that there are as many inlining opportunities as possible. In practice this means the following:

  • Prefer static methods over virtual methods, especially in parts that are critical from performance or security viewpoint.
  • When possible, use the final keyword when declaring virtual methods.

Note: Some code will never get translated.

There are a number of conditions that prevent translation of specific classes and methods. For example:

  • Classes belonging to packages that are shipped with the Android platform are automatically excluded from the protection scope, as ART will prefer the package from the platform over the one included in the application. Therefore changes to those classes would have no effect, and in some cases translation causes runtime issues.
  • Using dynamic features such as reflection, custom class loaders, and runtime annotations for classes and methods makes it very difficult to determine reliably at protection time what specific bytecode methods may potentially be invoked by Java VM. This prevents method wiping completely, as invoking a non-existent method at runtime would result in NoSuchMethodException being thrown.
  • Recursive code paths cannot be reliably translated into native code. This is because ART only allows up to 512 JNI local references per JNI invocation, and the number of references consumed by recursive invocations is not known at compile time.
    Violating the reference limit results in OutOfMemoryError being thrown.
  • Methods containing System.loadLibrary() invocations for loading JNI libraries cannot be translated into native code, because it would interfere with ART library loading implementation resulting in UnsatisfiedLinkError at runtime.
    Static initializer blocks and constructors are never translated into native code due to Java design constraints. Verimatrix recommends factoring any sensitive initialization code into separate methods, preferably static ones as they have the best prospects for protection.

Designing Methods

As static methods may never be overridden, invocation of any translated static method will always be performed C-to-C when applicable. Furthermore, any translated static methods that are never invoked from bytecode methods will be completely wiped away, thus providing immunity against Java debugging techniques. Whenever possible, any methods should be declared private, as this guarantees they will never be overridden and thus make good candidates for being wiped.

Any methods implementing interface methods, or overriding base class methods are never wiped away because that would potentially break the Java VM virtual invocation. Verimatrix recommends writing any sensitive code in a more procedural manner instead of relying on interfaces and method override.

Interoperability with R8

Verimatrix recommends using R8 for stripping any unused code from the application before protection. The class and method renaming features of the tools may also be considered useful by the developer and are compatible with apkdefender. However, it should be noted that these provide only little additional protection. The R8 mapping files are supported by apkdefender. For more information see section R8 mapping file support.

Performance Tuning

Injecting protection code can increase performance overhead. You may sometimes need to tune the protection scope to improve application performance. For detailed instructions on how to define the protection scope, please refer to Defining the protection scope. As a general rule, the activity packages and classes are often good candidates for being included in the protection scope, as the application lifecycle hooks do not execute often.

Identifying what classes or packages to exclude requires applying application-specific knowledge when trying to determine a feasible protection scope. Any classes or packages known to have an observable negative impact on performance should be excluded as the very first rough performance tuning step. After initial rough adjustments it will often be necessary to iteratively tune the protection scope and benchmark the resulting performance to see what parts of the application really are performance sensitive.

Source code level analysis is a good and often adequate tool for understanding the protection impact on performance. The tool mainly impacts performance by translating methods to native code for further protection, therefore performance critical classes should usually be excluded from the scope. However, it should be noted that all scope exclusions to improve performance will affect the protection level, and thus should be weighed up against the possible impact on overall security.

Annotations

Adding checks and fine-tuning protection scope can also be done directly from the Java source code by adding annotations to methods.The supported annotations are:

  • @Translate
  • @Wipe
  • @DontWipe
  • @DontTranslate
  • @Check
  • @SuperCheck
  • @StatusResponse
  • @Analyze (aardefender only)

The support library apkdefender.jar must be included in the build, if annotations are used.

Excluding some methods from wiping, or even translation, may be necessary for stability and performance reasons. With annotations the excluded methods can be determined already at development time. Methods marked with annotation @DontWipe are excluded from wiping, which is necessary for example if they are called from any native libraries. Methods annotated with @DontTranslate are excluded from translation completely, for example for performance reasons. These annotations are equal to the configuration options exclude_translated_methods_from_wiping and exclude_methods_from_translation.

Sensitive methods can be annotated with @Translate or @Wipe to ensure best possible protection. Bytecode to native translation is attempted for methods annotated with @Translate. The static methods that are annotated with @Wipe are translated and completely wiped away. The annotation is valid only, if enable_method_wiping configuration option is set to true, otherwise it is ignored.

Enforcing protection scope via annotations works on best effort basis. If a method is annotated for translation or wiping, but that is prevented for other reasons, the tool will fail with an error message. Annotations can also be used to guide the integrity check placement in the code that is translated to native. If a method is annotated with @Check, injecting integrity checks to the method translated to native is attempted. If @SuperCheck annotation is used, injecting superchecks that provide instant integrity check of the entire native library is attempted. Annotation @StatusResponse is used to denote a callback method implementation for the event detection features. The event detection callback details are described in section Event callback.

Annotation @Analyze is used to denote API functions and their calling scores. This annotation accepts the API function calling score as a parameter. It can be used instead of the configuration option library_api_methods. The annotation is meaningful only in the context of the aardefender tool.

Annotations with R8 Shrinking

By default shrinking will remove the annotations from code, and by heavy inlining change the method structure. To prevent removing the annotations and inlining the methods, keep rules for the used annotations must be added to the R8 configuration file. For the annotation keep rules to be effective when using Gradle build tool, the option minifyEnabled true must be set in the build.gradle file of the project.

For example the following keep rules will ensure that @DontWipe and @DontTranslate annotations do not get removed:

-keepattributes *Annotation*
-keep class com.insidesecure.core.annotations.DontTranslate
-keep class com.insidesecure.core.annotations.DontWipe
-keepclassmembers class * {
@com.insidesecure.core.annotations.DontTranslate <methods>;
@com.insidesecure.core.annotations.DontWipe <methods>;
}

Please note that the keep rules above will also prevent the method renaming. All used annotations need to be added to the keep rules explicitly.

Defining the Protection Scope

The protection scope can be set in the configuration file. The file contains list elements, which allow filtering at package, class and method levels. Parameters dealing with package names support wildcard notation, where for any provided filters asterisk will match everything, and can be given to replace any part of the filter. For example, configuring [com.example.]{.title-ref} in the include_packages option will include all packages with names beginning "com.example.", and [.sample]{.title-ref} in the exclude_packages option will exclude all packages ending in ".sample". The exclude_methods_from_translation and exclude_translated_methods_from_wiping list elements should be given as complete method names, including the class part. For listed methods that have overloaded versions, all of the methods with the same name will get excluded.

The protection_coverage.json file will provide detailed information about translated, wiped methods as well as information about package, class and method exclusions and encrypted assets and string resources.

An example that shows how filtering could be performed is given below.

\{
  
    "include_packages": [
        "com.example.package"],
    "exclude_packages": [
        "com.example.package.sub.*",
        "com.example.package.sub2.*"],
    "exclude_classes": [
         "com.example.package.sub3.SomeClass"],
    "exclude_methods_from_translation": [
         "com.example.package.sub4.SomeClass.SomeMethod"],
    "exclude_translated_methods_from_wiping": [
         "com.example.package.sub4.SomeClass.SomeOtherMethod"]
}

Good candidates for exclusion are any third party libraries publicly available in source code format, third party libraries with problematic license terms or with already protected bytecode, or packages or classes that are most likely to have a significant negative impact on perceived application performance.

Event Callback

Some features can be configured with report, exit, or report and exit action. If the report option has been chosen, detected event will be reported to a callback method that is implemented in the application.The callback method can be named anything, but the method signature must be of following format:

public static void eventResponse(int data)

The event detection callback method is annotated with @StatusResponse.

The following rules must be observed in the callback method implementation:

  • The method must be declared static.
  • Resource intensive actions must not be executed in the callback method.
  • Only one callback method can be implemented in the application.

Sample implementation for a rooting detection callback:

import com.insidesecure.core.annotations.StatusResponse

@StatusResponse
public static void eventResponse(int data) {

    String tag = "STATUSRESPONSE";

    if (data == StatusResponse.STATUS_RESPONSE_ROOTING_DETECTED)
    {
        Log.i(tag, "StatusResponse callback, device rooting detected." );

        StatusResponseAction action = new StatusResponseAction();
        Thread actionThread = new Thread(action);
        actionThread.start();
    }
}

private static class StatusResponseAction implements Runnable {
    public void run()
    {
        /* Resource intensive event response action can be added here */
    }
}

Currently available status response values are:

STATUS_RESPONSE_ROOTING_DETECTED
STATUS_RESPONSE_EMULATOR_DETECTED
STATUS_RESPONSE_OVERLAY_DETECTED
STATUS_RESPONSE_UNLOCKED_BOOTLOADER_DETECT

Bootloader unlocking renders the device insecure and it is considered rooted. Bootloader unlocking detection is controlled by the rooting detection configuration options. When the bootloader is unlocked, the event callback is normally called only with STATUS_RESPONSE_UNLOCKED_BOOTLOADER_DETECTED status, which should be treated as rooted. In case when the bootloader unlocking is hidden by the rooting tool, only STATUS_RESPONSE_ROOTING_DETECTED status is received.

Please note, that if the report_and_exit option is configured, any resource intensive event response action is not possible: redirecting resource intensive tasks in another thread will not succeed, as the process will exit without waiting the thread to finish. Resource intensive tasks in main thread are not tolerated by the Android platform.

DEX Encryption

The apkdefender and aabdefender tools support the DEX encryption feature to enable loading and executing Dalvik executable DEX bytecode dynamically from obfuscated static files.

At SDK level 28 Android API introduces the android.app.AppComponentFactory feature for instantiating the various application components declared in the manifest. The AppComponentFactory is the natural standard mechanism for hooking instantiation of application components, and the tool uses it for DEX encryption on modern devices. With this approach the tool is able to encrypt the entire application DEX bytecode.

To support older OS versions without the AppComponentFactory feature support, the tool may need to apply a number of workarounds that depend on implementation details. Therefore, if the minSdkLevel is less than 28, minimal amount of the original bytecode needed for loading the original application class may appear in plaintext format.

With default settings DEX encryption feature has an autoprobe functionality, which automatically disables the feature for applications that are incompatible with it. If DEX encryption is explicitly configured to enabled or disabled with the enable_dex_encryption configuration option in the configuration JSON file, the autoprobe functionality is not applied.

Bytecode Renaming

The apkdefender and aabdefender tools support renaming classes, methods and fields in the original bytecode. The tool analyzes resources to automatically identify any references to original bytecode names, and preserves any referenced classes automatically.

Method renaming is part of the bytecode renaming feature, and is controlled by the same configuration options. For stronger protection, using DEX encryption is recommended.

Bytecode renaming feature is enabled with the general configuration option enable_bytecode_renaming in the configuration JSON file. The renaming scope is configured with renaming_scope option. Only classes that match an explicit include package rule and do not match any explicit exclude package rule will be processed. In addition, there are configuration options for explicitly keeping specific classes and fields. All renaming scope rules support wildcard notation.

The bytecode renaming scope must be configured in a way that does not break any name dependent features, such as reflection or bytecode callbacks from native libraries. By default the renaming scope is empty to prevent renaming standard classes such as the java.lang package. Renaming standard classes would produce unstable protected applications.

Invisible characters are used by default with renaming on Linux and Windows hosts. The filesystem on macOS limits the use of such characters, and there the invisible characters are not used. Additionally, the use of invisible characters can also be disabled by setting internal debug option enable_bytecode_visual_spoofing to 'false' in the general_configuration section of the configuration JSON file.

Bytecode renaming produces a JSON file mapping.json in the working directory, with the mapping of the renamed classes and fields to the original names.

R8 Mapping File Support

If the application bytecode has been obfuscated with R8, the original package, class or function names cannot be directly used for configuring the protection scope any more. However, R8 write the data for mapping the original package, class and method names to the obfuscated one to a mapping file. This mapping file can be used as input for the apkdefender tool, enabling the use of original package, class or function names for the include and exclude filters. Operating with the original names provides an easier user experience removing the need to use the cryptic obfuscated names. Using the original names also helps in cases, where the bytecode obfuscation causes each new build of the application to use slightly different renaming logic.

Using the mapping file with apkdefender is enabled by giving the mapfile as an input for the --mapping-file command line option:

apkdefender -c <path-to-configuration-file--mapping-file <path-to-mapping-fileapp-release.apk

After protecting an application, the protection_coverage.json file will contain the "unobfuscated_data" key that contains data for translated and filtered methods with the original names. This information can be used to map the original Java method name to the translated native function name.

Gradle Plugin

A gradle plugin library ApkdefenderPlugin.jar can be found in the XTD Protect for Android package Plugins directory. The jar library can be shared in multiple builds and projects. For each available release variant in your project, such as Vanilla, the plugin will create new Tasks, such as apkprotectVanillaRelease or aabprotectVanillaRelease, depending if the variant outputs are apk or aab:

  • apkprotectVariantRelease performs single pass protection of application variant. This may lead to protecting several configuration apks.
  • aabprotectVariantRelease performs the protection of App Bundle in a single pass.
  • aarprotectVariantRelease performs the protection of library in a single pass.

To take the plugin in use, copy the plugin jar ApkdefenderPlugin.jar to your project directory and add it to the classpath in your module-level build.gradle file. Next, apply the plugin and optionally define default and output-specific configuration files in the apkdefender extension:

buildscript {
     dependencies {
         classpath files(<jar-directory>/ApkdefenderPlugin.jar)
     }
 }

 apply plugin: com.insidesecure.apkdefender.ApkdefenderPlugin

 // Optional, but useful for defining default protection behaviour for various outputs
 apkdefender {
     config = 'default_config.json'
     mapping= 'default_mapping'
     variant_configs = [
         'TargetVariant' : '<path-to-variant-specific-config-file>'
     ]
     variant_mappings = [
         'TargetVariant' = '<path-to-variant-specific-mapping-file>'
     ]
 }

In the apkdefender section the 'config' file is used by the apkdefender tool, unless otherwise specified. To determine the right formatting for output names, run the gradle tasks command and look at the output-specific tasks added by the apkdefender plugin.

Android Studio plugin

An Android Studio plugin for the protection tools for Android applications is provided to make development easier for IDE users.

Steps to install the plugin:

  1. Install the plugin from Android Studio: select File -> Settings -\Plugins -\Install plugin from disk
  2. Choose the plugin JAR file mfjava-plugin.jar from the XTD Protect for Android package Plugins directory.
  3. Press Apply and restart the IDE.

An Apkdefender option can now be found under the Tools option in the main menu bar. Fill in all the required parameters.

The APK is protected by choosing APK-protect as the build mode. The protected application is saved to the same location as the original APK.

In order to protect Android App Bundles or libraries, please select the AAB-protect or AAR-protect options respectively.

The debug log from apkdefender can be found in the temporary directory, in a directory starting with apkdefender-plugin-, logs and artifacts will go to the working directory. Android Studio IDE log files can be found from Help button on the main menu bar and then 'Show Log in Files'. Logs generated by the plugin itself, for example the full command line of apkdefender, can be found there.

Xamarin for Android support

The apkdefender tool supports protecting Xamarin for Android applications. To deploy the Xamarin-specific protective measures, set the optionenable_xamarin_protection to 'true'.

Xamarin Java bytecode containing bidirectional bindings between ART and the Mono runtime is translated into native code. The tool establishes a tight cryptographic coupling to the resulting protection library by embedding the application .NET bytecode in the library.

Each Xamarin assembly file is compressed for storage, encrypted using AES/CBC with a unique 256-bit random key automatically generated at protection time, and embedded as static read-only binary data inside the native protection library. Decryption uses a custom implementation in pure C, which makes the implementation immune to key extraction attempts by tampering the Java platform cryptographic providers.

Xamarin.Android 11.0 version compresses managed assemblies by default for release builds. As apkdefender tool compresses and encrypts managed Xamarin assemblies providing space optimization and security, the compressed assemblies are not compatible with Xamarin protection. AndroidEnableAssemblyCompression MSBuild property must be set to false to disable managed assembly compression.

<PropertyGroup>
  <AndroidEnableAssemblyCompression>false</AndroidEnableAssemblyCompression>
  <AndroidEnableAssemblyCompression>false</AndroidEnableAssemblyCompression>
  <AndroidUseAssemblyStore>false</AndroidUseAssemblyStore>
</PropertyGroup>

Kotlin Support

The apkdefender tool supports protecting applications that are developed using Kotlin programming language. Kotlin runs on the Java virtual machine, and it is designed to interoperate with Java code. The bytecode produced by Kotlin is similar to that produced by Java, and both can be translated by apkdefender tool in the same manner.

Gaming Protection

The protection tools for Android applications provide protection for video games against cheat tools. Running the application on a rooted device is prevented when Rooting detection is enabled. A gamer may also try to use overlay applications which run over the game application, trying for example to draw lines which help to improve the game result by increasing the length of the aim while playing the game. This cheat mechanism can be detected with the Overlay detection feature. For Android 12 and above the platform's hide overlays feature can be enabled in the application. Cloning and modifying theapplication is prevented by default with the application integrity checks.

BlueStacks Emulation Detection

Normally, BlueStacks Mobile Gaming Platform for Android is considered to be an emulator, and should be detected. To ensure detection, x86 binaries should not be included in the protected application package. This will enforce BlueStacks to Houdini emulation mode that will be detected.

In a special case when BlueStacks Gaming Platform should be allowed, include both arm and x86 binaries in the application, and turn root detection into report mode.

Monitoring Service

Verimatrix App Shield Monitoring is a cloud service for real time security monitoring of mobile applications. It allows the customer to understand the risk profile of each individual application instance operating in their ecosystem and to take action to mitigate the risks. The functionality offered by Verimatrix XTD Monitoring is sometimes called Device Attestation or Mobile Threat Device.

Protected applications have the option of sending security events to the cloud service for analysis. This builds up a more detailed risk analysis for each instance of the application than can be achieved within the application context alone.

To enable the Monitoring service, monitor_api_key, monitor_api_key_id and monitor_api_key_owner_email configuration options must be set. The API key and key ID can be obtained from the Verimatrix Platform. The API key owner email is the email address connected to the account that was used to create the Monitoring service API key on Verimatrix Platform. If Monitoring service is enabled for an AAR archive, configuration option target_app_id must also be set. This configuration option binds the AAR to a single application, thus the AAR needs to be reprotected for each application that is using it.

Protection Report

The apkdefender and aabdefender tools offer an option to quickly and reliably verify if an APK or AAB is already protected. This can be achieved with the --build-mode check-protection-status command line option.

For example, to check if an APK is protected:

apkdefender --build-mode check-protection-status app-release.apk

Additionally, the tools provide a JSON formatted efficacy report, which summarises the protections that have been applied. The efficacy report is optionally produced, and generating it needs to be enabled with the command line option --generate-report , as follows:

apkdefender -c <path-to-configuration-file--generate-report app-release.apk

The efficacy report JSON file is generated under the current working directory, with name <app‑filename>‑efficacy_report‑<timestamp>.json, for example app‑release.apk‑efficacy_report‑2024-06-24T11-30-52Z.json.

16 KB Page Size Support

Starting from Android 15 devices that are configured to use a page size of 16 KB are supported by Google. Device manufacturers continue to build devices with larger amounts of physical memory, and many of these devices will adopt 16 KB page sizes to optimize the device performance. Adding support for 16 KB page size devices enables applications to run on these devices and helps them to benefit from the performance improvements.

16 KB devices require the shared library ELF segments to be aligned properly using 16 KB ELF alignment in order for an application to run. Any application that uses NDK libraries, either directly or indirectly through an SDK, will need to be rebuilt to support 16 KB devices. Without recompiling, applications might not work on 16 KB devices in future Android releases. The security library from protection tools for Android applications is always aligned to 16 KB boundary. Note that 16 KB pages are currently only supported on arm64 targets with 16 KB kernels.

Certificate Pinning

By default, secure connection from application using protocols like TLS and HTTPS trust all pre-installed CAs. In case of a fraudulent CA certificate, the application would be vulnerable to a man-in-the-middle attack. It is possible to limit the set of accepted certificates by certificate pinning. For certificate pinning a set on certificates is provided, and the certificate chain is then validated against this set, verifying that the chain contains at least one hash of a public key from the set of the pinned certificates.

To configure certificate pinning, the file path to a certificate or a set of certificates need to be provided in the certificate_pinning option of configuration JSON file. To ensure unaffected connectivity if switch to new keys or changing CAs is forced, a backup key should always be included in the list of pinned certificates. A sample of the certificate pinning configuration format and options is given below:

"network_security_configuration": {
    "certificate_pinning": [
    {
        "domain": "www.sample.com",
        "pins": [
            "sample.com.pem"
        ],
        "subdomains": true,
        "cleartext": false
    }
    ]
}

If only encrypted traffic should be allowed to the configured destination, the cleartext option in configuration should be set to false. Additionally, to prevent connectivity issues in applications that have not been updated, it is possible to set to set an expiration time for certificate pins. However, it should be noted that setting an expiration time may help attackers to bypass the pinned certificates.

Configuring Signing Certificate

During protection the application is bound to a signing certificate for full application integrity protection. At least one signing certificate must be given for binding.

The signing certificate is configured with the option signing_certificate in the configuration JSON file. The option is configured as the file path for the signing certificate. Only RSA certificates are supported, and the certificate must be given in DER or PEM format.

If Google Play Signing service is used, at least the signing certificate provided by Google must be given. Note that this is not the same as the developer certificate that is used to sign the application before Google Play Store upload.

It is possible to configure also a secondary signing certificate for binding. This can be for example the local signing certificate that is used to sign the application for local testing. The option signing_certificate accepts a list of file paths to certificates as the value. If the secondary certificate has been configured, the application can be signed with either signing key without breaching the integrity.

Please note, that the local signing key must be stored securely, as it will allow resigning the application after any changes. Please do not use any well known debug signing keys even for local testing.

Protecting Dynamic Modules in an AAB

Normally aabdefender tool includes the base.apk of an AAB into the protection scope. Protection for dynamic modules in an AAB can be achieved by including a protected AAR in the module. In such scenario the parts of the dynamic feature module that are desired in the protection scope should be refactored into an AAR. The AAR can then be protected with the aardefender tool. The AAR module should then be added as a dependency to the AAB dynamic feature.

The split APK that will include the protected AAR can be defined by the target_split option in configuration JSON file. If this option is defined, it is verified that the protected code is in the intended split APK. By giving the signing certificate with the signing_certificate configuration option, the dynamic module can be bound to the signing certificate of the final application bundle. If target_split has been defined, the integrity verification is applied separately to the corresponding dynamic module.

Connection Whitelisting

The protection tools for Android applications provide a capability to detect supply chain attacks. This can be achieved by configuring the allowed DNS names for TLS connections with the connection_whitelist option in the configuration JSON file. Connection attempts in violation of the configured list will be reported to the monitoring interface, thus enabling detection of suspicious connections. The feature is applicable only if the Monitoring service is also enabled.

Uploading a Protected Application to Google Play Store

If the protected application is uploaded to the Google Play store, it is recommended that the customer manages their own signing key and keystore, and sign the application themselves.

The apkdefender tool provides also a signing_certificate configuration option to allow using Google Play App Signing service. When using Google Play App Signing, Google removes the upload key signature, and re-signs the app with the app signing key on their server. Google may also modify the application before signing for example by adding some analytics. When the signing certificate has been provided at the protection time, the application integrity is considered in context of the given signing certificate. Otherwise both re-signing as well as any modifications to the application will cause the application integrity checks to fail, and are interpreted as tampering.

The given signing certificate must be the App signing key certificate provided by Google, and not the developer certificate that is used to sign the application before Google Play Store upload. The certificate can be downloaded from Google Play Console.

Please note that while Google Play App Signing is optional, the opt-in is permanent. Once the application is enrolled in Google Play App Signing, it can not be withdrawn.

Uploading a Protected Application to Amazon Appstore

The apkdefender tool provides an option to allow distributing applications via Amazon Appstore. As Amazon service may modify the application before signing by injecting additional bytecode and making changes to the application resources, some special handling is required for integrity verification. To enable Amazon Appstore compatibility, set the app_store_compatibility option in the configuration JSON file:

"app_store_compatibility": "Amazon"

Due to the modifications by Amazon, resource encryption and DEX encryption features are not supported if Amazon Appstore compatibility is configured.

As Amazon service re-signs the app with the app signing key on their server, the signing certificate SHA1 fingerprint must be set in the option signing_certificate_fingerprint in the configuration JSON file. When the signing certificate has been provided at the protection time, the application integrity is considered in context of the given signing certificate. Otherwise both re-signing as well as any modifications to the application will cause the application integrity checks to fail, and are interpreted as tampering.

The given signing certificate fingerprint must be from the App signing key certificate provided by Amazon. The certificate fingerprint can be retrieved from the Amazon developer console. Enabling Amazon Appstore compatibility implicitly limits the protected application distribution to Amazon Appstore only. Other allowed package installers as the installation source cannot be defined.

Note, that the target API level for the application must be 30 or higher to ensure that Amazon Appstore uses APK signature scheme v2 signature for signing the application.