Android用C/C++来保存密钥

来源:互联网 发布:mac双系统win8.1激活 编辑:程序博客网 时间:2024/05/22 07:47

保存密钥我们可以将密钥写在如下三个地方:

1.java source code;

2.gradle中,使用BuildConfig读取;

3.写在gradle properties中,再在build gradle中读取,同第二种方法;

上述三种方法可以用且方便为什么我们要将密钥写在C/C++中呢!大家都知道写在Android代码中很容易让别人通过反编译进行读取;这样就存在很大的安全隐患;

可能有网友会问写在C/C++中别人也可以将我们的.so包拿到(将Apk解包就能拿到),然后自己声明native方法,load库,然后调用native方法,那么我们即使写在C/C++中也是白做功夫,所以今天我们来改进一下在C/C++中保存读取密钥。

改进步骤如下:

首先我们要在native代码里面,先验证一下应用的签名是否是我们的,如果是,才返回正确的密钥。

1.获取签名唯一字符串

在任意一个Activity中调用如下方法,可以得到签名的字符串。

public void getSignInfo() {       try {           PackageInfo packageInfo = getPackageManager().getPackageInfo(                   getPackageName(), PackageManager.GET_SIGNATURES);           Signature[] signs = packageInfo.signatures;           Signature sign = signs[0];           System.out.println(sign.toCharsString());       } catch (Exception e) {           e.printStackTrace();       }   }
2.修改native方法的声明,传入Context对象。

public native String nativeMethod(Context context);

然后修改C++代码添加逻辑:

#include <jni.h>#include <stdio.h>#include <string.h>#ifdef __cplusplusextern "C"{#endifstatic jclass contextClass;static jclass signatureClass;static jclass packageNameClass;static jclass packageInfoClass;/**    之前生成好的签名字符串*/const char* RELEASE_SIGN = "第1步,生成好的字符串";/*    根据context对象,获取签名字符串*/const char* getSignString(JNIEnv *env,jobject contextObject) {    jmethodID getPackageManagerId = (env)->GetMethodID(contextClass, "getPackageManager","()Landroid/content/pm/PackageManager;");    jmethodID getPackageNameId = (env)->GetMethodID(contextClass, "getPackageName","()Ljava/lang/String;");    jmethodID signToStringId = (env)->GetMethodID(signatureClass, "toCharsString","()Ljava/lang/String;");    jmethodID getPackageInfoId = (env)->GetMethodID(packageNameClass, "getPackageInfo","(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;");    jobject packageManagerObject =  (env)->CallObjectMethod(contextObject, getPackageManagerId);    jstring packNameString =  (jstring)(env)->CallObjectMethod(contextObject, getPackageNameId);    jobject packageInfoObject = (env)->CallObjectMethod(packageManagerObject, getPackageInfoId,packNameString, 64);    jfieldID signaturefieldID =(env)->GetFieldID(packageInfoClass,"signatures", "[Landroid/content/pm/Signature;");    jobjectArray signatureArray = (jobjectArray)(env)->GetObjectField(packageInfoObject, signaturefieldID);    jobject signatureObject =  (env)->GetObjectArrayElement(signatureArray,0);    return (env)->GetStringUTFChars((jstring)(env)->CallObjectMethod(signatureObject, signToStringId),0);}jstring Java_[ClassAPackage]_A_nativeMethod(JNIEnv *env,jobject thiz,jobject contextObject) {    const char* signStrng =  getSignString(env,contextObject);    if(strcmp(signStrng,RELEASE_SIGN)==0)//签名一致  返回合法的 api key,否则返回错误    {       return (env)->NewStringUTF("你的密钥");    }else    {       return (env)->NewStringUTF("error");    }}/**    利用OnLoad钩子,初始化需要用到的Class类.*/JNIEXPORT jint JNICALL JNI_OnLoad (JavaVM* vm,void* reserved){     JNIEnv* env = NULL;     jint result=-1;     if(vm->GetEnv((void**)&env, JNI_VERSION_1_4) != JNI_OK)       return result;     contextClass = (jclass)env->NewGlobalRef((env)->FindClass("android/content/Context"));     signatureClass = (jclass)env->NewGlobalRef((env)->FindClass("android/content/pm/Signature"));     packageNameClass = (jclass)env->NewGlobalRef((env)->FindClass("android/content/pm/PackageManager"));     packageInfoClass = (jclass)env->NewGlobalRef((env)->FindClass("android/content/pm/PackageInfo"));     return JNI_VERSION_1_4; }#ifdef __cplusplus}#endif
getSignString方法也许看起来很复杂,如果熟悉java反射的Api的话,其实很类似,就是拿到方法的Id,调用方法。

上述这种改进的话就能防止别人破译你的密钥。


阅读全文
0 0