When a configuration is created using the OneSpan Customer Portal, it is possible to shield an application. You can bind your application to App Shielding via the OneSpan Customer Portal either manually or automated.

App Shielding writes the shielded application to shielded-app.apk in the same directory as the application itself.

Manual shielding

To manually shield an app

  1. In the OneSpan Customer Portal, on the Configuration Page, select the configuration you want to use.

    OneSpan Customer Portal – iOS configuration page

    OneSpan Customer PortalAndroid configuration page

  2. Upload the binary to be shielded.
  3. When uploaded, click Start Shielding to start the shielding process.

    The binary to shield (i.e. APK for Android) can be signed or unsigned.

  4. When the shielding process is completed, download the shielded binary.

    Shielding modifies the executable of the application, which means that after shielding, the application needs to be re-signed.

    This must be handled with special care so that all required parts of the application are re-signed.

After shielding, application startup performance could be impacted. If this occurs, exclusion options can be defined to reduce the impact of the shielding on the application.

The security provided by App Shielding is reduced when you exclude classes from the shielding process. The number of shieldings must be high enough to ensure security requirements (more than one thousand data shieldings).

To avoid performance issues, the following OneSpan packages can be excluded from shielding:

  • untouchable class com.vasco.digipass.sdk.obfuscated.*
  • untouchable class com.vasco.digipass.sdk.utils.utilities.obfuscated.*

Automated shielding

REST web services can be used to automate the shielding process. For more information about the REST web services, refer to the API documentation.

To start the shielding process

  • Call POST /public_api/v1/rasp/bind_package.

    Input parameters:

    • An API key
    • The binary to be shielded.

    It starts the shielding process on the provided binary, and returns an identifier for that shielding process.

To obtain the shielding status

  • Call GET /public_api/v1/rasp/status.

    Input parameter:

    • An API key

    It returns the statuses of all the started and finished processes for the given configuration (i.e. API key) within the last two hours.

To download the shielded binary

App obfuscation

App Shielding provides app obfuscation mechanisms where the Java bytecode of an application is obfuscated, and e.g. class names, function names, and field names are modified. Parts of an application code are rewritten in a purposefully unintelligible way, while the application functionalities are left unchanged. The purpose of obfuscation is to discourage static analysis attempts.

If an attacker attempts a static analysis of the code by decompiling the app, the previously sequential instructions with telling variable names and significant values, should, after obfuscation, look like a seemingly random set of instructions. This can in turn prevent or at least slow down any attack whose success requires some knowledge of the underlying code – some forms of repackaging, code hooking, code injections etc. App Shielding offers the possibility to obfuscate the Java bytecode part of an Android APK or AAB application format.

You can obfuscate the application either with App Shielding alone, or use it in combination with the Android built-in minification and resource shrinking options. These options can be enabled in the app's gradle file. For more information about Android options for shrinking and obfuscation, refer to the Android Studio documentation.

Enabling the App Shielding obfuscation feature in combination with Android obfuscation options results in a greater obfuscation depth of your code.

Obfuscation of App Shielding

The design of the native App Shielding libraries makes it difficult to analyze, as it uses several layers of obfuscation, employing multiple techniques. This is done primarily to make it harder to partially or completely remove or disable App Shielding.

Features such as shielding, where parts of the application data are removed from the code, depend on App Shielding being hard to disassemble and analyze to obtain encryption keys or other information. The shielding mechanism tries to force the attacker to break App Shielding (or the cryptography).

Enable obfuscation with default rules

The Shielding Tool comes with a number of default rule files. These are loaded automatically, according to the enabled options. For instance, to enable obfuscation, you can pass a rules file to the Shielding Tool with the command line option --rules <file> with the following contents:

include "builtin:obfuscate-on.cfg"

This loads the built-in rules file builtin:obfuscate-on.cfg, which enables class name obfuscation for all classes, and then loads another built-in rules file, builtin:default-unobfuscate.cfg, which includes rules to not obfuscate some well-known exceptions.

To load the built-in rules file via the configuration settings of the OneSpan Customer Portal or OneSpan Mobile Portal, you have the following two options:

  1. Enable Rules.cfg, and copy the include "builtin:obfuscate-on.cfg" command into the input field.
  2. Enable Default Obfuscate.

