Menu
Menu
inquire

Documentation

Android

Configuring DexProtector

Introduction to configuring DexProtector

DexProtector works best when it is tailored to your app.

Configuration is by means of a single XML file, which can be edited directly or via the DexProtector Studio interface. A default configuration file (dexprotector.xml) can be found in the root folder of the distribution package, but every app (or SDK) has different requirements. We therefore strongly recommend that you create your own tailored configuration, in order to target the correct code and resources for protection.

You can use the configuration file to control the DexProtector process according to your needs. The configuration file allows you to specify the details of:

  • Build and logging settings for the DexProtector process
  • Signing methods
  • Protection mechanisms and filters for including/excluding code and resources for protection
  • Environment checks, for the detection of rooted devices; debuggers; emulators; and hooking tools
  • Network security options, including certificate monitoring for both Certificate Transparency and Public Key Pinning mechanisms
  • Integration with Licel's Threat Reporting and Attack Telemetry system, Alice

Protection Recommendations

We recommend making use of all of the security features provided, as each element of protection adds more security and more resistance against malware, reverse engineering, tampering, and Man-in-the-Middle attacks.

Note: For some features, a DexProtector Enterprise license is required. For more information, see our feature comparison for DexProtector Standard and DexProtector Enterprise.

Configuration file overview

xml
<?xml version="1.0" encoding="utf-8" standalone="no"?>

<dexprotector>

<!-- BUILD SETTINGS -->

  <verbose>false</verbose>
  <optimize>false</optimize>
  <securityAssessment>
    <signingCertificateCompromised mode="error" />
    <signingCertificateWeakKey mode="error" />
    <dependencyCheck mode="warning" />
  </securityAssessment>
  <proguardMapFile>no_default</proguardMapFile>

<!-- SIGNING -->
 
  <signMode>debug</signMode>
  <keystore>no_default</keystore>
  <storepass>no_default</storepass>
  <alias>no_default</alias>
  <keypass>no_default</keypass>
  <certificate>no_default</certificate>
  <sha256CertificateFingerprint>no_default</sha256CertificateFingerprint>
  <legacySha256CertificateFingerprint>no_default</legacySha256CertificateFingerprint>
  
<!-- CODE STRIPPING -->

  <stripLogging>no_default</stripLogging>
  <stripMethodCalls>
        <filters>
            <filter>no_default</filter>
        </filters>
    </stripMethodCalls>        

<!-- CODE PROTECTION -->

  <stringEncryption/>

  <annotationEncryption/>

  <jniObfuscation/>

  <hideAccess/>

  <classEncryption/>
  
  <nativeLibraryEncryption/>

<!-- RESOURCE PROTECTION -->

  <resourceEncryption>
    <assets>
        <filters>
            <filter>no_default</filter>
        </filters>
    </assets>
    <res>
        <filters>
            <filter>no_default</filter>
        </filters>
    </res>
    <strings>
        <filters>
            <filter>no_default</filter>
        </filters>
    </strings>
    <nameObfuscation>
        <filters>
            <filter>no_default</filter>
        </filters>
    </nameObfuscation>
    <androidManifestMangling/>
    <xamarinAssemblies/>
  </resourceEncryption>
  
<!-- RASP - ENVIRONMENT CHECKS -->

  <antiDebug>true</antiDebug>
  <antiEmulator>true</antiEmulator>
  <antiManualInstall>true</antiManualInstall>
  <antiMalware>true</antiMalware>
  <runtimeChecks/>

<!-- NETWORK SECURITY -->

  <publicKeyPinning src="no_default">
    <trace>0</trace>
    <actions>block, report</actions>
    <network-security-config>
      <domain-config>
          <domain includeSubdomains="false">no_default</domain>
          <pin-set expiration="no_default">
	     <pin digest="no_default"></pin>
          </pin-set>
      </domain-config>
    </network-security-config>
  </publicKeyPinning>
  <certificateTransparency>
	<trace>0</trace>
	<domain includeSubdomains="false">no_default</domain>
	<!-- <logFile>no_default</logFile> -->
  </certificateTransparency>

<!-- UI PROTECTION -->

  <uiProtection/>

<!-- THREAT REPORTING AND TELEMETRY (ALICE) -->

  <reportMonitoring>
    <apiKey>no_default</apiKey>
    <!-- <customFieldsUpdate>no_default</customFieldsUpdate> -->
    <trace>0</trace>
  </reportMonitoring>

<!-- AAR PARAMETERS -->

    <aar autoInit="on" autoInitAuthorities="${applicationId}" kotlinSupport="on" />
 
</dexprotector>
Build Settings Description and values

Verbose Logging

boolean

Element: verbose

Description: Enables/disables verbose logging for the DexProtector process.

Valid values: true; false. Default value: false

<verbose>false</verbose>

Code Optimization

boolean

Element: optimize

Description: Removes redundant metadata from the package that might otherwise affect performance and/or be exploited by bad actors

Valid values: true; false. Default value: false

<optimize>false</optimize>

Security Assessment

signingCertificateCompromised

signingCertificateWeakKey

dependencyCheck

Element: securityAssessment

Description: With Security Assessment enabled, the DexProtector process will fail if the signing certificate is known to be compromised, or recognized as weaker than recommended.

Valid values: true; false. Default value: false

Nested elements (<signingCertificateCompromised>, <signingCertificateWeakKey>, <dependencyCheck>)

Element (nested): signingCertificateCompromised

Description: If the signing certificate has been compromised, the DexProtector process will throw an error and the build will fail.

Valid values: warning; error; off. Default value: error

Element (nested): signingCertificateWeakKey

Description: If the signing key is weak (i.e. an RSA key that is <2048 bits), the DexProtector process will throw an error and the build will fail.

Valid values: warning; error; off. Default value: error

Element (nested): dependencyCheck

Description: Checks application dependencies for known vulnerabilities.

Valid values: warning; error; off. Default value: warning

<securityAssessment>
    <signingCertificateCompromised mode="error" />
    <signingCertificateWeakKey mode="error" />
    <dependencyCheck mode="warning" />
</securityAssessment>

ProGuard mapping file

string

Element: proguardMapFile

Description: Specifies the absolute path to ProGuard's mapping file. If you use ProGuard for name obfuscation, it is necessary to provide the path to ProGuard's mapping file so that DexProtector can locate classes for encryption.

Default value: no default value. Note: When using the DexProtector Gradle Plugin, the value for the proguardMapFile (if necessary) is set automatically.

