Security of saving secret keys in Android (increases decompilation difficulty)

In this article, we will explain a problem that may be encountered in the development of an Android product: how to save static keys in the App and ensure its security. Many mobile apps need to save some static string constants on the app side, which may be static keys, third-party appIds, etc. When saving these string constants, it involves how to ensure the security of the secret key. How to ensure that the static secret key in the App is unique, correct and secure is a very important issue. There are static string constant type secret keys in the company's products, so an obvious question is how to generate the secret key and ensure that the secret key is unique. Key security?

Several mainstream common practices for saving static keys today: (Reference: Android Security Development: A Brief Talk on Key Hardcoding)

•Save static keys through SharedPreferences;

•Saved via Java hardcoding

•Save the static key in the so file through NDK;

Advantages and disadvantages of several ways to save static keys:

•The key is stored directly in the sharedprefs file in clear text, which is the most insecure.

•The key is hardcoded directly in the Java code, which is very insecure, and the dex file can easily be reversed into Java code.

•Divide the key into different segments, some are stored in files, some are stored in code, and finally spliced ​​together, the whole operation can be written very complicated. This is because it is still at the Java layer, and the reverser only needs to spend With a little time, it can be easily reversed.

•Developed with ndk, the key is placed in the so file, and the encryption and decryption operations are all in the so file. This improves security to a certain extent and blocks some reversers, but experienced reversers will still use IDA to crack of.

•The key is not stored in the so file. The key is encrypted and decrypted in the so file. The encrypted key is named as another ordinary file, stored in the assets directory or other directories, and then in the so file. Although adding irrelevant code (flower instructions) can increase the difficulty of static analysis, dynamic debugging methods can be used to track the encryption and decryption functions, and the key content can also be found.

It can be said that there is basically no solution to securely storing keys on the device, and the only option is to increase the reverse cost. For ordinary developers, this requires a lot of effort. You must evaluate the importance of your app application and choose the corresponding technical solution.

The secret key that needs to be saved in the product App:

Since the app needs to interact with the server, if the user logs in at this time, the client needs a default secret key to confirm the identity of the App's visitor, so a default request secret key needs to be saved in the app.

•When the user is not logged in or logs out, request the server to use the default secret key string;

• Use the slave server or its secret key string when the user logs in;

In this way, we need to save a default secret key string on the App side to identify the user's visitor identity. One question is how to ensure the security of the secret key string?

Several other ways to save keys to consider:

•Save secret key information by saving files;

•Save secret key information through database;

•Save the secret key information by configuring gradle;

•Save the secret key information by configuring string.xml;

File method: It is obvious to save the secret key information by saving the file. One problem is that if the user maliciously deletes the secret key information saved by the App, then the App will not be able to use the default secret key information, so the uniqueness of the secret key will also be lost. There is no guarantee.

Database method: Just like saving the key information through files, saving through the database cannot guarantee that malicious users will delete the database information, so our App will lose the default key information.

Gradle configuration: We will explain how to configure variable information in gradle through the following gradle configuration variables.

string.xml: We will introduce it in detail below.

Configure variables through gradle:

In Android gradle, we can not only reference dependent packages, but also configure static variables when executing scripts:

buildTypes {

debug {
    // 显示Log
    buildConfigField "boolean", "LOG_DEBUG", "true"
    buildConfigField "String", "appKeyPre", "\"xxx\""
    //混淆
    minifyEnabled false
    //Zipalign优化
    zipAlignEnabled true
    // 移除无用的resource文件
    shrinkResources true
    //加载默认混淆配置文件
    proguardFiles getDefaultProguardFile('proguard-Android.txt'), 'proguard-rules.pro'
    //签名
    signingConfig signingConfigs.debug
}
release {
    // 不显示Log
    buildConfigField "boolean", "LOG_DEBUG", "false"
    buildConfigField "String", "appKeyPre", "\"xxx\""
    //混淆
    minifyEnabled true
    //Zipalign优化
    zipAlignEnabled true
    // 移除无用的resource文件
    shrinkResources true
    //加载默认混淆配置文件
    proguardFiles getDefaultProguardFile('proguard-Android.txt'), 'proguard-rules.pro'
    //签名
    signingConfig signingConfigs.relealse
}

}
As shown in the above code, we configured a string variable named appKey in gradle. Compiling gradle will generate this static variable in gradle's compiled class: BuildConfig:

