Androidjni加密
我们经常会有些敏感的信息需要客户端加解密,但android很容易被反编译,所以我们写在客户端⾥的密钥终究得不到安全,可能有⼈会想把加密⽅式写在C代码中,⽣成.so供APK使⽤,可是别⼈不关⼼你C⾥的代码,直接把你的so⽂件给拿去⽤就可以了,那么有没有⼀种安全的措施来加⼤难度呢,我说⼀下我们项⽬中如果解决客户端加密安全⽅案.
⾸先加密的代码仍然写在C代码中,⾄少C被反编译出来是汇编代码,加⼤阅读难度,另外⼀点就是如何让我们的.so⽂件只允许在我们⾃⼰的APK中使⽤,⽽不被别⼈拿去使⽤呢,这就需要签名验证这做⼀些⽂章,当程序调⽤.so时我们可以C代码中检查当前程序的签名是不是
我们的,条件成⽴才进⾏加解密操作,当然签名泄露这个就另说了,毕竟没有百分百的安全,先来看看C代码
#include <string.h>
#include <jni.h>
#include <stdio.h>
#include <stdlib.h>
#include <android/log.h>
//LOG宏定义
#define LOG_TAG  "JNI_SCRIPT"
#define LOG_E(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
jstring JNICALL
Java_com_example_administrator_myapplication_Test_getScriptData( JNIEnv* env,jobject obj, jobject contextObj){
//根据传⼊的context对象获取getApplicationContext(),防⽌java中获取其它已安装APK的Context对象
jclass context_cls = (*env)->GetObjectClass(env,contextObj);
jmethodID applicationContextMethod = (*env)->GetMethodID(env, context_cls, "getApplicationContext", "()Landroid/content/Context;");
jobject applicationContext = (*env)->CallObjectMethod(env, contextObj, applicationContextMethod);
if (applicationContext == NULL) {
LOG_E("context invalid!!");
}
char *st = "application"; //当前程序包名
//根据传⼊的context对象getPackageName
jmethodID pkgName_method = (*env)->GetMethodID(env, context_cls, "getPackageName", "()Ljava/lang/String;");
jstring pkgName = (*env)->CallObjectMethod(env, applicationContext, pkgName_method);
char *pkg = (*env)->GetStringUTFChars(env, pkgName, NULL);
//对⽐
if (strcmp(st, pkg) != 0) {
LOG_E("package name invalid!!");
return NULL;
android获取真正的签名}
// 获取PackageManager对象
jmethodID getPackageManager_method = (*env)->GetMethodID(env, context_cls, "getPackageManager", "()Landroid/content/pm/PackageManager;");    jobject packageManager = (*env)->CallObjectMethod(env, applicationContext, getPackageManager_method);
// PackageManager class
jclass packageManager_cls = (*env)->GetObjectClass(env, packageManager);
// 得到 getPackageInfo ⽅法
jmethodID getPackageInfo_method = (*env)->GetMethodID(env, packageManager_cls,
"getPackageInfo", "(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;");
// 获取PackageInfo类对象
jobject packageInfo = (*env)->CallObjectMethod(env, packageManager,
getPackageInfo_method, pkgName, 64);
// 获取PackageInfo class
jclass packageInfo_cls = (*env)->GetObjectClass(env, packageInfo);
jfieldID signatures_field = (*env)->GetFieldID(env, packageInfo_cls, "signatures", "[Landroid/content/pm/Signature;");
jobjectArray signatures = (*env)->GetObjectField(env, packageInfo, signatures_field);
jobject signature = (*env)->GetObjectArrayElement(env, signatures, 0);
jclass signature_cls = (*env)->GetObjectClass(env, signature);
jclass signature_cls = (*env)->GetObjectClass(env, signature);
jmethodID hashcode_method = (*env)->GetMethodID(env, signature_cls, "hashCode", "()I");
int hashCode = (*env)->CallIntMethod(env, signature, hashcode_method);
// 检测apk签名的hashCode值,然后进⾏对⽐
if (hashCode != -1370002482) {
LOG_E("apk signature error,don not use this .so !!")
}
//上⾯条件都通过后可以进⾏加密算法处理,加密代码省略
//........
return "加密后的字符串";
}
java中代码
public class Test {
static{
System.loadLibrary("mylib");
}
public native String getScriptData(Context context);
}
上⾯的C的代码中我们从jni对API的反射来获取APK签名的hashCode值,第⼀步jni中接收Context对象,然后通过context对象获取getApplicationContext(),可能你会问我为什么要这么做,其实这⼀步是为了防⽌在java层传⼊恶意的context对象,打个⽐⽅,我们的程序叫A,恶意者的程序叫B,如果恶意者把A和B都安在⼿机中,那么在B程序中完全可以获取A程序的Context对象,这样⼀旦把A程序的Context对象传⼊jni中,我们的检测是通过的,恶意者是怎么能够在它的B程序中获取A程序的Context对象呢?下⾯⼀条语句就可以完成:
Context thirdContext = createPackageContext("A程序的包名", Context.CONTEXT_IGNORE_SECURITY|Context.CONTEXT_INCLUDE_CODE);
是不是很容易,不过不⽤担⼼,虽然在B程序中通过上⾯代码获取A的Context,但我们只要⽤ApplicationContext()会返回null,所以我们在调⽤jni中第⼀步就是先获取⼀下Applicati
onContext对象
第⼀步通过后,第⼆步获取当前程序的包名,进⾏对⽐,包名相同后接下来我们就要获取so的调⽤者程序的签名了,这⼀步就是安全的关键性所在,如果以上条件都通过了,逻辑就到了我们进⾏加解密的地⽅。
其实我在签名判断完后,⼜在安全性上做了⼀些⼩的⼿脚,⽐如判断我们⼯程下某些java类是不是存在,这些类中某些⽅法是不是存在,这样及时签名被泄露后恶意者也要把项⽬中的⼀些类、⽅法全部⼀模⼀样的保留才能使⽤我们的.so库⽂件,⼜增加⼀些复杂度。安全没有⼗全⼗美,我们能做的就是尽量加⼤恶意者的破坏难度。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。