接口数据加密之MD5加密

  • MD5 加密
  • Java层加密
  • Native层加密
  • 签名校验
  • 功能实现
  • 1.MD5 加密的实现
  • 2.签名校验
  • 源码


MD5 加密

定义: 一种被广泛使用的密码散列函数,可以产生出一个128位(16字节)的散列值(hash value),MD5 加密之后是一个 32 位不可逆字符串

Java层加密

加密算法网上有一大堆,但是我不推荐用java来写加密,防止别人抓包,无法防止别人反编译或者调试,作用不大。

Native层加密

需要一定的C,C++基础 ,了解一定的NDK知识
但是还是无法防止别人反编译,因为ndk本身原因无法对方法进行混淆,只要匹配好包名和方法名照样在任何地方都可以调用,所以要加入 签名校验,后台配置App的包名和签名,才可以使用

签名校验

Native 层校验签名,保护一下 ,虽然不能说是百分比的安全,但是安全的级别大大的增强

功能实现

1.MD5 加密的实现

  1. 在网上找到的MD5加密的方法:md5.cpp md5.h
  2. 我这里就不贴代码了 一会把例子上传一下
  3. 直接上核心
/**
 * @author li
 * 版本:1.0
 * 创建日期:2020/7/2 10
 * 描述:
 */
public class MD5SignUtils {
    static {
    		// 加载自己的库  
        System.loadLibrary("native-lib");
    }
	//实现方法 MD5加密
    public static native String signParams(String params);
	//签名校验
    public static native void  checkSignature(Context context);
}

接下来我们先MD5加密

首先我们在需要掉接口登录的时候先将数据加密 如

String s = MD5SignUtils.signParams("userName=HHHYYY&userPwd=123456");

直接调用native方法 ,接下来 我们在native-lib.cpp中进行实现

static  char *MD5TITLE = "LIMD5";
using namespace std ;
extern "C"
JNIEXPORT jstring JNICALL
Java_com_li_md5sign_MD5SignUtils_signParams(JNIEnv *env, jclass clazz,jstring params_) {
    //1.传过来的String转成char
    const char *params = env->GetStringUTFChars(params_,0);
    //打印log日志文件
    __android_log_print(ANDROID_LOG_ERROR,"JNI_TAG","params_的值:%s",params);
    //2.字符串前后做点手脚 (加盐)
    string signature_str(params);
    signature_str.insert(0,MD5TITLE);
    //去掉后两位 可以根据自身情况任意修改方法
    signature_str = signature_str.substr(0,signature_str.length()-2);
    //3 MD5加密
    MD5_CTX *ctx = new MD5_CTX();
    MD5Init(ctx);
    MD5Update(ctx, (unsigned char *) signature_str.c_str(), signature_str.length());
    unsigned char digest[16] = {0};
    MD5Final(digest,ctx);
	//这里出现问题 在mac电脑上报错 可以初始化大点就没问题了 
	//  char md5_str[128] = {0};
    char md5_str[32] = {0};
    for (int i = 0; i < 16;i++) {
        sprintf(md5_str,"%02x",digest[i]);
    }
	//释放资源
    env->ReleaseStringUTFChars(params_,params);
    __android_log_print(ANDROID_LOG_ERROR,"JNI_TAG","md5_str的值:%s",md5_str);
	//返回String
    return env->NewStringUTF(md5_str);

}

加密基本完成 这个加密加盐可根据自身情况任意修改,生成的.so文件可以给后台进行调用 这样相同的规则下可保证自己的加密和后台的相同

2.签名校验

1.先用java代码获取到正式包下的签名

PackageInfo packageInfo = null;
                try {
                    packageInfo = getPackageManager().getPackageInfo(getPackageName(), PackageManager.GET_SIGNATURES);
                    Signature[] signatures = packageInfo.signatures;
                    Log.e("TAG",signatures[0].toCharsString());
                } catch (PackageManager.NameNotFoundException e) {
                    e.printStackTrace();
                }

2.在 native 层 校验

