Android使用C/C++来保存密钥

本文主要介绍如何通过native方法调用取出密钥,以替代原本直接写在Java中,或写在gradle脚本中的不安全方式。

为什么要这么做

如果需要在本地存储一个密钥串,典型的方式有 
1. 直接写在java source code中 
2. 写在gradle脚本中,使用BuildConfig读取 
3. 写在gradle.properties中,再到gradle脚本中读取,后面同第二点 
4. 使用native方法,读取存放在C/C++中的字段

本质上来讲方式1,2,3**没有什么区别**。1为硬编码,2可以做到在不同的BuildType使用不同的密钥,3将配置写到脚本之外,方便管理查看。

然而,在项目编译之后,方式1,2,3都会把密钥直接替换到字节码文件中,对于反编译如此方便的Android来说,无疑是将密钥拱手让人。

因此,将密钥放在难以反编译的C/C++代码中,是一个解决的办法。

怎么做

java怎么调用C/C++方法

如果想详细的明白以下步骤,请查阅JNI相关的资料,此处仅列出大概步骤。

  1. 下载ndk
  2. 在类中声明native方法。
  public class A {public native String nativeMethod();}
  • 1
  • 2
  • 3
  • 4
  • 5
  1. 在项目根目录下新建一个名为jni的目录,并在其中新建三个文件,分别为:

    • Android.mk (名字固定)
    • Application.mk (名字固定)
    • Project.cpp (名字随意)
  2. Android.mk

    文件的内容如下:

    LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)LOCAL_MODULE := project
    LOCAL_SRC_FILES := Project.cppinclude $(BUILD_SHARED_LIBRARY)
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    除了LOCAL_MODULELOCAL_SRC_FILES之外,其它都是固定的。前者是这个库的名称,后者是cpp文件的路径。

  3. Application.mk

    文件的内容如下:

    APP_ABI := all
    • 1

    意思是生成所有平台的so库。

  4. Project.cpp

    
    #include <jni.h>#include <stdio.h>#include <string.h>#ifdef __cplusplusextern "C"{#endifjstring Java_[ClassAPackage]_A_nativeMethod(JNIEnv *env,jobject thiz) {// 返回密钥return (env)->NewStringUTF("你的密钥");}#ifdef __cplusplus}#endif
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    ClassAPackage为类A在java中的包名全称,并将分隔的.改成_

  5. 以上就把native的代码写好了,在第一步下载好的NDK里面,使用解压后目录下的一个叫ndk-build的程序。cdjni目录下,执行ndk-build,如果执行无误的话,会如下图所示。

  6. 执行完上一步之后,会生成一个与jni同级的目录libs,将libs下的文件拷贝到app/src/main/jniLibs目录下。

  7. 在类A中,加入以下静态语句块,引入编译好的native库。

    static {System.loadLibrary("project");
    }
    • 1
    • 2
    • 3

    这里的"project"就是在第4步中的LOCAL_MODULE的值。

  8. 到了这一步,就可以拿到native代码中保存的值了。

有啥问题不

肯定有啊。

试想,如果有人将我们的.so包拿到了(把apk解包就能拿到),然后自己声明native方法,load本地库,然后调用native方法,那么我们做的这么多是不是都白费了?是的,白费了。所以我们需要改进。

如何改进

有什么东西,只有你自己知道,并且有的,但是别人不能模仿的?--应用签名。

那么,我们在native代码里面,先验证一下应用的签名是否是我们的,如果是,才返回正确的密钥。

  1. 获取签名唯一字符串 
    BuildVariants切换到release,也就是使用生产版本的签名文件,然后将下面的代码粘贴至任意一个Activity内,在控制台里,可以获取这个字符串。
public void getSignInfo() {try {PackageInfo packageInfo = getPackageManager().getPackageInfo(getPackageName(), PackageManager.GET_SIGNATURES);Signature[] signs = packageInfo.signatures;Signature sign = signs[0];System.out.println(sign.toCharsString());} catch (Exception e) {e.printStackTrace();}}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  1. 修改native方法的声明,传入Context对象。
public native String nativeMethod(Context context);
  • 1
  1. 修改C++代码,添加验证逻辑。