Enable obfuscation with customized rules

You can customize the settings for obfuscation by defining rules in the Rules.cfg file. This determines how App Shielding will modify the Android application, especially in the context of shielding and obfuscation (see Configuration of Shielding Tool rules).

 

obfuscate class com.example.app.*; # Obfuscate all classes within com.example.app package.

Customize obfuscation rules is not a mere ON / OFF feature. The rules must be configured manually (see the table with the class_operation options in Configuration of Shielding Tool rules). The effort to fine-tune the configuration is comparable to the one required to fine-tune similar commercially available obfuscation tools.

Obfuscation techniques

App Shielding offers three types of obfuscation techniques:

Default obfuscation techniques

App Shielding employs these obfuscation techniques with any Java bytecode that is not implicitly or explicitly excluded from obfuscation. By default, App Shielding provides the implicit obfuscation rule. The explicit obfuscation rule is the sum of all rule lines that have been explicitly set in the configuration file, using the preserve and obfuscate keywords.

You can either apply none of these by implicitly or explicitly excluding the relevant piece of code, or apply all of them by targeting the relevant piece of code (or by not excluding it, either implicitly or explicitly).

The default obfuscation techniques are:

  • Class, method, and field name randomization

    Classes, methods, and field names are replaced with random, meaningless, and often shorter values. All references and resources are also updated accordingly.

  • Namespace flattening

    All classes will be moved into one big flat namespace which removes information about logical class grouping.

  • Code shuffling

    Code that belongs in one class can be moved to another class, and all references to this code can be updated accordingly to the relocated piece of code. This creates new application-internal code dependencies which are confusing to an attacker but also act as an internal binding mechanism that makes it harder to remove certain parts of the code tree.

Obfuscation on demand: debug information removal

If you use this technique, obfuscation is only applied if a specific keyword is added as an rule line in the App Shielding configuration.

Debug information such as parameter type information, source file and line number information can be removed.

  • removeDebug / keepDebug
  • removeSourceFile / keepSourceFile
  • removeLines / keepLines

Java annotations can be removed with the removeAnnotation / keepAnnotation keywords.

This may break some uses of Java Generics.

Obfuscation as a side-effect

To enable your app to protect itself, App Shielding decompiles, modifies, and recompiles the app. Some of the modifications performed on the app also obfuscate the app code: the mandatory obfuscation and the binding and string scrambling mechanisms.

  • Mandatory obfuscation

    Java bytecode related to App Shielding will be obfuscated, whether obfuscation is enabled or disabled on the configuration level. This is why every shielded app is retrieved with a mapping.txt file. This file contains the mapping of original names to obfuscated names. Also, it is useful for de-obfuscating the traces from the app and should therefore be preserved.

  • Binding and string scrambling

    The main purpose of the binding and string scrambling mechanisms is to prevent an attacker from removing the App Shielding libraries and configurations from the shielded app. A side-effect of these mechanisms happens to be code obfuscation: where previously there was a plain text right-hand string value, there is now a piece of code. See Comparison of code after applying obfuscation as a side-effect for a comparison of the string value and the pieces of code after binding and string scrambling.

Comparison of code after applying obfuscation as a side-effect
Original code After Binding After String Scrambling
const-string v0, "ABC"

const-string v1, "DEF"

const-string v2, "GHI"

const-class v0, Lcom/onespan/shieldenum/ObfTest;

const v1, 0x15a

invoke-static {v0, v1}, Ljt/d;->a(Ljava/lang/Class;I)V

const v0, 0x7fb

invoke-static {v0}, Ljt/d;->a(I)Ljava/lang/String;

move-result-object v0

const v1, 0x7fc

invoke-static {v1}, Ljt/d;->a(I)Ljava/lang/String;

move-result-object v1

const v2, 0x7fd

invoke-static {v2}, Ljt/d;->a(I)Ljava/lang/String;

move-result-object v2

const/16 v4, 0x3