public class BaseApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        //application 进行调用
        MD5SignUtils.checkSignature(this);
    }
}
static int is_verify = 0;
static char *PACKAGE_NAME = "com.li.md5sign";
static  char *APP_SIGNATURE= "308202e4308201cc020101300d06092a864886f70d010105050030373116301406035504030c0d416e64726f69642044656275673110300e060355040a0c07416e64726f6964310b30090603550406130255533020170d3230303431333035313432335a180f32303530303430363035313432335a30373116301406035504030c0d416e64726f69642044656275673110300e060355040a0c07416e64726f6964310b300906035504061302555330820122300d06092a864886f70d01010105000382010f003082010a0282010100b3734ac8d1ecf7e30be02a75bdaae0fd98b044cb12e007405b815636a1aa83d868551740356ac33643ff9294dbf011569b2776be8e8a318de33a56cf50c9ac93dbabce53f64239dd21908a5d252e37fcd42e57a3a6bad84f5b7eafb268038e3645c3d32c4a985731be3d4ed0a26ecbf46ab2d0b3d0d4910d75a5261f161aa438f2b7f14449a6044ca396620ad9351b0ec9cf560d16c771b3a3214124fd933ddeb6325a906de07ad914c9a084f47594d60e749aceca6e8be1e4e343e75cc62d36bdf7a27690f58103b65934a0d9f83875503e3dcf2c09c8db2ead37818b176d59b0ff5c73cd26af6a4ecf03fe21c38e460ad47bf36d0ae7669a8b38835d2f9e790203010001300d06092a864886f70d01010505000382010100148bf14237f11ac18c4ad0e66989794fb11f1b151c29b6c217b2b3e3d9d615a53b4dc2ea1b94d49cfe0aff7d4904a6c97846fd52972ecebf4100e5bd4999631c9d94540f4889a6122a8478510b907046f541ea0bd6b1d1c6a59583b03c91acbf9b1420630aa615177aa0ed34da508bf16be0893234d72d78570314df5c437f9c57184ce5cb93ee13ab2fa421da4e66215c3bd378aece0917a2f6f487c9453a22662d148aea11bc6f27ecda3489be4c86c738f03fb64200b9e0b11203a6acda227fb6d7dec3290c61400fa529431a6372ac46298f2937c8153c1677e884045d3cd63b31728de92c635807a594ea5e3faf036e66d9c4fb4ff17e30cd0913717500";

extern "C"
JNIEXPORT void JNICALL
Java_com_li_md5sign_MD5SignUtils_checkSignature(JNIEnv *env, jclass clazz, jobject context) {
    // TODO: implement checkSignature()
    //1  根据 this 获取包名
    jclass  j_clz = env->GetObjectClass(context);
    jmethodID j_mid = env->GetMethodID(j_clz,"getPackageName","()Ljava/lang/String;");
    jstring j_package_name = static_cast<jstring>(env->CallObjectMethod(context, j_mid));
    //2 对比包名和自己的PACKAGE_NAME 是否一致
    const  char *c_package_name = env->GetStringUTFChars(j_package_name,NULL);
    if(strcmp(c_package_name,PACKAGE_NAME)!=0){
        return;
    }
    // 校验App的签名 这里根据java代码来写
   //获取PackageManager
   j_mid = env->GetMethodID(j_clz,"getPackageManager","()Landroid/content/pm/PackageManager;");
   jobject  j_package_manger = env->CallObjectMethod(context,j_mid);
   //获取PackageInfo
    j_clz = env->GetObjectClass(j_package_manger);
    j_mid = env->GetMethodID(j_clz,"getPackageInfo","(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;");
    jobject  j_package_info = env->CallObjectMethod(j_package_manger,j_mid,j_package_name,0x00000040);
   //获取Signature
    j_clz = env->GetObjectClass(j_package_info);
    jfieldID  j_fid = env->GetFieldID(j_clz,"signatures","[Landroid/content/pm/Signature;");
    jobjectArray j_signature = static_cast<jobjectArray>(env->GetObjectField(j_package_info,
                                                                             j_fid));
   //去第0个位置
    jobject signature_first = env->GetObjectArrayElement(j_signature,0);
   // 3.5 调用 signatures[0].toCharsString();
    j_clz = env->GetObjectClass(signature_first);
    j_mid = env->GetMethodID(j_clz,"toCharsString","()Ljava/lang/String;");
    jstring j_signature_str = static_cast<jstring>(env->CallObjectMethod(signature_first, j_mid));
    const char * c_signature_str = env->GetStringUTFChars(j_signature_str,NULL);
   // 4. 比对签名是否一样
    if (strcmp(c_signature_str,APP_SIGNATURE)!=0){
        return;
    }
    __android_log_print(ANDROID_LOG_ERROR,"JNI","校验签名成功 :%s",c_signature_str);
    is_verify = 1;
}

3 根据 0 或者是1 来判断是否是否进行加密
在执行1 之前先做判断

if (is_verify == 0) {
        return env->NewStringUTF("error_signature");
    }

基本上功能就实现了

源码