#include <jni.h>
#include <stdio.h>
#include <string.h>#ifdef __cplusplus
extern "C"{
#endifstatic jclass contextClass;
static jclass signatureClass;
static jclass packageNameClass;
static jclass packageInfoClass;/**之前生成好的签名字符串
*/
const char* RELEASE_SIGN = "第1步,生成好的字符串";/*根据context对象,获取签名字符串
*/
const char* getSignString(JNIEnv *env,jobject contextObject) {jmethodID getPackageManagerId = (env)->GetMethodID(contextClass, "getPackageManager","()Landroid/content/pm/PackageManager;");jmethodID getPackageNameId = (env)->GetMethodID(contextClass, "getPackageName","()Ljava/lang/String;");jmethodID signToStringId = (env)->GetMethodID(signatureClass, "toCharsString","()Ljava/lang/String;");jmethodID getPackageInfoId = (env)->GetMethodID(packageNameClass, "getPackageInfo","(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;");jobject packageManagerObject =  (env)->CallObjectMethod(contextObject, getPackageManagerId);jstring packNameString =  (jstring)(env)->CallObjectMethod(contextObject, getPackageNameId);jobject packageInfoObject = (env)->CallObjectMethod(packageManagerObject, getPackageInfoId,packNameString, 64);jfieldID signaturefieldID =(env)->GetFieldID(packageInfoClass,"signatures", "[Landroid/content/pm/Signature;");jobjectArray signatureArray = (jobjectArray)(env)->GetObjectField(packageInfoObject, signaturefieldID);jobject signatureObject =  (env)->GetObjectArrayElement(signatureArray,0);return (env)->GetStringUTFChars((jstring)(env)->CallObjectMethod(signatureObject, signToStringId),0);
}jstring Java_[ClassAPackage]_A_nativeMethod(JNIEnv *env,jobject thiz,jobject contextObject) {const char* signStrng =  getSignString(env,contextObject);if(strcmp(signStrng,RELEASE_SIGN)==0)//签名一致  返回合法的 api key,否则返回错误{return (env)->NewStringUTF("你的密钥");}else{return (env)->NewStringUTF("error");}
}/**利用OnLoad钩子,初始化需要用到的Class类.
*/
JNIEXPORT jint JNICALL JNI_OnLoad (JavaVM* vm,void* reserved){JNIEnv* env = NULL;jint result=-1;if(vm->GetEnv((void**)&env, JNI_VERSION_1_4) != JNI_OK)return result;contextClass = (jclass)env->NewGlobalRef((env)->FindClass("android/content/Context"));signatureClass = (jclass)env->NewGlobalRef((env)->FindClass("android/content/pm/Signature"));packageNameClass = (jclass)env->NewGlobalRef((env)->FindClass("android/content/pm/PackageManager"));packageInfoClass = (jclass)env->NewGlobalRef((env)->FindClass("android/content/pm/PackageInfo"));return JNI_VERSION_1_4;}#ifdef __cplusplus
}
#endif
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69

getSignString方法也许看起很复杂,如果熟悉java反射的Api的话,其实很类似,就是拿到方法Id,调用方法。

**以上就是本文的讨论内容,有些技术细节没有深入介绍,请自行查阅相关资料。 
如果有不同的方式,欢迎讨论**

