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

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,如果执行无误的话,会如下图所示。

    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,调用方法。

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/286420.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

无代码iVX编程实现简单跳跃超级玛丽游戏

首先咱们打开 iVX 的在线编辑器&#xff1a;https://editor.ivx.cn/ 随后咱们选择2D游戏类型制作一个简单跳跃游戏&#xff1a; 接下来创建几个图片&#xff0c;并且添加物体&#xff0c;如图所示&#xff1a; 在此需要更改对应称重地面的阻尼值&#xff0c;让其能够缓慢降落…

【三维激光扫描】实验01:环境搭建CAD2014+StonexSiScan软件安装

目 录 一、CAD2014简体中文版安装1. 安装过程2. 激活过程二、Si-Scan安装1. 主程序安装2. 驱动安装一、CAD2014简体中文版安装 1. 安装过程 双击安装包:AutoCAD_2014_Simplified_Chinese_Win_64bit_dlm.sfx.exe,进行自解压。 解压完成后,如下图所示,点击【安装】。 接受许…

C# 11 新特性:原始字符串

之前我们经常需要使用 string 类型定义字符串文本&#xff0c;字符串文本用一对双引号括起来表示&#xff1a;var str "Hello MyIO";字符串可包含任何字符文本&#xff0c;但是有些字符需要转义才能表示&#xff0c;比如双引号要转义成\"&#xff1a;var str …

bzoj1011

因为允许5%的误差。。所以把&#xff1e;一定长度的一段看成一段近似计算就行了。。 1 #include<cstdio>2 #include<cstdlib>3 #include<cstring>4 #include<ctime>5 #include<cmath>6 #include<iostream>7 #include<algorithm>8 #i…

一名全栈工程师的必备“百宝箱”

摘要&#xff1a;全栈工程师&#xff0c;也叫全端工程师&#xff0c;是指掌握多种技能&#xff0c;并能利用多种技能独立完成产品的人。全栈工程师熟悉多种开发语言&#xff0c;同时具备前端和后台开发能力&#xff0c;从需求分析&#xff0c;原型设计到产品开发&#xff0c;测…

为VMware虚拟主机添加新磁盘

轨迹: 关闭VMware虚拟主机 ---> 虚拟机 ---> 设置 ---> 硬件 ---> 硬盘 ---> 添加 ---> (弹出添加硬件向导)硬盘 ---> 磁盘类型 ---> 选择磁盘 ---> 指定磁盘容量(最好选择“将虚拟磁盘存储为单个文件”) ---> 指定磁盘文件 ---> 点击“完成…

【ArcGIS风暴】全站仪、RTK测量坐标数据在CASS和ArcGIS中展点的区别和联系(带数据)

ArcGIS展经纬度点完整教程:【ArcGIS风暴】ArcGIS 10.2导入Excel数据X、Y坐标(经纬度、平面坐标),生成Shapefile点数据图层 目录 1. CASS展点操作步骤2. ArcGIS展点操作步骤3. 案例数据下载RTK或全站仪地面实测的三维坐标数据文件一般包括点号,编码,东坐标,北坐标,高程等…

php一篇文零基础到制作在线图片编辑网站赚钱(gif压缩、九宫格裁剪、等比裁剪、大小变换)【php华为云实战】

注意本篇文适用于&#xff1a; 零基础小白想要了解一下php开发或者网站开发的同学&#xff08;但是注意&#xff0c;零基础你可以通过本篇完成&#xff0c;但是由于是速成会有一些难度&#xff0c;本篇内容由于是速成&#xff0c;有一些额外知识点&#xff0c;不会可以来问我1…

MAUI 自定义绘图入门

在2022的5月份&#xff0c;某软正式发布了 MAUI 跨平台 UI 框架。我本来想着趁六一儿童节放假来写几篇关于 MAUI 入门的博客&#xff0c;可惜发现我不擅长写很入门的博客。再加上 MAUI 似乎是为了赶发布日期而发布&#xff0c;只能勉强说能开发了&#xff0c;能用了。于是我就来…

【三维激光扫描】实验02:StonexSiScan新建项目、加载点云数据

文章目录 1. 新建工程2. 打开工程3. 加载点云1. 新建工程 打开StonexSiScan点云后处理软件,点击【新建】按钮。 选择工程存放路径,输入工程名称。 2. 打开工程 点击【打开】按钮。

