Android JNI NDK C++ so本地验证 获取应用签

作者: callxkj | 来源:发表于2016-12-09 17:54 被阅读4796次

    一.前言

    最近做的应用涉及到敏感数据加密,就是数据加密后传到后台,然后后台解密出相应的值.初步用RSA公钥加密私钥解密,那么问题来了如果公钥用java文件写的话很容易泄露,所以要把公钥放在C里面,那么问题又来了,如果有人直接把SO文件拷贝过去岂不是又可以直接使用了?所以这个地方要使用so本地验证.怎么验证呢?思路是在C里面获取应用的签名,然后比对签名是否正确,如果正确才可以给java代码返回对的key值.

    二.参考

    有几篇非常nice的blog可以参考下.
    Android Studio ndk-Jni开发详解
    android so 文件存私密数据,且防止 so文件未知应用盗用
    jni_获取应用包名、签名

    三.集成简单的JNI

    1.首先配置ndk环境

    Paste_Image.png

    这个ndk可以去官网下载也可以用as下载,as下载的方法如下

    Paste_Image.png

    2.配置app下的build.gradle

    Paste_Image.png Paste_Image.png

    以上两个都在android{}内

    3.调用c的java类

    public class JNITest {
        static {
            System.loadLibrary("bbCourseLib");   //bbCourseLib 就是上一步在build.gradle中配置的moduleName so的名字
        }
    
        public static native String getbbCourseKeyFromC(Object contextObject);
    
    }
    

    4.C的头文件

    先clean project 然后 rebuild project


    Paste_Image.png

    然后就会看见生成classes文件夹


    Paste_Image.png
    然后打开as下面的terminal 输入下面指令
    Snip20161209_7.png

    cd是指进入debug文件夹


    Paste_Image.png
    javah -jni后面的路径是第3步调用c的java类的class地址,注意是debug文件夹下面的哦 Paste_Image.png

    执行上面两句命令后会在debug文件夹下生成头文件

    Paste_Image.png

    然后再main文件夹下新建一个jni文件夹,将刚刚剪切的头文件放到这个jni文件夹内


    Paste_Image.png

    5.C文件

    在jni文件夹新建一个C++文件 名字随便取


    Paste_Image.png

    打开刚刚生成的头文件发现代码是这样的


    Paste_Image.png
    复制上图红色部分到新建的c++文件中,然后加上一个返回语句
    Paste_Image.png

    6.验证结果

    Paste_Image.png Paste_Image.png

    最后打印结果为"哈哈哈",说明调用c++里面的方法成功

    四.C++增加验证

    1.首先用java代码打log打印出打包后的app的签名

        public static String getSignature(Context context)
        {
            try {
                /** 通过包管理器获得指定包名包含签名的包信息 **/
                PackageInfo packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), PackageManager.GET_SIGNATURES);
                /******* 通过返回的包信息获得签名数组 *******/
                Signature[] signatures = packageInfo.signatures;
                /******* 循环遍历签名数组拼接应用签名 *******/
                return signatures[0].toCharsString();
                /************** 得到应用签名 **************/
            } catch (PackageManager.NameNotFoundException e) {
                e.printStackTrace();
            }
            return null;
        }
    

    2.修改C++代码,增加验证C++的完整代码如下

    #include <jni.h>
    #include <string.h>
    #include <stdio.h>
    #include "com_xxx_module_rsa_JNITest.h"
    
    /**
     *这个key就是要用来加密传输内容的rsa的公钥
     */
    const char* AUTH_KEY = "1111";
    
    /**
     * 发布的app 签名,只有和本签名一致的app 才会返回 AUTH_KEY
     * 这个RELEASE_SIGN的值是上一步用java代码获取的值
     */
    const char* RELEASE_SIGN = "xxxxxx30102020440625908300d06092a864886f70d01010b0500305a310b3009060355040613023836310e300c060355040813056875626569310e300c06035504071305777568616e310d300b060355040a130468656865310d300b060355040b130468656865310d300b0603550403130463616c6c301e170d3136303932393130303234355a170d3436303932323130303234355a305a310b3009060355040613023836310e300c060355040813056875626569310e300c06035504071305777568616e310d300b060355040a130468656865310d300b060355040b130468656865310d300b0603550403130463616c6c30820122300d06092a864886f70d01010105000382010f003082010a0282010100b508259dd7e36da221a2b5de5158e6e1f310f2b11073b359b4a3e49d80f0b8c741c167e1364e0d3054af4a084d70a7a793cc51c47818c6b862ccb11d8316cc29c9f26ae5d543288b3392d36ad7556673621d25c6ad0dc469b8355d75ead3799d7806878c1f925dad789173c8e09d196b1197a300d73ecee78228c5def17c483138db50376c5d7c1ce0aaea3e7e90b37fa8d94f3418056f25aa12522356005678065b1f559b164758dfa470c0a63f6678400abba1983db0621422eac20d2f5406d4667f6d9175084641dd12180a1a1b048836864bb0336b9ad439d5ee059562352037473460e6885ac85362a5258d9438266a07085ae8044303049b2df6a0340f0203010001a321301f301d0603551d0e04160414fcc824f06f53f2a8c8efa1b97c8fcd43f5bcfff3300d06092a864886f70d01010b050003820101004f09129e656dc9ba39082615a112ce68a08383e518dbe9fe6c12d2b67fcf4287ee7d89faadbd189f31a374be641167ec366d2ae16b82a215fef9a33f468877a1d7edc395f5224fb0a4237fdfa4e960b42a99b082f66fbc37c991b7ee0306fdfd565e432ec6e11807e6c541aad33bd221fc793484519e932b82d963694df6605e2af3d66996188cc78d9e76a2e9b5d2ab60ea481384d327f3b62efef7eab79eb6df447cfadfc6a5c0717b9b3a22592080eec1822c22380f1fa37bc0119d30878f3b8a78d93da2d3d06fd6b45f4eac4afed8fac66393b04666e6436c86f0a68e31e3013634c1a6c93ed70256f3a3bf47506baab07bfb578d48922eaeea881bacd7";
    
    
    JNIEXPORT jstring JNICALL Java_com_xxx_module_rsa_JNITest_getbbCourseKeyFromC
            (JNIEnv *env, jclass jclazz, jobject contextObject){
    
        jclass native_class = env->GetObjectClass(contextObject);
        jmethodID pm_id = env->GetMethodID(native_class, "getPackageManager", "()Landroid/content/pm/PackageManager;");
        jobject pm_obj = env->CallObjectMethod(contextObject, pm_id);
        jclass pm_clazz = env->GetObjectClass(pm_obj);
    // 得到 getPackageInfo 方法的 ID
        jmethodID package_info_id = env->GetMethodID(pm_clazz, "getPackageInfo","(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;");
        jclass native_classs = env->GetObjectClass(contextObject);
        jmethodID mId = env->GetMethodID(native_classs, "getPackageName", "()Ljava/lang/String;");
        jstring pkg_str = static_cast<jstring>(env->CallObjectMethod(contextObject, mId));
    // 获得应用包的信息
        jobject pi_obj = env->CallObjectMethod(pm_obj, package_info_id, pkg_str, 64);
    // 获得 PackageInfo 类
        jclass pi_clazz = env->GetObjectClass(pi_obj);
    // 获得签名数组属性的 ID
        jfieldID signatures_fieldId = env->GetFieldID(pi_clazz, "signatures", "[Landroid/content/pm/Signature;");
        jobject signatures_obj = env->GetObjectField(pi_obj, signatures_fieldId);
        jobjectArray signaturesArray = (jobjectArray)signatures_obj;
        jsize size = env->GetArrayLength(signaturesArray);
        jobject signature_obj = env->GetObjectArrayElement(signaturesArray, 0);
        jclass signature_clazz = env->GetObjectClass(signature_obj);
        jmethodID string_id = env->GetMethodID(signature_clazz, "toCharsString", "()Ljava/lang/String;");
        jstring str = static_cast<jstring>(env->CallObjectMethod(signature_obj, string_id));
        char *c_msg = (char*)env->GetStringUTFChars(str,0);
        //return str;
        if(strcmp(c_msg,RELEASE_SIGN)==0)//签名一致  返回合法的 api key,否则返回错误
        {
            return (env)->NewStringUTF(AUTH_KEY);
        }else
        {
            return (env)->NewStringUTF("error");
        }
    }
    

    测试发现返回的是111,然后更改c中的RELEASE_SIGN,发现返回的是error 说明代码成功.

    五.删掉C文件换so

    先在 app/build/intermediates/nde/debug/lib目录下找到so文件

    Paste_Image.png

    复制以上的文件夹内容到libs文件加下

    Paste_Image.png

    最后删掉jni文件夹下的c文件,到此大功告成😆

    六.delicious

    最后的福利

    相关文章

      网友评论

      • 郭某人1:我的沒有ndk文件夾是什麼情況?
        而且創建的nativie方法是報錯的;
        創建c文件時類型沒有選項。這是怎麼了?
      • khunkk:还是不够安全啊~so文件用记事本可以看到RELEASE_SIGN明文
        callxkj:@JOAH_际遇做向导 都是乱码啊 怎么做到的?
        khunkk:@callxkj 我是用notepad++打开的,就可以看到
        callxkj:@JOAH_际遇做向导 什么记事本可以看?
      • hahaYXXXJ:好文章,刚刚好在找!
      • d5962134cef8:您好. 请问用as写ndk代码的时候 怎么才能有代码提示呢?
        瑾玄:用Cmake开发吧,提示,断点,跳转 一应俱全
      • yuger:还没用Cmake

      本文标题:Android JNI NDK C++ so本地验证 获取应用签

      本文链接:https://www.haomeiwen.com/subject/ejiemttx.html