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.