Android密码保护

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

JNIEXPORT 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 __cplusplus
extern "C" {
#endif
jstring 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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值