一、背景
最近在做代码安全方面的工作,发现一些关键信息如:密钥、加密策略等直接写死在项目代码中,存在代码安全隐患。本文档提供一种示例:把关键信息,保存在native层;并对安装包关联信息进行校验,防止反编译、重签名导致信息泄露的可能。
二、处理方式
- 关键信息、算法、逻辑下沉;利用C/C++编写,利用JNI方式引用。so库+核心java类,导出aar文件。
- 导入aar,加载so库过程中对其关联的APP签名进行校验。如果校验通过,才能获取库内部信息。
- 对C/C++编写的so库进行安全加固,二次防编译。
三、关键逻辑
1. 签名校验
1.1 java代码获取签名
private String getSign1() {
try {
//通过PackageManager获取
PackageManager pm = getPackageManager();
PackageInfo pi = pm.getPackageInfo(getPackageName(), PackageManager.GET_SIGNATURES);
Signature[] signatures = pi.signatures;
Signature signature0 = signatures[0];
return signature0.toCharsString();
} catch (Exception e) {
e.printStackTrace();
return "";
}
}
private String getSign2() {
try {
// 下面几行代码展示如何任意获取Context对象,在jni中也可以使用这种方式
Class<?> activityThreadClz = Class.forName("android.app.ActivityThread");
Method currentApplication = activityThreadClz.getMethod("currentApplication");
Application application = (Application) currentApplication.invoke(null);
PackageManager pm = application.getPackageManager();
PackageInfo pi = pm.getPackageInfo(application.getPackageName(), PackageManager.GET_SIGNATURES);
Signature[] signatures = pi.signatures;
Signature signature0 = signatures[0];
return signature0.toCharsString();
} catch (Exception e) {
e.printStackTrace();
return "";
}
}
1.2 C获取签名
static int verifySign(JNIEnv *env) {
// Application object
jobject application = getApplication(env);
if (application == NULL) {
return JNI_ERR;
}
// Context(ContextWrapper) class
jclass context_clz = env->GetObjectClass(application);
// getPackageManager()
jmethodID getPackageManager = env->GetMethodID(context_clz, "getPackageManager",
"()Landroid/content/pm/PackageManager;");
// android.content.pm.PackageManager object
jobject package_manager = env->CallObjectMethod(application, getPackageManager);
// PackageManager class
jclass package_manager_clz = env->GetObjectClass(package_manager);
// getPackageInfo()
jmethodID getPackageInfo = env->GetMethodID(package_manager_clz, "getPackageInfo",
"(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;");
// context.getPackageName()
jmethodID getPackageName = env->GetMethodID(context_clz, "getPackageName",
"()Ljava/lang/String;");
// call getPackageName() and cast from jobject to jstring
jstring package_name = (jstring) (env->CallObjectMethod(application, getPackageName));
// PackageInfo object
jobject package_info = env->CallObjectMethod(package_manager, getPackageInfo, package_name, 64);
// class PackageInfo
jclass package_info_clz = env->GetObjectClass(package_info);
// field signatures
jfieldID signatures_field = env->GetFieldID(package_info_clz, "signatures",
"[Landroid/content/pm/Signature;");
jobject signatures = env->GetObjectField(package_info, signatures_field);
jobjectArray signatures_array = (jobjectArray) signatures;
jobject signature0 = env->GetObjectArrayElement(signatures_array, 0);
jclass signature_clz = env->GetObjectClass(signature0);
jmethodID toCharsString = env->GetMethodID(signature_clz, "toCharsString",
"()Ljava/lang/String;");
// call toCharsString()
jstring signature_str = (jstring) (env->CallObjectMethod(signature0, toCharsString));
// release
env->DeleteLocalRef(application);
env->DeleteLocalRef(context_clz);
env->DeleteLocalRef(package_manager);
env->DeleteLocalRef(package_manager_clz);
env->DeleteLocalRef(package_name);
env->DeleteLocalRef(package_info);
env->DeleteLocalRef(package_info_clz);
env->DeleteLocalRef(signatures);
env->DeleteLocalRef(signature0);
env->DeleteLocalRef(signature_clz);
//获取到安装的APK对应的签名
const char *sign = env->GetStringUTFChars(signature_str, NULL);
if (sign == NULL) {
LOGE("分配内存失败");
return JNI_ERR;
}
// LOGI("应用中读取到的签名为:%s", sign);
int result_debug = strcmp(sign, SIGN_Debug);
int result_release = strcmp(sign, SIGN_Release);
// 使用之后要释放这段内存
env->ReleaseStringUTFChars(signature_str, sign);
env->DeleteLocalRef(signature_str);
if (result_debug == 0 || result_release == 0) { // 签名一致
return JNI_OK;
}
return JNI_ERR;
}
1.3 是否打开APP签名校验
void Java_com_ankerwork_ctool_Security_setCheckSign(JNIEnv *env, jclass clazz, jboolean is_check) {
unsigned char b = is_check;
LOGI("/n is_check: %lu ", b);
if (b) {
isCheck = true;
} else {
isCheck = false;
}
}
2. 打包AAR
找到Module对应的Task,执行 执行assembleRelease
如:
3. 调用
3.1 引入AAR
- 将aar拷贝至该Module的libs目录中
- 在该Module的build.gradle中补上下面的代码
repositories {
flatDir {
dirs 'libs'
}
}
dependencies {
compile(name:'XXX', ext:'aar')
}
3.2 Java代码获取签名
参考1.1方法
3.3 c代码中填写签名内容
定义变量如:
static const char *sign = "1111"
3.4 设置是否校验签名
public class Security {
static {
System.loadLibrary("selfsecurity");
}
public static native String getIjmSecret();
public static native void setCheckSign(boolean isCheck);
}
Security.setCheckSign(true);//设置校验签名
3.5 获取关键Key
Security.getIjmSecret()
四、参考代码
完整代码链接:https://download.csdn.net/download/fepengwang/87594007