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.pswp.cn/news/286420.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/286420.shtml
英文地址,請注明出處:http://en.pswp.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 …

File,FileInfo,Directory,DirectoryInfo

兩者的共同點&#xff1a; 一&#xff1a;都用于典型的操作&#xff0c;如復制、移動、重命名、創建、打開、刪除和追加到文件 二&#xff1a;默認情況下&#xff0c;將向所有用戶授予對新文件的完全讀/寫訪問權限。 兩者的區別&#xff1a; File類是靜態類&#xff0c;由…

C語言試題四之計算并輸出3到n之間所有素數的平方根之和

??個人主頁:個人主頁 ??系列專欄:C語言試題200例目錄 ??推薦一款刷算法、筆試、面經、拿大公司offer神器 ?? 點擊跳轉進入網站 ?作者簡介:大家好,我是碼莎拉蒂,CSDN博客專家(全站排名Top 50),阿里云博客專家、51CTO博客專家、華為云享專家 1、題目 請編寫函數…

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…

Mustache.js使用筆記(內容屬于轉載總結)

1、Mustache的語法很簡單&#xff0c;用兩個大括號標記要綁定的字段即可&#xff0c;“{{}}” Mustache主要的渲染方法為Mustache.render()&#xff0c;該方法有兩個參數&#xff0c;第一個為要渲染的模版&#xff0c; 也就是例子中的template&#xff0c;第二個就是數據源也就…

MAUI 自定義繪圖入門

在2022的5月份&#xff0c;某軟正式發布了 MAUI 跨平臺 UI 框架。我本來想著趁六一兒童節放假來寫幾篇關于 MAUI 入門的博客&#xff0c;可惜發現我不擅長寫很入門的博客。再加上 MAUI 似乎是為了趕發布日期而發布&#xff0c;只能勉強說能開發了&#xff0c;能用了。于是我就來…

C語言試題五之計算并輸出給定數組(長度為9)中每相鄰兩個元素之平均值的平方根之和

??個人主頁:個人主頁 ??系列專欄:C語言試題200例目錄 ??推薦一款刷算法、筆試、面經、拿大公司offer神器 ?? 點擊跳轉進入網站 ?作者簡介:大家好,我是碼莎拉蒂,CSDN博客專家(全站排名Top 50),阿里云博客專家、51CTO博客專家、華為云享專家 1、題目 請編寫函數…

【三維激光掃描】實驗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…

C語言試題六之使字符串的前導*號不得多于n個;若多于n個,則刪除多于的*號;若少于或等于n個,則什么也不做。字符串中間和尾部的*號不刪除。

??個人主頁:個人主頁 ??系列專欄:C語言試題200例目錄 ??推薦一款刷算法、筆試、面經、拿大公司offer神器 ?? 點擊跳轉進入網站 ?作者簡介:大家好,我是碼莎拉蒂,CSDN博客專家(全站排名Top 50),阿里云博客專家、51CTO博客專家、華為云享專家 1、題目 請編寫函數…

QT坑爹的事..

昨晚用QT寫了個網絡小程序&#xff0c;布局怎么都出不來add->widge()無效&#xff0c;原來因為在QMainWindow的基類下 需要人工添加一個 widge new QWidge() this->setCentralWidget(widget);最后在 widget->setLayout(mainLayout);才行 dialog和widge的基類 則沒有任…

企業有了程序員為什么還要用 低代碼/無代碼

一、備受“爭議”的無代碼/低代碼開發 在看這篇內容時&#xff0c;我們要知道&#xff0c;技術無時無刻不在進行發展&#xff0c;IT技術更是如此&#xff0c;快速的技術更新使得程序員在進行應用開發時效率更高&#xff1b;我記得在十多年前&#xff0c;開發一個普通的 HTML 頁…