Android NDK学习系列--第三篇(持续更新)

NDK学习系列--第三篇

简介

        目前是NDK开发的入门阶段,以前对C++有一点点基础,不过因为多年没有编写过C++程序了,所以现在也是希望可以边通过项目和实现功能来熟悉。今天学习了如何通过NDK来实现网络请求参数加密和签名校验,可能大部分人都没有了解过参数加密和签名校验,或者平时只是使用普通的对称加密算法对参数进行加密,比如通过MD5对参数加密,发送到服务器,服务端再对参数进行解密,得到实际的参数。这样的方式和裸奔基本上没啥区别,别人只要反编译拿到你的加密key值就可以随便破解了。可能小公司对安全方面的考虑没这么重视,但是大公司肯定会精密设置加密和安全校验,如百度地图、支付宝等。




为什么要用NDK来实现加密和校验呢?

        首先最重要的一点是第一篇所说的基于安全性的考虑,防止代码被反编译。我们对参数的加密方式,如果是用java来写,被人反编译后就可以看到我们的逻辑,这样别人只要把代码一copy,然后就可以随便调用的我们服务接口,对我们的后台服务进行攻击或者篡改数据,这样是非常不安全的。其次通过NDK来实现,一套代码可以拿到多个平台去使用,不止Android可以使用,这样可以大大提高可用性和效率。

       有加密了为啥还要检验呢?这二者缺一不可,有了加密,并不代表就是安全的,别人可以看你是如何调用加密接口,只要会调用接口,那别人的参数也一样是合法的,发送到服务端也是有效的请求。因此我们需要校验调用我们的接口的人是不是我们所希望的人。就好比一些秘密行动需要接头暗号一般,确认对方是自己人后才能和他接头,否则不理睬。业内的做法一般是校验报名和签名,使用过百度地图SDK的人应该都知道我们需要通过报名和签名来配置应用的百度地图key,这个key需要注册到项目的清单文件中。我个人猜测百度就是在so库去获取的应用的包名和签名,通过他们定好的规则设成key,然后拿这个key和你在清单文件里面的注册的key进行对比,一致的话说明你是正确的接头人,可以去使用百度地图SDK服务。不一致则会报错,使用不了百度地图。




实现过程

1、进行合法性校验。

代码如下:

// 校验签名

// 校验成功的变量

static int is_verify =0 ;


ava_cn_novate_ndk_day01_SignatureUtils_signatureVerify(JNIEnv *env, jclass type, jobject context) {

// TODO

    // 1. 获取包名

    jclass j_clz = env->GetObjectClass(context);

    // getPackageName:表示方法名;

    // ()Ljava/lang/String; :表示方法参数;

    // ():表示无参构造方法

    jmethodID j_mid = env->GetMethodID(j_clz, "getPackageName" , "()Ljava/lang/String;");

    jstring j_package_name = (jstring) env->CallObjectMethod(context, j_mid);

    // 2. 比对包名是否一样

    const char * c_package_name = env -> GetStringUTFChars(j_package_name, NULL);

    // strcmp方法: 字符串比较

    if(strcmp(c_package_name, PACKAGE_NAME) !=0){

return;

    }

// 打印log , %s表示占位符  打印结果:包名一致: cn.novate.ndk.day01

    __android_log_print(ANDROID_LOG_ERROR , "JNI_TAG" , "包名一致: %s" , c_package_name);

    // 3. 获取签名

    // 3.1 获取PackageManager

    j_mid = env -> GetMethodID(j_clz, "getPackageManager" , "()Landroid/content/pm/PackageManager;");

    jobject  package_manager = env->CallObjectMethod(context, j_mid);

    // 3.2 获取PackageInfo

    j_clz = env->GetObjectClass(package_manager);

    j_mid = env->GetMethodID(j_clz, "getPackageInfo" , "(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;");

    // 0x00000040 = 64

    jobject package_info = env->CallObjectMethod(package_manager, j_mid, j_package_name, 0x00000040);

    // 3.3 获取 signature数组

    j_clz = env -> GetObjectClass(package_info);

    jfieldID j_fid = env->GetFieldID(j_clz, "signatures" , "[Landroid/content/pm/Signature;");

    jobjectArray signatures = (jobjectArray) env->GetObjectField(package_info, j_fid);

    // 3.4 获取signature[0]

    jobject signature_first = env->GetObjectArrayElement(signatures,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 = (jstring) env->CallObjectMethod(signature_first, j_mid);

    const char * c_signature_str = env->GetStringUTFChars(j_signature_str,NULL);

    // 比较字符串

    // 比较签名是否相等,如果不相等,则直接 return;

    if(strcmp(c_signature_str, APP_SIGNATURE) !=0){

return;

    }

// 打印下日志

    __android_log_print(ANDROID_LOG_ERROR , "JNI_TAG" , "签名校验成功:%s" , c_signature_str);

    // 4. 比对签名是否一样,如果签名一样,就让is_verify = 1 ;

    is_verify =1 ;

}

       这个方法就是用来校验包名和签名,通过反射调用Java类的接口和熟悉,拿到包名和签名,然后进行校验,我这里只是进行普通的校验,将签名写死在了代码中。如果是在公司的项目中,这个签名应该放在后台服务中才是安全的。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容