Orca.so

GitHub address, welcome Stars

Shall not be reproduced without permission

First, the reason for writing this article

In daily development, we often need to save some strings, and these strings are more important, we can save through a number of ways

(1) The dynamic password and the corresponding encrypted string are distributed through the network. The password is valid for a certain period of time.

(2) The password is stored locally in some places, such as SP, database, MMKV, dynamic link library, etc

In this paper, based on the second scheme of dynamic link libraries to develop, namely we commonly known as So library, because there is no complete solution doesn’t open way of encryption, are the problems of long and short, we must increase the decryption time cost, and the convenient when considering the development and performance issues, we use a variety of combination, such as network + double check local secret key, Or dynamic distribution of encryption key information recombination and so on.

Gradle Plugin development

1. Create a Module

2. Basic Grdlde configuration

Assuming our Module name is Plugin, change the import method in the Project root directory

//include(":plguin")Copy the code

This project is developed based on Kotlin, so it is necessary to change build.gradle to build.gradle. KTS file with the following general configuration:

buildscript {
    repositories {
        jcenter()
        google()
    }
    dependencies {
        classpath("Org. Jetbrains. Kotlin: kotlin - gradle - plugin: 1.4.21")
    }
}

dependencies {
    compileOnly(gradleApi())
    compileOnly("Org. Jetbrains. Kotlin: kotlin - gradle - plugin: 1.4.21")
    compileOnly("Com. Android. Tools. Build: gradle: 4.0.1." ")
    implementation("Com. Squareup: javapoet: 1.13.0.") // To produce JAVA code} gradlePlugin {plugins {create(name of Plugin, define yourself) {id ="Define your own independent Id"
            implementationClass = "The path to the corresponding Plugin, which is the package name. The name of the class. ""}}}Copy the code

Where “org. Jetbrains. Kotlin: kotlin – gradle – plugin: 1.4.21” can be changed to “kotlin – DSL” mainly to import kotlin development gradle plugin based environment, “Com. Squareup: javapoet: 1.13.0” used in the production of custom JAVA code, we store content dynamically generated code processing.

How to implement standalone :app and :Library

1. Generate your own CMakeLists file

If you have experience with NDK development, you should know that we need a cmakelists. TXT file to build projects with mixed Native C++, and this cmakelists. TXT file is prone to conflicts and other problems, including the dynamic link library with the same name. So we use Gradle Task to dynamically generate cMakelists. TXT, and So library names are also different names, the content is roughly as follows:

Cmake_minimum_required (VERSION 3.4.1) add_library(core-client // yours. So library name, that is, the resulting libcore-client. So file, System.loadLibrary("core-client") SHARED src/main/cpp/core-client.cpp src/main/cpp/core-environment.cpp SRC /main/ CPP /core-encryption.cpp) find_library(log-lib log) target_link_libraries(core-client // yours. ${log-lib})Copy the code

With this key factor in mind we can solve the problem of duplicate names. So library problem, I changed the name, let each other interfere.

How to dynamically bind the Nativce method to our JAVA file

Since our.so is independent, we’d better call these methods in a separate JAVA file. At this time, we need to be compatible with the existence of APP and Libray, and the package name path will be different. If the simple traditional native method is not feasible to automatically generate code

Extern "C" package _ name _XXX_XXX_XX_ name (JNIEnv *env,jclass clazz) extern "C" package _ Name _XXX_XXX_XX_ Name (JNIEnv *env,jclass clazz)Copy the code

This does not meet our requirements, we need a dynamic registration method

Jint JNI_OnLoad(JavaVM *vm, void *reserved)// System.loadLibrary("") loadCopy the code

I’ll sign up for the method here

extern "C" JNIEXPORT jstring JNICALL getString(JNIEnv *env,jclass clazz,jstring key_){ return env->NewStringUTF(result);  } JNINativeMethod methods[] = { { "getString", "(Ljava/lang/String;) Ljava/lang/String;" ,(void*)getString}, }; jint JNI_OnLoad(JavaVM *vm, void *reserved) { env->RegisterNatives(clazz, methods, sizeof(methods)/sizeof(JNINativeMethod)); return JNI_VERSION_1_6; } public static native String getString(String key);Copy the code

JNINativeMethod methods[] the first is the method name, the second is the (parameter) and the return value (return = what?). , the third point to the implementation of how to fill in the specific, here also do not expand detail. Find this reference article

3. Two encryption modes are supported

This project adopts the simplest two symmetric encryption methods: AES and DES

Support to generate Java classes or Kotlin classes

Kotlin:

public object CoreClient {
  init {
    System.loadLibrary("app-core-client")
  }

  public external fun getString(key: String): String

  public fun getData(): String = getString("8d777f385d3dfec8815d20f7496026dc")
}

Copy the code

Java:

public final class CoreClient { static { System.loadLibrary("app-core-client"); } private CoreClient() throws IllegalAccessException { throw new IllegalAccessException(); } public static native String getString(String key); public static String getData() { return getString("8d777f385d3dfec8815d20f7496026dc"); }}Copy the code

Specific call

Since I have already uploaded the library to the Jitpack, I need to configure the environment in project in the root directory

buildscript { repositories { google() jcenter() maven { setUrl("https://jitpack.io") } } dependencies { The classpath (" com. The occ. Orca: orca. So: 2.0.0 - release14 ")}}Copy the code

Then add build.gradle in your project’s Module and app

plugins {
    id 'com.android.application'
    id 'Orca'
    id 'kotlin-android'
}
Copy the code

It is also easy to write in the build.gradle of a project or module

Android {orca. go{isDebug = true // Default is false, Signature encryptMode = "des" // Enter AES or DES, // storeSet{"data"{value = "4444444444"} "1223"{value = "888888" // Name the first number, The method name will be underlined}}}} // that's where we call CoreClient. Note that CoreClient has a package name, Val data = coreclient.getData () val data1 = Coreclient.get_1223 () val data = coreclient.get_1223 ()Copy the code

4, use attention

In the C++ layer, the signature is checked, and if the signature is checked incorrectly, the field will crash. The Key is secretKey, and the Key is signature. The isDebug field controls whether signature is entered or not.

summary

AESEncryption and DESEncryption adopted in this project still need to be improved in the proposed scheme. In the future, we will consider directly doing encryption operations in C++ layer. Welcome to Pull Request.