一.前言
最近做的应用涉及到敏感数据加密,就是数据加密后传到后台,然后后台解密出相应的值.初步用RSA公钥加密私钥解密,那么问题来了如果公钥用java文件写的话很容易泄露,所以要把公钥放在C里面,那么问题又来了,如果有人直接把SO文件拷贝过去岂不是又可以直接使用了?所以这个地方要使用so本地验证.怎么验证呢?思路是在C里面获取应用的签名,然后比对签名是否正确,如果正确才可以给java代码返回对的key值.
二.参考
有几篇非常nice的blog可以参考下.
Android Studio ndk-Jni开发详解
android so 文件存私密数据,且防止 so文件未知应用盗用
jni_获取应用包名、签名
三.集成简单的JNI
1.首先配置ndk环境
这个ndk可以去官网下载也可以用as下载,as下载的方法如下
2.配置app下的build.gradle
以上两个都在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
然后就会看见生成classes文件夹
然后打开as下面的terminal 输入下面指令
cd是指进入debug文件夹
javah -jni后面的路径是第3步调用c的java类的class地址,注意是debug文件夹下面的哦
执行上面两句命令后会在debug文件夹下生成头文件
然后再main文件夹下新建一个jni文件夹,将刚刚剪切的头文件放到这个jni文件夹内
5.C文件
在jni文件夹新建一个C++文件 名字随便取
打开刚刚生成的头文件发现代码是这样的
复制上图红色部分到新建的c++文件中,然后加上一个返回语句
6.验证结果
最后打印结果为"哈哈哈",说明调用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文件
复制以上的文件夹内容到libs文件加下
最后删掉jni文件夹下的c文件,到此大功告成😆