Android使用C/C++来保存密钥相关推荐

  1. java appkey_1.新建Android studio工程2.新建class:AppKey.java.主要为了保存密钥代码块package com...adminap...

    1.新建Android studio工程 2.新建class:AppKey.java.主要为了保存密钥 代码块 package com...adminapp.lib.utils.jni; /** * ...

  2. [Android] 拍照、截图、保存并显示在ImageView控件中

    最近在做Android的项目,其中部分涉及到图像处理的内容.这里先讲述如何调用Camera应用程序进行拍照,并截图和保存显示在ImageView控件中以及遇到的困难和解决方法.     PS:作者购买 ...

  3. Android安全开发之浅谈密钥硬编码

    Android安全开发之浅谈密钥硬编码 作者:伊樵.呆狐@阿里聚安全 1 简介 在阿里聚安全的漏洞扫描器中和人工APP安全审计中,经常发现有开发者将密钥硬编码在Java代码.文件中,这样做会引起很大风 ...

  4. android 读取excel数据并保存为xml文件

    今天,简单讲讲android如何  读取excel数据并保存为xml文件. 最近,我这边需要把客户翻译的Excel字符资源作为xml字符资源,当时自己是一个一个的复制,发现效率太低.后来,在网上搜 ...

  5. android图片保存形式,Android应用开发之Android ScrollView截图和图片保存到相册的方式...

    本文将带你了解Android应用开发之Android ScrollView截图和图片保存到相册的方式,希望本文对大家学Android有所帮助. 1.1首先来看你一种截取屏幕,这种代码有缺陷,只能截取一 ...

  6. Android APK系列3-------使用platform密钥来给apk文件签名

    Android APK系列3-------使用platform密钥来给apk文件签名 1.使用platform密钥对apk进行签名 1.1.进入<Android_Source_Path>\ ...

  7. 分别使用liunx,windows命令和android代码,快速生成facebook密钥散列

    安卓集成facebook并且上传应用到facebook中,需要在facebook后台填入facebook的密钥散列,这里分别使用liunx,windows命令和android代码,快速生成facebo ...

  8. 建议118:使用SecureString保存密钥等机密字符串

    建议118:使用SecureString保存密钥等机密字符串 托管代码中的字符串是一类特殊的对象,它们不可用被改变.每次使用System.String类张的方法之一时,或者使用此类型进行运算时(如赋值 ...

  9. android读取excel数据库,Android 读取Excel数据并保存在本地数据库

    在工作中遇到需要将Excel的数据读取出来并保存在本地数据库中的操作,数据如下: 图片.png 需要做以下准备: 读取Excel的jar包[文章末尾会分享该jar包] 保存数据的数据库框架,在这里我们 ...

最新文章

  1. 人工智能与区块链交换了眼神儿,之后呢……
  2. 机虚拟磁盘附加到计算机上,解决在NAS的共享文件夹下的VHDX虚拟磁盘无法附加到系统...
  3. 工作流程怎么安排?用Edraw Max轻松创建工作流程图!
  4. CentOS中安装的Gitlab忘记管理员密码怎样重置密码
  5. IDEA中新建SpringBoot项目时提示:Artifact contains illegal characters
  6. C++smallest circle 获取外接给定点集的最小圆的中心和半径算法(附完整源码)
  7. 分类算法支持向量机(SVM) 简介与入门
  8. 辽宁交通高等专科学校计算机专业宿舍,辽宁省交通高等专科学校宿舍条件怎么样 有独立卫生间和空调吗...
  9. 2018蓝桥杯A组:方格计数(3种方法)
  10. linux命令mount是什么,Linux命令——mount、umount
  11. ARM-linux的启动流程
  12. 蓝桥杯历年真题分类汇总(史上最全版本,一定不要错过)
  13. 2021-09-10 参数计算
  14. 钱包:BUMO 小布口袋 APP 用户手册
  15. Hibernate高级映射技术(二)自定义数据类型StringMap (转载用于收藏)
  16. 从零开始学版图(一)——反相器版图
  17. Dropbox和其他备份产品概述
  18. Html 标签中的Alt和Title
  19. Oracle计算指定日期内的工作日(不包含周末)
  20. 如何退出git log状态

热门文章

  1. 加入域--深入理解DNS在域中作用
  2. SQL Server的数据导入MySQL数据库方法简介
  3. 哎,最近心情非常烦乱!
  4. 可视化ASP.Net Core Web API健康检查
  5. 在VS Code中执行SQL查询,是怎样一种体验?
  6. 为WPF播放GIF伤神不?
  7. 更新两个WPF开源项目
  8. C# WPF项目实战(经典)
  9. 微软2020开源回顾:止不住的挨骂,停不下的贡献
  10. dnSpy反编译、部署调试神器