eBPF 在云原生环境中的应用

端午假期&#xff0c;我翻译了 OReilly 的报告《什么是 eBPF》&#xff0c;其中我觉得第五章「云原生环境中的 eBPF」解答了我心中的很多疑惑&#xff0c;比较不错&#xff0c;分享给大家。下面是第五章译文。《什么是 eBPF》中文版封面近年来&#xff0c;云原生应用已呈指数级…

使用HtmlAgilityPack抓取网页数据

XPath路径表达式&#xff0c;主要是对XML文档中的节点进行搜索&#xff0c;通过XPath表达式可以对XML文档中的节点位置进行快速定位和访问&#xff0c;html也是也是一种类似于xml的标记语言&#xff0c;但是语法没有那么严谨&#xff0c;在codeplex里有一个开源项目HtmlAgility…

企业有了程序员为什么还要用 低代码/无代码

一、备受“争议”的无代码/低代码开发 在看这篇内容时&#xff0c;我们要知道&#xff0c;技术无时无刻不在进行发展&#xff0c;IT技术更是如此&#xff0c;快速的技术更新使得程序员在进行应用开发时效率更高&#xff1b;我记得在十多年前&#xff0c;开发一个普通的 HTML 页…

【三维激光扫描技术】原理、方法及实验图文教程目录

《三维激光扫描技术》专栏讲述目前最先进、最流行的三维激光技术&#xff0c;包括三维激光扫描技术原理&#xff0c;三维测距原理&#xff0c;国内外三维扫描设备&#xff0c;点云特点&#xff0c;三维建模&#xff0c;三维激光优势、应用领域&#xff0c;应用技术案例等。 文章…

求最长回文串

Given a string S, find the longest palindromic substring in S. You may assume that the maximum length of S is 1000, and there exists one unique longest palindromic substring. 转载于:https://www.cnblogs.com/yangscode/p/5017527.html

实训三(cocos2dx 3.x 打包apk)

上一篇文章《实训二&#xff08;cocos2dx 2.x 打包apk&#xff09;》简单的讲述的利用cocos2dx 2.x引擎在windows平台上打包apk的方法与过程&#xff0c;本文将介绍3.x版本引擎&#xff0c;如何打包apk的问题。 首先&#xff0c;Cygwin在3.x版本引擎上已经用不到了&#xff0c;…

网页精美动效/动画制作 按钮鼠标悬浮动效基础 01《炫彩网页 iVX 无代码动效/动画制作》

编辑器地址&#xff1a;https://editor.ivx.cn/ 一、准备工作 进入编辑页后&#xff0c;选择其中一种应用类型创建应用&#xff0c;在此以相对应用作为示例&#xff1a; 创建好应用后在此选择对应的屏幕作为示例演示&#xff0c;在此选择电脑屏幕作为对应的大小&#xff1a…

Java并发编程 - Executor,Executors,ExecutorService, CompletionServie,Future,Callable

一、Exectuor框架简介 Java从1.5版本开始&#xff0c;为简化多线程并发编程&#xff0c;引入全新的并发编程包:java.util.concurrent及其并发编程框架&#xff08;Executor框架&#xff09;。 Executor框架是指java 5中引入的一系列并发库中与executor相关的一些功能类&a…

网页精美动效/动画制作 按钮鼠标悬浮动效的注意点 02《炫彩网页 iVX 无代码动效/动画制作》

一、按钮动效的使用 在上一节中&#xff0c;我们创建了一个动效&#xff0c;但是并没有使用&#xff0c;在此我们给按钮设置一个悬浮事件&#xff0c;当鼠标悬浮在按钮之上后就调用该动效&#xff0c;点击按钮添加事件&#xff1a; 点击按钮添加事件后将会出现一个事件编辑框…

WPF 实现带明细的环形图表

本文经原作者授权以原创方式二次分享&#xff0c;欢迎转载、分享。原文作者&#xff1a;普通的地球人原文地址&#xff1a;https://www.cnblogs.com/tsliwei/p/7155616.htmlGithub地址&#xff1a;https://github.com/WPFDevelopersOrg/WPFDevelopers.Charts大体思路图表使用Ar…