Android密码保护

来源:互联网 发布:免费自建app软件 编辑:程序博客网 时间:2024/04/30 00:27

Android密码保护

由于公司其中一个app在与服务端交互时要求加密,当时想想还挺简单的就用AES加密就ok可后面仔细想想AES加密需要用到密码而这个密码直接写到程序中别人反编译你app之后就能找到你的密码拿去使用,那咋办呢?有没有办法让别人拿不到我的密码呢?当然有啦,我们可以把密码写到jni里面这样别人就没有办法直接拿到我的密码,但是别人把我的so库拷贝到他自己的项目中不就可以一样使用了,还好办法还是有的,在获取密码之前我们可以获取当前app的签名信息,然后做一下对比(当前我们自己可以先获取一下签名信息可以写死在jni里面)如果匹配不上那么久认为不是咋们自己的app在调用就返回一个错误的密码给调用者也不提示错误玩死他们(O(∩_∩)O哈哈哈~)一下视具体实现方法
注:使用androidstudio开发,阅读前请先了解一下什么是jni开发。

  • 先创一个用于获取AES密码的类来加载jni
package com.ocmgr.util;import android.content.Context;public class MYJNI {    static {        System.loadLibrary("mylib");    }    public native static String GetAESKey(Context context);}
  • 在项目中创建jni文件夹加入一下文件
