Android本地关键代码安全处理

本文档介绍了一种提高代码安全性的方法,将关键信息如密钥存储在native层,并通过JNI在C/C++中处理。通过签名校验防止反编译和重签名导致的信息泄露。首先,关键信息和算法被下沉到C/C++的so库中,然后创建AAR文件并集成到项目中。在加载so库时,会检查APP的签名,只有校验通过才能访问。此外,还提供了Java和C代码示例来获取和验证签名。
摘要由CSDN通过智能技术生成

一、背景

最近在做代码安全方面的工作,发现一些关键信息如:密钥、加密策略等直接写死在项目代码中,存在代码安全隐患。本文档提供一种示例:把关键信息,保存在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
  1. 将aar拷贝至该Module的libs目录中
  2. 在该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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值