<proguardMapFile>/Users/developer/project/proguard/mapping.txt</proguardMapFile>
Signing Description and Values

Signing Mode

string

Element: signMode

Description: Specifies options for app signing.

Valid values:

  • debug - signature is performed with a debug key, derived from ${USER_HOME}/.android/debug.keystore
  • release - signature is performed with the developer's key
  • google - signing mode to be used with Google Play App Signing (see Google Play App Signing)
  • amazon - signing mode to be used for Amazon Appstore (see Amazon App Signing)
  • none - no signing key. This is only appropriate in cases when signing will take place at a later stage, for example with a manufacturer’s or platform key for system apps.
<signMode>google</signMode>

Keystore information (for signMode == release; signMode == google; signMode == amazon)

Keystore

Keystore password

Key alias

Key password

Element: keystore

Description: Specifies the path to the keystore containing the relevant signing key

Default value: no default value. Note: With the DexProtector Gradle Plugin, the path is specified automatically.


Element: storepass

Description: Specifies the password to the keystore containing the relevant signing key

Default value: no default value. Note: With the DexProtector Gradle Plugin, the path is specified automatically.


Element: alias

Description: Specifies the alias string for the relevant signing key in your keystore

Default value: no default value. Note: With the DexProtector Gradle Plugin, the path is specified automatically.


Element: keypass

Description: Specifies the password for the signing key

Default value: no default value. Note: With the DexProtector Gradle Plugin, the path is specified automatically.


<keystore>/home/developer/example.keystore</keystore>
<storepass>examplestorepass</storepass>
<alias>examplealias</alias>
<keypass>examplekeypass</keypass>

Keystore information (for signMode == google)

SHA-256 Certificate Fingerprint

Legacy SHA-256 Certificate Fingerprint

Element: sha256CertificateFingerprint

Description: Specifies the SHA-256 Certificate Fingerprint corresponding to the Google Play App Signing Key used in Play app signing.

Default value: no default value. Note: with the DexProtector Gradle plugin, the value is derived automatically.

<signMode>google</signMode>
<keystore>/home/developer/example.keystore</keystore>
<storepass>examplestorepass</storepass>
<alias>examplealias</alias>
<keypass>examplekeypass</keypass
<sha256CertificateFingerprint>1B:5F:8B:D0:5A:A2:37:FC:D3:5F:AF:21:B7:F1:57:BD:E0:17:17:FA:6D:C3:35:FC:D5:AB:7A:2A:A5:70:33:2E</sha256CertificateFingerprint>

Element: legacySha256CertificateFingerprint

Description: If you publish your app to Google Play, you can upgrade the signing key for your published app through the Play Console—your new key is used to sign new installs and app updates, while your older app signing key is used to sign updates for users who installed your app before the key upgrade. If you have upgraded the signing key for your published app and are DexProtecting an update, you must specify both the SHA-256 Certificate Fingerprint corresponding to the new key, and the Legacy SHA-256 Certificate Fingerprint corresponding to the original key.

Default value: no default value.

Code Stripping Description and Values

Logging Call Stripping

Element: stripLogging

Description: Prevents unwanted log outputs by blocking the calling of methods with android.util.Log. The scale of verbosity (from least to most) is WTF, ERROR, WARNING, INFO, DEBUG, VERBOSE.

Valid values: wtf [strip android.util.Log.wtf(...)]; error [strip android.util.Log.e(...)]; warning [strip android.util.Log.w(...)]; info [strip android.util.Log.i(...)]; debug [strip android.util.Log.d(...)]; verbose [strip android.util.Log.v(...)]; all [strip all of the above]

<stripLogging>all</stripLogging>

Method Call Stripping

filters

Element: stripMethodCalls

Description: Blocks the calling of specified methods (for example, the method calls of third-party logging libraries)

Element (nested): filters (Only when mode != off):

Format: string

Default value: no default value

<stripMethodCalls>
        <filters>
            <filter>android.util.Log.println</filter>
        </filters>
</stripMethodCalls>
Code Protection Description and Values

String Encryption

filters

Element: stringEncryption

Description: Enables DexProtector's String Encryption mechanism. Note: We recommend encrypting as many strings as possible, but especially those containing sensitive data (logins, passwords, API credentials, keys, etc.). There is no need, on the other hand, to encrypt any strings contained in publicly available third party libraries, and DexProtector’s default configuration file contains certain example filters to exclude such libraries from the string encryption process.

Element (nested): filters

Format: string

Default value: no default value