//----------------//  jni接口  文件名mylib.h//----------------#include <jni.h>#ifndef _Included_COM_OCMGR_UTIL_MYJNI #define _Included_COM_OCMGR_UTIL_MYJNI #ifdef __cplusplusextern "C" {#endifJNIEXPORT jstring JNICALL Java_com_ocmgr_util_MYJNI _GetAESKey        (JNIEnv *, jobject);#ifdef __cplusplus}#endif#endif
// 文件名:mylib.c#include <jni.h>#include <android/log.h>#include <string.h>#include <stdio.h>#define   LOG_TAG    "MYJHI"#define   LOGI(...)  __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)#define   LOGE(...)  __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)#ifndef _Included_COM_OCMGR_UTIL_MYJHI#define _Included_COM_OCMGR_UTIL_MYJHI#ifdef __cplusplusextern "C" {#endifjstring ErrorAESKey = "需要自己改写(错误的密码)";jstring SuccessAESKey = "正确的密码";jstring SuccessMD5;jint SuccessHashCode = 正确的HashCode值;// 字节流转换为十六进制字符串void ByteToHexStr(const unsigned char* source, char* dest, int sourceLen){    short i;    unsigned char highByte, lowByte;    for (i = 0; i < sourceLen; i++)    {        highByte = source[i] >> 4;        lowByte = source[i] & 0x0f;        highByte += 0x30;        if (highByte > 0x39)            dest[i * 2] = highByte + 0x07;        else            dest[i * 2] = highByte;        lowByte += 0x30;        if (lowByte > 0x39)            dest[i * 2 + 1] = lowByte + 0x07;        else            dest[i * 2 + 1] = lowByte;    }    return;}JNIEXPORT jstring JNICALL GetSignature(JNIEnv *env, jobject jObj,jobject context) {    // 获得Context类    jclass cls = (*env)->GetObjectClass(env, context);    // 得到getPackageManager方法的ID    jmethodID mid = (*env)->GetMethodID(env, cls, "getPackageManager", "()Landroid/content/pm/PackageManager;");    // 获得应用包的管理器    jobject pm = (*env)->CallObjectMethod(env, context, mid);    // 得到getPackageName方法的ID    mid = (*env)->GetMethodID(env, cls, "getPackageName", "()Ljava/lang/String;");    // 获得当前应用包名    jstring packageName = (jstring)(*env)->CallObjectMethod(env, context, mid);    // 获得PackageManager类    cls = (*env)->GetObjectClass(env, pm);    // 得到getPackageInfo方法的ID    mid  = (*env)->GetMethodID(env, cls, "getPackageInfo", "(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;");    // 获得应用包的信息    jobject packageInfo = (*env)->CallObjectMethod(env, pm, mid, packageName, 0x40); //GET_SIGNATURES = 64;    // 获得PackageInfo 类    cls = (*env)->GetObjectClass(env, packageInfo);    // 获得签名数组属性的ID    jfieldID fid = (*env)->GetFieldID(env, cls, "signatures", "[Landroid/content/pm/Signature;");    // 得到签名数组    jobjectArray signatures = (jobjectArray)(*env)->GetObjectField(env, packageInfo, fid);    // 得到签名    jobject sign = (*env)->GetObjectArrayElement(env, signatures, 0);    // 获得Signature类    cls = (*env)->GetObjectClass(env, sign);    // 得到toCharsString方法的ID([B)Ljava/lang/String;    mid = (*env)->GetMethodID(env, cls, "toByteArray", "()[B");    // 返回当前应用签名信息    jbyteArray signaturesByte = (jbyteArray)(*env)->CallObjectMethod(env, sign, mid);    //      MessageDigest localMessageDigest = MessageDigest.getInstance("MD5");    jclass class_MessageDigest = (*env)->FindClass(env, "java/security/MessageDigest");    jmethodID tem_method = (*env)->GetStaticMethodID(env, class_MessageDigest, "getInstance", "(Ljava/lang/String;)Ljava/security/MessageDigest;");    jobject obj_md5 = (*env)->CallStaticObjectMethod(env, class_MessageDigest, tem_method, (*env)->NewStringUTF(env, "md5"));    //      localMessageDigest.update(localSignature.toByteArray());    //tem_class = (*env)->GetObjectClass(env, obj_md5);    tem_method = (*env)->GetMethodID(env, class_MessageDigest, "update", "([B)V");// 这个函数的返回值是void,写V    (*env)->CallVoidMethod(env, obj_md5, tem_method, signaturesByte);    // localMessageDigest.digest()    tem_method = (*env)->GetMethodID(env, class_MessageDigest, "digest", "()[B");    // 这个是md5以后的byte数组,现在只要将它转换成16进制字符串,就可以和之前的比较了    jobject obj_array_sign = (*env)->CallObjectMethod(env, obj_md5, tem_method);// jni中有强转类型的概念吗    //      // 这个就是签名的md5值    //      String str2 = toHex(localMessageDigest.digest());    // 尝试用c写一下:http://blog.csdn.net/pingd/article/details/41945417    jsize int_array_length = (*env)->GetArrayLength(env, obj_array_sign);    jbyte* byte_array_elements = (*env)->GetByteArrayElements(env, obj_array_sign, JNI_FALSE);    char* char_result = (char*) malloc(int_array_length*2+1);// 开始没有+1,在有的情况下会越界产生问题,还是在后面补上\0比较好    // 将byte数组转换成16进制字符串,发现这里不用强转,jbyte和unsigned char应该字节数是一样的    ByteToHexStr(byte_array_elements, char_result, int_array_length);    *(char_result+int_array_length*2) = '\0';// 在末尾补\0    jstring string_result = (*env)->NewStringUTF(env, char_result);    // release    (*env)->ReleaseByteArrayElements(env, obj_array_sign, byte_array_elements, JNI_ABORT);    // 释放指针使用free    free(char_result);    return string_result;}JNIEXPORT char * JNICALL Jstring2CStr(JNIEnv * env,jstring jstr){    char * rtn = NULL;    jclass clsstring = (*env)->FindClass(env,"java/lang/String");    jstring strencode = (*env)->NewStringUTF(env,"UTF-8");    jmethodID mid = (*env)->GetMethodID(env,clsstring, "getBytes", "(Ljava/lang/String;)[B");    jbyteArray barr= (jbyteArray)(*env)->CallObjectMethod(env,jstr,mid,strencode);    jsize alen = (*env)->GetArrayLength(env,barr);    jbyte * ba = (*env)->GetByteArrayElements(env,barr,JNI_FALSE);    if(alen > 0){        rtn = (char*)malloc(alen+1); //new char[alen+1];        memcpy(rtn,ba,alen);        rtn[alen]=0;    }    (*env)->ReleaseByteArrayElements(env,barr,ba,0);    return rtn;}// 重点在这里JNIEXPORT jstring JNICALL Java_com_ocmgr_util_MYJHI_GetAESKey(JNIEnv * env, jobject jObj,jobject context){    if (context == NULL){        return (*env)->NewStringUTF(env,ErrorAESKey);    }    jstring appMd5 = GetSignature(env, jObj,context);    jint hashCode = GetSignHashCode(env, jObj,context);    SuccessMD5 = (*env)->NewStringUTF(env,"正确MD5值");    if (hashCode == SuccessHashCode && (strcmp(Jstring2CStr(env,appMd5),Jstring2CStr(env,SuccessMD5)) == 0)){        return (*env)->NewStringUTF(env,SuccessAESKey);    }    return (*env)->NewStringUTF(env,ErrorAESKey);}JNIEXPORT jint JNICALL GetSignHashCode(JNIEnv *env, jobject jObj,jobject context) {    //Context的类    jclass context_clazz = (*env)->GetObjectClass(env, context);    // 得到 getPackageManager 方法的 ID    jmethodID methodID_getPackageManager = (*env)->GetMethodID(env, context_clazz,"getPackageManager", "()Landroid/content/pm/PackageManager;");    // 获得PackageManager对象    jobject packageManager = (*env)->CallObjectMethod(env, context,methodID_getPackageManager);//  // 获得 PackageManager 类    jclass pm_clazz = (*env)->GetObjectClass(env, packageManager);    // 得到 getPackageInfo 方法的 ID    jmethodID methodID_pm = (*env)->GetMethodID(env, pm_clazz, "getPackageInfo","(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;");////  // 得到 getPackageName 方法的 ID    jmethodID methodID_pack = (*env)->GetMethodID(env, context_clazz,"getPackageName", "()Ljava/lang/String;");    // 获得当前应用的包名    jstring application_package = (*env)->CallObjectMethod(env, context,methodID_pack);    const char *str = (*env)->GetStringUTFChars(env, application_package, 0);    //__android_log_print(ANDROID_LOG_DEBUG, "JNI", "packageName: %s\n", str);    // 获得PackageInfo    jobject packageInfo = (*env)->CallObjectMethod(env, packageManager,methodID_pm, application_package, 64);    jclass packageinfo_clazz = (*env)->GetObjectClass(env, packageInfo);    jfieldID fieldID_signatures = (*env)->GetFieldID(env, packageinfo_clazz,"signatures", "[Landroid/content/pm/Signature;");    jobjectArray signature_arr = (jobjectArray)(*env)->GetObjectField(env,packageInfo, fieldID_signatures);    //Signature数组中取出第一个元素    jobject signature = (*env)->GetObjectArrayElement(env, signature_arr, 0);    //读signature的hashcode    jclass signature_clazz = (*env)->GetObjectClass(env, signature);    jmethodID methodID_hashcode = (*env)->GetMethodID(env, signature_clazz,"hashCode", "()I");    jint hashCode = (*env)->CallIntMethod(env, signature, methodID_hashcode);//    __android_log_print(ANDROID_LOG_DEBUG, "JNI", "hashcode: %d\n", hashCode);//    //回当前应用签名信息//    // 得到toCharsString方法的ID//    jmethodID methodID_toCharsString = (*env)->GetMethodID(env, signature_clazz, "toCharsString", "()Ljava/lang/String;");//    jstring signature_info = (jstring)(*env)->CallObjectMethod(env, signature, methodID_toCharsString);//    __android_log_print(ANDROID_LOG_DEBUG, "JNI", "signature_info: %d\n", signature_info);    return hashCode;}#ifdef __cplusplus}#endif#endif
// 在build.gradle中的defaultConfig中加入ndk {    moduleName "mylib"          //生成的so名字    abiFilters "armeabi", "armeabi-v7a", "x86"    ldLibs "log"}

大功告成

附加:获取Android原生类的方法签名
javap -classpath (android.jar位置) -s -p (需要获取方法签名的类)
例如:javap -classpath D:\androidDev\androidSdk\platforms\android-23\android.jar -s -p android.os.Bundle
如果觉得命令行看起来不方便那么就输出到文件中
javap -classpath D:\androidDev\androidSdk\platforms\android-23\android.jar -s -p android.os.Bundle > D:\androidDev\androidProject\Bundle.txt

0 0
原创粉丝点击