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

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

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 __cplusplus
extern "C"{
#endif

static 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,调用方法。

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


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值