<stringEncryption>
    <filters>
        <filter>glob:!**/**</filter>
        <filter>glob:com/test/**</filter>
    </filters>
</stringEncryption>

Annotation Encryption

filters

Element: annotationEncryption

Description: Encrypt Kotlin annotations to protect sensitive metadata. Important: Filters for Annotation Encryption work differently than for other mechanisms. Filters must target the classes in which annotations are defined, and not the classes in which annotations are referenced. DexProtector will then locate all instances of the annotations and encrypt them.

Element (nested): filters

Format: string

Default value: no default value

<annotationEncryption>
    <filters>
        <filter>glob:kotlin/Metadata.class</filter>
        <filter>your/own/secret/Annotations.class</filter>
        <filter>other/framework/secret/Annotations.class</filter>
    </filters>
</annotationEncryption>

JNI Obfuscation

filters

Element: jniObfuscation

Description: Enables the obfuscation of JNI (Java Native Interface) method names in native libraries and classes, in accordance with the specified filters. Note: When setting filters for JNI Obfuscation, you only need to specify the classes that contain JNI methods. DexProtector will then automatically process native libraries containing the JNI methods from those classes.

Element (nested): filters

Format: string

Default value: no default value

<jniObfuscation>
    <filters>
        <filter>glob:com/sample/NativeLibrary.class</filter>
    </filters>
</jniObfuscation>

Hide Access

filters

Element: hideAccess

Description: The Hide Access mechanism conceals method calls and field accesses in the packages and classes specified in the filters, breaking the link between the call site and the function being called.

Element (nested): filters

Format: string

Default value: no default value

<hideAccess>
    <filters>
        <filter>glob:!**/**</filter>
        <filter>glob:com/test/**</filter>
    </filters>
</hideAccess>

Class Encryption

filters

Element: classEncryption

Description: DexProtector encrypts entire classes.dex including Application, Activities, ContentProviders, and Receivers, in accordance with the specified filters. Note: DexProtector will encrypt all classes by default, without affecting the application’s performance. However, it is important to exclude any classes that must remain accessible to third parties, such as those that form part of a public API.

Element (nested): filters

Format: string

Default value: no default value

<classEncryption/>

Native Library Encryption

filters

Element: nativeLibraryEncryption

Description: DexProtector encrypts native libraries (.so files) within the target package, in accordance with the chosen filters.

Element (nested): filters

Format: string

Default value: no default value

<nativeLibraryEncryption>
        <filters>
            <filter>glob:libsample-jni.so</filter>
        </filters>
</nativeLibraryEncryption>
Resource Protection Description and Values

Resource Encryption

assets folder encryption

res folder encryption

resources.arsc string encryption

resource name obfuscation

root folder encryption

AndroidManifest mangling

Xamarin assemblies

Element: resourceEncryption

Description: Resource encryption protects against malicious copying, modification, and piracy by encrypting an application's internal resources; resource names; and, for cross-platform apps, HTML, JS, and CSS code. Filters can target the assets and res folders, as well as resources in the root folder, and individual string resources.


Nested elements (<assets>, <res>, <strings>, <nameObfuscation>,<root>, <androidManifestMangling>, <xamarinAssemblies>):

Element (nested): assets

Description: Encrypts files in assets/. Files can be targeted by file pattern (i.e. .png denotes all files of PNG file format), name pattern (i.e. File1 denotes all files whose names begin with the string "File1"), by specific file name (e.g. File2.json), or by path (e.g. TestDir/File3.txt). Note 1: If there are no assets/res elements in the configuration file under the resourceEncryption section, the encryption of the assets folder will be performed in accordance with DexProtector’s default settings. Note 2: Assets directly accessed by the OS must not be encrypted.

Format: contains nested elements

     Element (nested): filters

     Format: string

     Default value: no default value

Element (nested): res

Description: Encrypts files in res/. Files can be targeted by file pattern (i.e. **png denotes all files of PNG file format), name pattern (i.e. File1** denotes all files whose names begin with the string "File1"), by specific file name (e.g. File2.json), or by path (e.g. TestDir/File3.txt). Note 1: If there are no assets/res elements in the configuration file under the resourceEncryption section, the encryption of the res folder will be performed in accordance with the default filter. Note 2: Resources directly accessed by Android must not be encrypted.

Format: contains nested elements

     Element (nested): filters

     Format: string

     Default value: no default value

Element (nested): strings

Description: Encrypts strings and string arrays in resources.arsc. Note:  If there is no ‘strings’ element in the configuration file under the resourceEncryption section, the encryption of strings and string-arrays in resources.arsc will be performed in accordance with DexProtector’s default settings.

Format: contains nested elements

     Element (nested): filters

     Format: string

     Default value: no default value

Element (nested): nameObfuscation

Description: With Resource Name Obfuscation enabled, DexProtector will obfuscate names of names of files in res/. It is possible to set filters for Resource Name Obfuscation independently of the filters specified for res folder encryption. For more information, see the guide to filters for resource encryption.

Format: contains nested elements

     Element (nested): filters

     Format: string

     Default value: no default value

Element (nested): root

Description: Encrypts files in root/. Files can be targeted by file pattern (i.e. **png denotes all files of PNG file format), name pattern (i.e. File1** denotes all files whose names begin with the string "File1"), by specific file name (e.g. File2.json), or by path (e.g. TestDir/File3.txt).

Format: contains nested elements

     Element (nested): filters

     Format: string

     Default value: no default value

Element (nested): androidManifestMangling

Description: Mangling settings for AndroidManifest.xml. If the element androidManifestMangling is included in the configuration file, DexProtector will mangle entities in the AndroidManifest.xml file.

Element (nested): xamarinAssemblies

Description: Encrypts Xamarin assemblies

Attribute: dir

Description: If the assemblies are not in the default folder (assets/assemblies), you can set a path to them using the attribute dir. The path should start from the APK’s root, for example: assets/xxx/assemblies

Format: string

Default value: no default value

<resourceEncryption>
    <assets>
        <filters>
            <filter>glob:cert/**</filter>
        </filters>
    </assets>
    <res>
        <filters>
            <filter>glob:raw/**</filter>
        </filters>
    </res>
    <root>
        <filters>
            <filter>glob:fonts/**</filter>
        </filters>
    <strings>
        <filters>
            <filter>my_api_key</filter>
            <filter>glob:mobile_token*</filter>                  
            <filter>glob:payments_**</filter>
            <filter>glob:sensitive_strings_arrays_etc*</filter>
        </filters>
    </strings>
    <nameObfuscation/>
    <androidManifestMangling/>
    <xamarinAssemblies/>
  </resourceEncryption>
RASP (Runtime Application Self-Protection) - Environment & Runtime Checks Description and Values

Anti-Debug

Element: antiDebug

Description: With this setting enabled, the DexProtector Runtime Engine will close the app instantly if it detects that a debugger is attached.

Anti-Emulator

Element: antiEmulator

Description: With this setting enabled, the DexProtector Runtime Engine will prevent the app from running on an emulator. Note: Client-side API and callback options are available as an alternative to closing the app. Please request more details on configuration and implementation.

Anti-Manual Install

Element: antiManualInstall

Description: With this setting enabled, the DexProtector Runtime Engine will prevent the app from running if it was sideloaded. Note: Client-side API and callback options are available as an alternative to closing the app. Please request more details on configuration and implementation.

Anti-Malware

Element: antiMalware

Description: With this setting enabled, the DexProtector Runtime Engine will report malware and Potentially Harmful Apps detected on the device to the organization's Alice Threat Intelligence database. Note: <reportMonitoring> must be enabled. Client-side API and callback options are also available. Please request more details on configuration and implementation.

Runtime Checks

Element: runtimeChecks

Description: With this setting enabled, the DexProtector Runtime Engine will prevent the app from running on custom firmware and rooted devices.

Network Security Description and Values

Public Key Pinning

Element: publicKeyPinning

Description: Settings for SSL/HTTP Public Key Pinning. These can be specified either via a security configuration file in res/xml (in accordance with specification for Android N), or via the network-security-config nested element. If you have a separate security configuration file, you must set the path to it within the attribute ‘src’.

Attribute: src

Valid values: Path to a security configuration file (see details here).

Default value: no default value

Note: if the file exists, all public key pinning settings are taken from the file. On the other hand, if the src attribute is not set and there is a network-security-config tag, all the settings are taken from the configuration defined inside the tag and the res/xml/networksecurityconfig.xml will be created, it will contain settings from the network-security-config tag, and also the following information will be added into the AndroidManifest.xml:

<app>
	...
	<meta-data android:name="android.security.net.config"
           	android:resource="@xml/network_security_config"/>
	...
</app>

Format: contains nested elements (<actions>, <reportUri>, <reportMethod>, <network-security-config>)

Element (nested): actions

Description: Specifies the actions to be performed if there are errors or anomalies detected during the Public Key Pinning checks

Format: list with the ',' separator

Valid values: block - block the connection; report - send a report regarding the connection

Default value: block, report

<actions>block, report</actions>

Element (nested): reportUri

Description: Specifies the address that will be used to send JSON reports regarding any errors or anomalies detected during the Public Key Pinning checks

Format: string

Element (nested): reportMethod

Description: Specfies a method (in the format ClassName.methodName) to which JSON reports are passed in the event of any errors or anomalies detected during the Public Key Pinning checks. These methods should have public static modifier and (String jsonStr) signature.

Format: string

Element (nested): cacheTTL

Description: Time to live for a server SSL certificate chain check result for each domain

Format: int

Default value: 180

Element (nested): network-security-config

Description: Embedded Security Configuration File

Examples of embedded security configuration settings:

Example #1:

<network-security-config>
            <domain-config>
                <domain includeSubdomains="true">example.com</domain>
                    <pin-set expiration="2023-01-01">
                    <pin digest="SHA-256">7HIpactkIAq2Y49orFOOQKurWxmmSFZhBCoQYcRhJ3Y=</pin>
                    <!-- backup pin -->
                    <pin digest="SHA-256">fwza0LRMXouZHRC8Ei+4PyuldPDcf3UKgO/04cDM1oE=</pin>
                    </pin-set>
            </domain-config>
</network-security-config>

Example #2 (Security Configuration File in res/xml):

<publicKeyPinning src="res/xml/network_security_config.xml">
            <actions>block, report</actions>
            <reportMethod>com.sampleapp.MainActivity.pkpReport</reportMethod>
            <reportUri>http://example.com/pkp-report</reportUri>
</publicKeyPinning>

Certificate Transparency

Element: certificateTransparency

Description: Settings for monitoring public key certificates according to the Certificate Transparency standard. DexProtector uses a list of log servers that is located in the distribution package. This list is based on: https://source.chromium.org/chromium/chromium/src/+/master:components/certificate_transparency/data/log_list.json Alternatively, a list of authorized log servers can be specified manually by entering a path to a file containing that list.

Format: contains nested elements (<trace>, <logFile>)

Element (nested): trace

Format: string

Description: For debugging purposes, set trace to 1000.

Default value: no default value

Element (nested): logFile

Format: string

Description: Path to file containing your own list of authorized log servers.

Default value: no default value

<certificateTransparency mode="on">
	<trace>0</trace>
        <domain includeSubdomains="true">no-sct.badssl.com</domain>
	<logFile>/path_to_log_list_file</logFile>
</certificateTransparency>
UI Protection Description and Values

UI Protection

Element: uiProtection

Description: DexProtector’s UI Protection blocks screen capture and prevents activity hijacking. Screen capture blocking hardens your app against screenshots, screen recording, and screen casting. Hijacking prevention protects the app from malware that takes control of the application and replaces legitimate windows with impostors.

Threat Reporting and Telemetry - Alice Integration Description and Values

Threat Reporting

Element: reportMonitoring

Description: Configures Licel’s Real-Time Attack Telemetry and Threat Intelligence service, Alice. For more information, see our guide to Alice.

Format: contains nested elements (<apiKey>, <customFieldsUpdate>, <trace>)

Element (nested): apiKey

Format: string

Default value: no default value

Element (nested): customFieldsUpdate

Format: string

Description: Specifies a method, if one is implemented in the package’s code, for customizing the types of data reported to Alice by the monitoring system (e.g. user’s IP address; device serial number, etc.) . The method specified must have the following format:

java
public class AliceCustomizer {
 public static void updateCustomFields(HashMap values) {
 values.put("Login", "MyVeryNewLogin");
 values.put("Description", "Hello, Bonny");
 }
}

Default value: no default value, but the field must have the following structure:

xml
<![CDATA[com.dexprotector.detector.envchecks.AliceCustomizer.updateCustomFields]]>

Element (nested): trace

Format: string

Description: The logging level of DexProtector messages on the end device. For debugging purposes, set to 1000. Otherwise, set to 0 or exclude the <trace> node.

Default value: no default value

xml
<reportMonitoring>
    <apiKey>137feb09-f390-4f00-b43f-ebccf530adf6lt</apiKey>
    <customFieldsUpdate><![CDATA[com.dexprotector.detector.envchecks.AliceCustomizer.updateCustomFields]]></customFieldsUpdate>
    <trace>0</trace>
</reportMonitoring>
AAR Parameters Description and Values

AAR

Element: aar

Description: Enables the DexProtection of Android libraries & SDKs (AAR). Since DexProtector's runtime engine must start before the AAR is first accessed, it is necessary to configure the initialization process, and/or to leave DexProtector's auto-initialization mechanism enabled.

Attribute: autoInit

Valid values: on - enable DexProtector's automatic initialization mechanism; off - disable DexProtector's automatic initialization mechanism. If autoInit="off" you must specify either initMethod(s) and/or initClass(es).

Default value: on

Attribute: autoInitAuthorities

Description: To ensure that DexProtector's runtime engine is initialized when necessary, the autoInit mechanism uses a Content Provider approach, and defines a content provider component in the AndroidManifest.xml. Therefore, if autoInit="on" we strongly recommend to specify autoInitAuthorities="${applicationId}", additionally specifying a unique ID for each AAR where necessary, to avoid the risk of conflict whereby multiple content providers with the same name are specified in android:authorities.

Default value: ${applicationId}

Attribute: kotlinSupport

Valid values: on - enable support for libraries containing Kotlin code; off - disable support for libraries containing Kotlin code

Default value: off

Attribute: nativeClassEncryption

Valid values: on - enable the encryption of native classes within the library; off - disable the encryption of native classes within the library

Default value: on

Attribute: nativeHideAccess

Valid values: on - enable the Hide Access mechanism for native classes within the library; off - disable the Hide Access mechanism for native classes within the library

Default value: off

<aar autoInit="on" autoInitAuthorities="${applicationId}" kotlinSupport="off" mode="all" nativeClassEncryption="on" nativeHideAccess="off"/>

We strongly recommend leaving autoInit="on", but if you prefer to set autoInit="off" you must specify either initMethod(s) and/or initClass(es). For example:

<aar autoInit="off" kotlinSupport="off" nativeClassEncryption="on" nativeHideAccess="off">
    <initMethod>com.sample.Lib.initMethod</initMethod>
    <initClass>com.sample.MyReceiver</initClass>
</aar>

Filters: A guide to targeting code and resources

An important part of the configuration process is specifying which packages and classes will be targeted for protection. DexProtector therefore provides a flexible mechanism for setting custom filters for each of its Code Protection, Resource Protection, and Code Stripping mechanisms.

If the protection mechanisms are enabled but no filters are specified, DexProtector automatically protects all strings in all classes (String Encryption), all methods, fields, and field accesses in all classes (Class Encryption, Hide Access), and also encrypts resources (Resource Encryption). This offers maximum protection, and is worth trying during the testing phase, but can occasionally lead to inefficiencies in the application's performance. This is especially true of the Hide Access protection mechanism, when particularly demanding loops are involved. We therefore recommend using the filters to fine-tune your protection where necessary.

You can use ‘glob’ or ‘regex’ syntax modes to apply protections according to specified name patterns.

To set a filter to include a given package or class in the protection process, simply specify the syntax mode and the name pattern in the form: ‘syntax:pattern’, where ':' stands for itself.

To exclude a given package or class from protection, use an exclamation mark (!) before the name pattern, in the form: ‘syntax:!pattern’

The following are guides to filters configured for inclusion, exclusion, and both, using the glob syntax mode.

Inclusion

Protecting every class in a specified package

Say we want to configure a filter in order to specify which classes DexProtector should include in its Class Encryption process. In our classes.dex we have the following structure of packages:

'
    android/support/...
    com/google/..
    com/facebook/..
    com/mycompany/..
    com/mycompany/ui/..
    com/mycompany/core/..

In this case, we want DexProtector to encrypt all of the classes contained in the com/mycompany package. We should therefore set the following filter in our configuration file:

'
glob:com/mycompany/**

Protecting a single class

To protect a single class - for example com/mycompany/core/InternalEngine.class - we use the following filter format:

'  
glob:com/mycompany/core/InternalEngine.class

Exclusion

Excluding every class in a specified package from protection

Exclusion filters are set by adding the exclamation mark (!) before the class or package name pattern:

'  
    glob:!android/support/**
    glob:!com/google/**
    glob:!com/facebook/**

Excluding a single class from protection

Exclusion filters for classes are done in the same way, by adding the exclamation mark (!) before the classpath:

'  
glob:!com/mycompany/core/BumpGenerator.class

Mixed filters: Combining inclusion and exclusion

One possible (and recommended) approach for String Encryption and Hide Access is first to set a global exclusion filter, to exclude every class from protection, and then to target specific classes for inclusion. For Class Encryption, no problems should arise from encrypting every class in the package, except for any that must remain accessible to third parties, such as those that form part of a public API.

This would be a simple example configuration for String Encryption, Class Encryption, and Hide Access:

<dexprotector>

***

  <stringEncryption mode="filters">
      <filters>
        <filter>glob:!**/**</filter> <!-- This filter excludes everything. The purpose is to exclude all nonsensitive parts of your APK or AAB from String Encryption, as a first step before including the specific packages you intend to secure. -->
        <filter>glob:com/test/**</filter> <!-- This filter includes everything within the specified package. Replace the placeholder package name with the one that you intend to secure with string encryption. -->
      </filters>
  </stringEncryption>

  <classEncryption/> <!-- DexProtector will encrypt all classes by default, without affecting the application's performance. However, it is important to exclude any classes that must remain accessible to third parties, such as those that form part of a public API. -->

  <hideAccess mode="filters">
      <filters>
        <filter>glob:!**/**</filter> <!-- This filter excludes everything. The purpose is to exclude all nonsensitive parts of your APK or AAB from the Hide Access mechanism, as a first step before including the specific packages you intend to secure. -->
        <filter>glob:com/test/**</filter> <!-- This filter includes everything within the specified package. Replace the placeholder package name with the one that you intend to secure with the Hide Access mechanism. -->
      </filters>
  </hideAccess>

  ***

</dexprotector>

Detailed filter syntax information

Filters are determined by the syntax mode and the pattern and take the form: ‘syntax mode:pattern’, where ':' stands for itself. The value of the syntax component is not case-sensitive.

When the syntax mode is ‘glob’, the pattern string representation is matched to paths using a limited pattern language that resembles regular expressions but with a simpler syntax.

For example:

. -- Matches file names containing '.'

foo.? -- Matches file names starting with foo. and a single character extension.

com// -- Matches package /com/test/a.

com/** -- Matches all packages and classes inside com package.

  • The * character matches zero or more characters of a name component without crossing directory boundaries.
  • The ** characters matches zero or more characters crossing directory boundaries.
  • The ? character matches exactly one character of a name component.
  • The backslash (\) is used to prevent characters from being interpreted as special characters. For example, the expression \\ matches a single backslash and \{ matches a left brace.
  • The [ ] characters constitute a bracket expression that matches a single character of a name component out of a set of characters. For example, [abc] matches "a", "b", or "c". The hyphen (-) may be used to specify a range so [a-z] specifies a range that matches from "a" to "z" (inclusive). These forms can be mixed so [abce-g] matches "a", "b", "c", "e", "f" or "g". If the character after the [ is a ! then it is used for negation, i.e. [!a-c] matches any character except "a", "b", or "c". Within a bracket expression the *, ? and \ characters match themselves, and therefore in this context do not have the pattern-matching effects seen above. The - character matches itself if it is either the first character within the brackets or the first character after the ! when ! is used for negating.
  • The { } characters are a group of subpatterns, where the group matches if any subpattern in the group matches. The , character is used to separate the subpatterns. Groups cannot be nested.
  • Leading period/dot characters (.) in the file name are treated as regular characters in pattern-matching operations. For example, the * glob pattern matches file name ".login".
  • Placing the ! character at the beginning of the pattern component negates the pattern.

Additional notes on filters: Resource Encryption and Annotation Encryption

Filters for Resource Encryption

For Resource Encryption, broadly the same principles apply as for the other protection mechanisms, but files can also be targeted by file pattern (i.e. *.png denotes all files of PNG file format; DexProtector will locate and encrypt all files of that type in the folder), name pattern (i.e. File1* denotes all files whose names begin with the string "File1"), by specific file name (e.g. File2.json), or by path (e.g. TestDir/File3.txt). Here is an example configuration specifically for the assets folder:

'
 <resourceEncryption>
      <assets>
            <filters>
               <filter>*.png</filter>
              <filter>File1*</filter>
              <filter>File2.json</filter>
              <filter>TestDir/File3.txt</filter>
            </filters>
      </assets>
  </resourceEncryption>

And another, with broader filters, targeting the assets and res folders, and strings and string arrays specified in strings.xml in resources.arsc.

'
 <resourceEncryption nameObfuscation="on" webViewSupport="off">
	<assets>
  	  <filters>
    	    <filter>glob:cert/**</filter>
  	  </filters>
	</assets>
	<res>
  	  <filters>
    	    <filter>glob:raw/**</filter>
  	  </filters>
	</res>
	<strings>
  	  <filters>
    	    <filter>my_api_key</filter>
    	    <filter>glob:mobile_token*</filter>             	
    	    <filter>glob:payments_**</filter>
    	    <filter>glob:sensitive_strings_arrays_etc*</filter>
  	  </filters>
	</strings>
  </resourceEncryption>

Resource Name Obfuscation

It is possible to separate filters for resource encryption from filters for resource name obfuscation, if so desired. This can be configured as follows:

'
<resourceEncryption nameObfuscation="on/off">
    <res>
        <filters>
            <filter>res-filter-pattern-01</filter>
            <filter>res-filter-pattern-02</filter>
        </filters>
    </res>
    <nameObfuscation>
        <filters>
            <filter>res-name-01</filter>
            <filter>res-name-02</filter>
        </filters>
    </nameObfuscation>
</resourceEncryption>

And the following are the permutations for the <nameObfuscation> element:

attribute ⇩ \ filter ⇨ Empty filter Filters specified No filters specified

on

Obfuscate all names

As per filter

Obfuscate all names

off

No name obfuscation

No name obfuscation

No name obfuscation

No attribute specified

Obfuscate all names

As per filter

No name obfuscation

Filters for Annotation Encryption

Filters for Annotation Encryption work differently than for other mechanisms. Filters must target the classes in which annotations are defined, and not the classes in which annotations are referenced. DexProtector will then locate all instances of the annotations and encrypt them. For example:

'
  <annotationEncryption>
    <filters>
        <filter>glob:kotlin/Metadata.class</filter>
        <filter>your/own/secret/Annotations.class</filter>
        <filter>other/framework/secret/Annotations.class</filter>
    </filters>
  </annotationEncryption>

Configuring DexProtector for applications (APK & AAB)

DexProtector's protection mechanisms work equally with APKs and AABs, and every Android element listed in the Configuration File Overview, with the exception of those within the <aar> tag, can be applied to both APKs and AABs. There are, however, some important differences between DexProtecting an APK and DexProtecting an AAB.

Firstly, to DexProtect an AAB, it is required to use Android Studio 3.2 (or higher) and (if Gradle is used), Android Gradle Plugin 3.2.0 (or higher).

Secondly, due to Google Play's requirements for app bundles, it is necessary to use Google Play App Signing for AABs that are intended for release via Google Play. That means that in the DexProtector configuration, the <signMode> must be set to google. If you select google signing mode, you will also need to enter your upload key keystore information and the target certificate SHA-256 fingerprint. You can find the certificate fingerprint by going to the App Signing page of the Google Play Console: App Signing → App signing certificate → SHA-256 certificate fingerprint.

It is important to note that if your app is DexProtected with Google Play App Signing mode enabled, your app must be uploaded to and downloaded directly from Google Play (including for open, closed, or internal testing) in order to work. If Google Play Signing mode is selected in the DexProtector configuration, and the app is tested locally before being processed through Google Play, the app will be inoperable, and will crash on start-up.

If you do not wish to use Google Play App signing, but do wish to publish your app to the Google Play Store, then you should use release signing mode in DexProtector. For this, see the section on Local Signing for APKs and AABs. As of August 2021, any new app published to the Google Play Store must be in app bundle (AAB) format, and must be signed via Play app signing.

Example configuration for applications (APK & AAB)

<?xml version="1.0" encoding="utf-8" standalone="no"?>

<dexprotector>

<!-- BUILD SETTINGS -->

  <verbose>true</verbose>
  <optimize>false</optimize>
  <securityAssessment>
    <signingCertificateCompromised mode="error" />
    <signingCertificateWeakKey mode="error" />
    <dependencyCheck mode="warning" />
  </securityAssessment>
  <proguardMapFile>/Users/developer/project/proguard/mapping.txt</proguardMapFile>

<!-- SIGNING -->
 
  <signMode>google</signMode>
  <keystore>/home/developer/example.keystore</keystore>
  <storepass>examplestorepass</storepass>
  <alias>examplealias</alias>
  <keypass>examplekeypass</keypass>
<sha256CertificateFingerprint>1B:5F:8B:D0:5A:A2:37:FC:D3:5F:AF:21:B7:F1:57:BD:E0:17:17:FA:6D:C3:35:FC:D5:AB:7A:2A:A5:70:33:2E</sha256CertificateFingerprint>
  
<!-- CODE STRIPPING -->

  <stripLogging>all</stripLogging>

  <stripMethodCalls>
    <filters>
        <filter>android.util.Log.println</filter>
    </filters>
  </stripMethodCalls>

<!-- CODE PROTECTION -->

  <stringEncryption/>

  <classEncryption/>

  <hideAccess/>

  <annotationEncryption/>
  
  <nativeLibraryEncryption/>

  <jniObfuscation/>

<!-- RESOURCE PROTECTION -->

  <resourceEncryption nameObfuscation="on" webViewSupport="on" idObfuscationMode="auto">
    <assets>
        <filters>
            <filter>glob:cert/**</filter>
        </filters>
    </assets>
    <res>
        <filters>
            <filter>glob:raw/**</filter>
        </filters>
    </res>
    <strings>
        <filters>
            <filter>my_api_key</filter>
            <filter>glob:mobile_token*</filter>                  
            <filter>glob:payments_**</filter>
            <filter>glob:sensitive_strings_arrays_etc*</filter>
        </filters>
    </strings>
    <androidManifestMangling/>
  </resourceEncryption>
  
<!-- RASP - RUNTIME & ENVIRONMENT CHECKS -->

  <runtimeChecks/> <!-- From DexProtector 12.5, it is no longer necessary to implement your own probe and callback methods. The <detect> element is deprecated. -->

<!-- NETWORK SECURITY -->

  <publicKeyPinning>
    <trace>0</trace>
    <actions> block<!--, report--></actions>
    <network-security-config>
          <domain-config>
          <domain includeSubdomains="true">onlinebanking.test.com</domain>
            <pin-set expiration="2022-10-19">
				<pin digest="SHA-256">h/QE8u3I70bpVEqNV43sttidMcKKt2wSt+j1RKJ9PKk=</pin>
			</pin-set>
      </domain-config>
    </network-security-config>
  </publicKeyPinning>
  <certificateTransparency>
	<trace>0</trace>
	<domain includeSubdomains="false">no-sct.badssl.com</domain>
	<!-- <logFile>path_to_custom_log_list_file</logFile> -->
  </certificateTransparency>

<!-- THREAT REPORTING AND TELEMETRY (ALICE) -->

  <reportMonitoring>
    <apiKey>137feb09-f390-4f00-b43f-ebccf530adf6lt</apiKey>
    <!-- <customFieldsUpdate><com.dexprotector.detector.envchecks.AliceCustomizer.update'CustomFields></customFieldsUpdate> -->
    <trace>0</trace>
  </reportMonitoring>
 
</dexprotector>

Configuring DexProtector for libraries and SDKs (AAR)

Most of DexProtector’s protection mechanisms can also be applied to Android libraries (AARs), but there are some differences, and certain features must be configured differently from how they are configured for APKs and AABs.

The most important differences between configuring DexProtecting an application package and DexProtecting an AAR concern:

  • Initialization
  • Kotlin
  • Protection of Public APIs

Initialization

Since DexProtector's runtime engine must start before the AAR is first accessed, it is necessary to configure the initialization process, and/or to leave DexProtector's auto-initialization mechanism enabled.

To ensure that DexProtector's runtime engine is initialized when necessary, the autoInit mechanism uses a Content Provider approach, and defines a content provider component in the AndroidManifest.xml. Therefore, if autoInit="on" we strongly recommend to specify autoInitAuthorities="${applicationId}", additionally specifying a unique ID for each AAR where necessary, to avoid the risk of conflict whereby multiple content providers with the same name are specified in android:authorities.

<aar autoInit="on" autoInitAuthorities="${applicationId}" />

We strongly recommend to leave autoInit="on", but if you prefer to set autoInit="off" you must specify either initMethod(s) and/or initClass(es). For example:

<aar autoInit="off" >
    <initMethod>com.sample.Lib.initMethod</initMethod>
    <initClass>com.sample.MyReceiver</initClass>
</aar>

Kotlin

If your AAR contains Kotlin classes, this needs to be specified in the configuration file (whereas it does not for APKs). The reason is that AARs are compiled to Java bytecode rather than Dalvik bytecode, so must be processed differently by DexProtector's native engines. Simply specify the kotlinSupport attribute within the <aar> element, as follows:

<aar kotlinSupport="on" />

Protection of Public APIs

It is vital when protecting AARs to specify filters for Class Encryption in such a way that public API classes are not encrypted.

Example configuration for libraries & SDKs (AAR)

<?xml version="1.0" encoding="utf-8" standalone="no"?>

<dexprotector>

<!-- BUILD SETTINGS -->

  <verbose>false</verbose>
  <optimize>false</optimize>
  <proguardMapFile>/Users/developer/project/proguard/mapping.txt</proguardMapFile>

<!-- AAR PARAMETERS -->

  <aar autoInit="on" autoInitAuthorities="${applicationId}" kotlinSupport="on" />
  
<!-- CODE STRIPPING -->

  <stripLogging>all</stripLogging>

  <stripMethodCalls>
    <filters>
        <filter>android.util.Log.println</filter>
    </filters>
  </stripMethodCalls>

<!-- CODE PROTECTION -->

  <stringEncryption>
    <filters>
        <filter>glob:!**/**</filter>
        <filter>glob:com/test/**</filter>
    </filters>
  </stringEncryption>

  <classEncryption/>

  <hideAccess>
    <filters>
        <filter>glob:!**/**</filter>
        <filter>glob:com/test/**</filter>
    </filters>
  </hideAccess>
  
  <nativeLibraryEncryption>
    <filters>
        <filter>libsome.so</filter>
    </filters>
  </nativeLibraryEncryption>

  <jniObfuscation>
    <filters>
        <filter>glob:com/sample/NativeLibrary.class</filter>
    </filters>
  </jniObfuscation>

<!-- RESOURCE PROTECTION -->

  <resourceEncryption>
    <assets>
        <filters>
            <filter>glob:cert/**</filter>
        </filters>
    </assets>
  </resourceEncryption>
  
<!-- RASP - RUNTIME & ENVIRONMENT CHECKS -->

  <runtimeChecks/> <!-- From DexProtector 12.5, it is no longer necessary to implement your own probe and callback methods. The <detect> element is deprecated. -->

<!-- NETWORK SECURITY -->

  <publicKeyPinning>
    <trace>0</trace>
    <actions> block<!--, report--></actions>
    <network-security-config>
          <domain-config>
          <domain includeSubdomains="true">onlinebanking.test.com</domain>
            <pin-set expiration="2022-10-19">
				<pin digest="SHA-256">h/QE8u3I70bpVEqNV43sttidMcKKt2wSt+j1RKJ9PKk=</pin>
			</pin-set>
      </domain-config>
    </network-security-config>
  </publicKeyPinning>
  <certificateTransparency>
	<trace>0</trace>
	<domain includeSubdomains="false">no-sct.badssl.com</domain>
	<!-- <logFile>path_to_custom_log_list_file</logFile> -->
  </certificateTransparency>

<!-- THREAT REPORTING AND TELEMETRY (ALICE) -->

  <reportMonitoring>
    <apiKey>137feb09-f390-4f00-b43f-ebccf530adf6lt</apiKey>
    <!-- <customFieldsUpdate><com.dexprotector.detector.envchecks.AliceCustomizer.update'CustomFields></customFieldsUpdate> -->
    <trace>0</trace>
  </reportMonitoring>
 
</dexprotector>

App Signing

Signing your app is a vital part of making it secure against tampering and cloning. For the strongest possible anti-tampering protection and integrity control, DexProtector uses a range of parameters to build a cryptographic chain of trust, and an app’s signing certificate information is an important link in that chain.

As a result, if a bad actor tries to re-sign a DexProtected application, the cryptographic chain of trust will be broken and the app will simply stop working. Every new version of your app must therefore be re-protected using DexProtector, and re-signed before release.

DexProtector checks the strength and integrity of the signing certificate as part of its Security Assessment; we strongly recommend a signing certificate with a key strength of at least 2048 bits, and DexProtector is set by default so that an app with a weaker or compromised signing certificate will not be processed, leading to an error message.

Your choice of signing mode will depend on your stage in the build cycle, and whether you are protecting an APK, an AAB, or an AAR. DexProtector therefore supports various signing modes, including both Google Play App Signing and Amazon Appstore Signing, either of which can be entered as the signMode in the DexProtector configuration file, or selected via DexProtector Studio. The signMode value can be set as google, amazon, debug, release, or none. See below for guides to each signing mode.

Cloud Signing (Google Play App Signing; Amazon Appstore)

Google Play App Signing

You will need to use the google signing mode if your app is or will be published on the Google Play Store and you use app signing by Google Play, with Google managing your app signing key and using it to sign your APKs when they are distributed.

If you select google signing mode, you will also need to enter your upload key and the certificate fingerprint of the target certificate’s SHA-256. You can find the certificate fingerprint by going to the App Signing page of the Google Play Console: App Signing -> App signing certificate -> SHA-256 certificate fingerprint.

It is important to note that with Google Play App Signing, you must use google signing mode in DexProtector, and once an app has been protected using google sign mode, it will only become functional when it has been uploaded to and downloaded directly from Google Play. This is because the app must be signed on Google servers with the expected App Signing Key, corresponding to the SHA-256 Certificate Fingerprint specified in the DexProtector configuration. If you try to run the app locally or with a testing service without first uploading it to and downloading it from Google Play, it will crash. If you wish to test an app that has been DexProtected with google signing mode, you must do so via the Play Console.

To configure google signing mode, follow these instructions:

  • In the configuration file (dexprotector.xml), enter google as the signMode value
<signMode>google</signMode>
  • Add the SHA-256 Certificate Fingerprint for the App Signing Key you have previously generated for this app through Google Play:
<sha256CertificateFingerprint>PUT_YOUR_VALUE_HERE</sha256CertificateFingerprint>
  • Enter the keystore-related information to the configuration file (if you use the DexProtector Gradle plugin, this information will be taken automatically from the signingConfig).
    • keystore - The path to the keystore containing the Upload Key used to generate the App Signing Key
    • storepass - password for the keystore
    • alias - key alias
    • keypass - password for the key

For example:

'
        <dexprotector>
        ...
        ...
            <signMode>google</signMode>
            <keystore>/Users/developer/keystores/upload.keystore</keystore>
            <storepass>android</storepass>
            <alias>android</alias>
            <keypass>android</keypass>
            <sha256CertificateFingerprint>1B:5F:8B:D0:5A:A2:37:FC:D3:5F:AF:21:B7:F1:57:BD:E0:17:17:FA:6D:C3:35:FC:D5:AB:7A:2A:A5:70:33:2E</sha256CertificateFingerprint>        
        ...
        ...
        </dexprotector>

And if you use Gradle, please be sure to add a new buildType/productFlavor specifically for Google Play publication, as it will not work for other stores, or for direct distribution.

Amazon Appstore Signing

You will need to use the amazon signing mode if your app is or will be published on the Amazon Appstore.

When you submit your app, Amazon removes the signature you used to sign your app and re-signs it with an Amazon signature that is unique to you, does not change, and is the same for all apps in your account.

Therefore, if you select amazon signing mode, you will also need to enter the target certificate’s SHA-256 fingerprint. This hash can be found in the Appstore Certificate Hashes section of the Amazon Developer Console.

It is important to note that with Amazon Appstore Signing, you must use amazon signing mode in DexProtector, and once an app has been protected using amazon sign mode, it will only become functional when it has been uploaded to and downloaded directly from Amazon Appstore. This is because the app must be signed on Amazon servers with the expected Amazon signature, corresponding to the SHA-256 Certificate Fingerprint specified in the DexProtector configuration. If you try to run the app locally or with a testing service without first uploading it to and downloading it from the Amazon Appstore, it will crash.

To configure amazon signing mode, follow these instructions:

  • In the configuration file (dexprotector.xml), enter amazon as the signMode value
<signMode>amazon</signMode>
  • Add the SHA-256 Certificate Fingerprint for the Amazon signature:
<sha256CertificateFingerprint>PUT_YOUR_VALUE_HERE</sha256CertificateFingerprint>

For example:

'
        <dexprotector>
        ...
        ...
            <signMode>amazon</signMode>
            <sha256CertificateFingerprint>1B:5F:8B:D0:5A:A2:37:FC:D3:5F:AF:21:B7:F1:57:BD:E0:17:17:FA:6D:C3:35:FC:D5:AB:7A:2A:A5:70:33:2E</sha256CertificateFingerprint>        
        ...
        ...
        </dexprotector>

And if you use Gradle, please be sure to add a new buildType/productFlavor specifically for Amazon publication, as it will not work for other stores, or for direct distribution.

Signing for System Apps and Pre-Installed Apps

For system apps and pre-installed apps, for which app signing is managed by the platform or vendor, the signing mode depends on whether you have access to the platform signing key.

If you have access to the key, then set <signMode>release</signMode> and complete all of the other signing-related tags as you would for your own private key:

'
    <signMode>release</signMode>
    <keystore>/Users/developer/keystores/production.keystore</keystore>
    <storepass>android</storepass>
    <alias>android</alias>
    <keypass>android</keypass>

If you do not have access to the key, then set <signMode>none</signMode> and, after asking for the signing certificate file from the platform vendor, add the path to the certificate using the <certificate> tag as follows:

'
    <signMode>none</signMode>
    <certificate>/home/developer/vendor.cert</certificate>

Local Signing for APKs

If you wish to sign an APK locally (either because it is intended for direct distribution, or because it is an update for an older app), and are therefore not using Google Play App Signing and not submitting the app to the Amazon Appstore, simply select <signMode>release</signMode> and also specify the keystore, keystore password, key alias, and key password:

'
    <signMode>release</signMode>
    <keystore>/Users/developer/keystores/production.keystore</keystore>
    <storepass>android</storepass>
    <alias>android</alias>
    <keypass>android</keypass>