/**

Compiled classes generated after gradle compilation
*/

public final class BuildConfig {
public static final boolean DEBUG = Boolean.parseBoolean(“true”);
public static final String APPLICATION_ID = “com.sample.renter”;
public static final String BUILD_TYPE = “debug”;
public static final String FLAVOR = “internal”;
public static final int VERSION_CODE = 1;
public static final String VERSION_NAME = “1.0.0”;
// Fields from build type: debug
public static final boolean LOG_DEBUG = true;
public static final String appKey = “xxx”;
}

It can be found that the static key can be found when decompiling by configuring gradle, but it increases the difficulty of reverse engineering and avoids malicious deletion by users. Therefore, the static key in the app is saved through gradle configuration string. Secret keys are a good choice.

Configure key information through string.xml

Configuring key information through string.xml is also a good choice. After defining the string value in string.xml, the effect of obtaining the decompiled apk through code is as follows:

Write picture description here

It can be found that the string value cannot be displayed explicitly here, but the ID value of the string appears here. However, it should be noted that malicious attacks can find the corresponding string in the R.java file through the ID of the string. name, and then find the string value in string.xml. But this also increases the difficulty of decompilation, which is relatively a good choice.

Practice of saving static secret keys in products:

Finally, after comparing various static key storage solutions, we decided to use gradle configuration + static code + string operation + string.xml value to achieve static key storage.

First, the static secret key is divided into four parts:

•The first part is stored through gradle configuration;

•The second part is stored through java hard coding;

•The third part is stored through Java string concatenation operation;

•The fourth part is saved through string.xml;

Obtain the first part of the appKey string:
We have introduced it above through gradle configuration, which is to define string variables under the buildType node in the gradle file in mudle. What needs to be noted here is that if there is a formal environment and a test environment points, we need to define string variables separately, so that we can get the string of the first part of appKay through BuildConfig.

/**

Get AppKey part1
*/

public static String getBK1() {

return BuildConfig.appKey;

}
Get the second part of appKey string:

The second part of the appKay string is generated through operations. The operations here can be any operation method. The more complex the better, and the less understandable the better. Of course, the result is unique when needed. for example:

public static StringBuffer getBk2() {

StringBuffer sb = new StringBuffer();
sb.append(Config.getGBS(2, 5));
return sb;

}
And here is the implementation of getGBS method:

public static int getGBS(int x, int y){

for(int i = 1; i<= x * y; i++){
    if(i % x == 0 && i % y == 0)
        return i;
}

return x * y;

}
The final result returned is: 10. Of course, different strings require different algorithms;

Get the third part string of appKey:

Here we just use simple string hardcoding

public static String getBK3() {

return “xhxh”;
}

Get the fourth part of appKey string
• Define the fourth part of appKey in string.xml

chs1

• Get the string value defined in string in code

public static String getBk4() {

mContext().getResources().getString(R.string.bk4);
}

Get the final appKey string:

/**

Get the final appKey string
*/

public static byte[] getDefaultKey() {

StringBuffer sb = new StringBuffer();
sb.append(getBK1()).append(getBK2()).append(getBk3()).append(getBk4());

return sb.toString();

}
In this way, after a series of operations, we obtain the final static key. Of course, it is best to implement the code obfuscation function in the product, which can also increase the difficulty of reverse engineering.

Summarize:

•Saving static keys on the App side can be achieved through SharedPreferences, java hard coding, so files in ndk, files, databases, and gradle configuration;

•In order to ensure the security of the secret key, it can be mixed in various ways, which can increase the difficulty of malicious decompilation;

•Saving the secret key on the App side cannot truly guarantee the security of the secret key, it can only increase the difficulty of decompilation;

• You can use gradle configuration to configure static keys, and use string.xml to configure keys to increase the difficulty of reverse decompilation;

•It is not recommended to use files or databases to save static keys, as they can easily be maliciously deleted by users and cause unpredictable errors;

Guess you like

Origin blog.csdn.net/weixin_30197685/article/details/104937145