new-array v3, v4, [C

const/16 v5, 0x38ff

xor-int/lit16 v5, v5, 0x38bd

int-to-char v5, v5

const v4, 0x1

aput-char v5, v3, v4

const v4, 0x1

aget-char v5, v3, v4

xor-int/lit16 v5, v5, 0x1

int-to-char v5, v5

const v4, 0x2

aput-char v5, v3, v4

const v4, 0x1

aget-char v5, v3, v4

xor-int/lit16 v5, v5, 0x3

int-to-char v5, v5

const v4, 0x0

aput-char v5, v3, v4

new-instance v5, Ljava/lang/String;

invoke-direct {v5, v3}, Ljava/lang/String;-><init>([C)V

invoke-virtual {v5}, Ljava/lang/String; ->intern()Ljava/lang/String;

move-result-object v0

const/16 v4, 0x3

new-array v3, v4, [C

const/16 v5,0x3867

xor-int/lit16 v5, v5, 0x3822

int-to-char v5, v5

const v4, 0x1

aput-char v5, v3, v4

const v4, 0x1

aget-char v5, v3, v4

xor-int/lit16 v5, v5, 0x1

int-to-char v5, v5

const v4, 0x0

aput-char v5, v3, v4

const v4, 0x0

aget-char v5, v3, v4

xor-int/lit16 v5, v5, 0x2

int-to-char v5, v5

const v4, 0x2

aput-char v5, v3, v4

new-instance v5, Ljava/lang/String;

invoke-direct {v5, v3}, Ljava/lang/String;-><init>([C)V

invoke-virtual {v5}, Ljava/lang/String;->intern()Ljava/lang/String;

move-result-object v1

const/16 v4, 0x3

new-array v3, v4, [C

const/16 v5, 0x7f17

xor-int/lit16 v5, v5, 0x7f5f

int-to-char v5, v5

const v4, 0x1

aput-char v5, v3, v4

const v4, 0x1

aget-char v5, v3, v4

xor-int/lit16 v5, v5, 0x1

int-to-char v5, v5

const v4, 0x2

aput-char v5, v3, v4

const v4, 0x1

aget-char v5, v3, v4

xor-int/lit16 v5, v5, 0xf

int-to-char v5, v5

const v4, 0x0

aput-char v5, v3, v4

new-instance v5, Ljava/lang/String;

invoke-direct {v5, v3}, Ljava/lang/String;-><init>([C)V

invoke-virtual {v5}, Ljava/lang/String;->intern()Ljava/lang/String;

move-result-object v2

The size of the obfuscated code does not necessarily mean it is safer or slower to execute. The String Scrambling mechanism leaves a longer code but is actually less safe and more performant than the Binding mechanism. Notice also the difference between the Binding and String Scrambling mechanisms and the default obfuscation techniques described above. The Binding mechanism affects the right-hand side of a string value while the String Scrambling essentially affects labels (classes, methods, fields etc.). Both are useful and contribute to strengthen the app against static analysis.

For more information about configuring rules, see Configuration of Shielding Tool rules.

When a configuration is created using the OneSpan Customer Portal, it is possible to shield an application. You can bind your application to App Shielding via the OneSpan Customer Portal either manually or automated.

Manual shielding

To manually shield an app

  1. In the OneSpan Customer Portal, on the Configuration Page, select the configuration you want to use.

    OneSpan Customer Portal – Android configuration page

    OneSpan Customer PortaliOS configuration page

  2. Upload the binary to be shielded.
  3. When uploaded, click Start Shielding to start the shielding process.

    The binary to shield (i.e. IPA for iOS) can be signed or unsigned.

  4. When the shielding process is completed, download the shielded binary.

    Shielding modifies the executable of the application, which means that after shielding, the application needs to be re-signed.

    This must be handled with special care so that all required parts of the application are re-signed. Ensure that the correct provisioning profile is copied, and the correct entitlements are used.

Automated shielding

REST web services can be used to automate the shielding process. For more information about the REST web services, refer to the API documentation.

To start the shielding process

  • Call POST /public_api/v1/rasp/bind_package.

    Input parameters:

    • An API key
    • The binary to be shielded.

    It starts the shielding process on the provided binary, and returns an identifier for that shielding process.

To obtain the shielding status

  • Call GET /public_api/v1/rasp/status.

    Input parameter:

    • An API key

    It returns the statuses of all the started and finished processes for the given configuration (i.e. API key) within the last two hours.

To download the shielded binary