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
- Android密码保护
- 密码保护
- 密码保护页面
- 密码保护页面
- 边锋密码保护计算器
- 利用Javascript进行密码保护
- ICQ密码保护的问题
- ASP.NET中密码保护
- 除NOD32的密码保护
- 密码保护页面代码
- 怎样设置密码保护问题
- VBA 如何设置密码保护
- 浅析QQ密码保护原理
- 浅谈QQ密码保护原理
- 给nginx设置密码保护
- httpserver启用密码保护
- 破解Excel 密码保护
- 解除Excel密码保护大全
- 选择排序法及时间测试
- Integer to English Words
- android Scroller
- 数字图像处理学习 matable
- Allegro 导入其他板子的OutLine
- Android密码保护
- 基于感知器模型的线性神经网络
- css的边框属性
- 远程服务例子2
- OpenGL基本图形绘制方式比较(glBegin,glCallList,glVertexPointer,VBO)
- Scale IPSec config over cisco router
- CPU内存每月的平均使用率
- Haproxy简单配置实例
- SSM框架入门和搭建 十部曲