主要基于Cmake来构建本NDK级别的安全认证方案
- 防止别人用新的签名文件对APK源码进行二次打包
- 进一步保护应用的安全性,用NDK实现校验逻辑相比用Java实现更为安全,严谨
- 同时也可以保护SO库自身的安全性,防止JNI方法被其它应用调用
- 预设签名:在NDK层预先设定该应用的签名信息串
- 反射调用:在NDK层通过反射动态去获取当前应用的签名信息,与预设签名进行比较
- 如又要兼备保护so库的jni方法不被其它App调用,又要有签名校验的功能,则需要在C++代码上加上JNI_OnLoad 逻辑:
/** * 当so库被初始化的时候,手动去调用JNI方法时,该方法被调用 */ jint JNI_OnLoad(JavaVM *vm, void *reserved) { JNIEnv *env = NULL; if (vm->GetEnv((void **) &env, JNI_VERSION_1_4) != JNI_OK) { return JNI_ERR; } if (verifySign(env) == JNI_OK) { return JNI_VERSION_1_4; } // LOGE("签名不一致!"); return JNI_ERR; }
- 【Java端调用】直接调用SecurityCore->isSecuritySign()的native方法进行签名合法性校验
SecurityCore.isSecuritySign(); //返回true:签名合法;false:不合法
- 使用前需要把 NDKSecurityLibrary/src/main/cpp/SecurityCore.cpp 文件里的预设签名*SIGN换成你自己应用的签名信息
- 预设签名可以用Java代码事先获取
/** * 展示了如何用Java代码获取签名 */ private String getSign() { 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); 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 ""; } }