日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

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

發布時間:2023/12/4 c/c++ 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 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相關的資料,此處僅列出大概步驟。

  • 下載ndk
  • 在類中聲明native方法。
  • public class A {public native String nativeMethod();}
    • 1
    • 2
    • 3
    • 4
    • 5
  • 在項目根目錄下新建一個名為jni的目錄,并在其中新建三個文件,分別為:

    • Android.mk (名字固定)
    • Application.mk (名字固定)
    • Project.cpp (名字隨意)
  • 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_MODULE和LOCAL_SRC_FILES之外,其它都是固定的。前者是這個庫的名稱,后者是cpp文件的路徑。

  • Application.mk

    文件的內容如下:

    APP_ABI := all
    • 1

    意思是生成所有平臺的so庫。

  • 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中的包名全稱,并將分隔的.改成_

  • 以上就把native的代碼寫好了,在第一步下載好的NDK里面,使用解壓后目錄下的一個叫ndk-build的程序。cd到jni目錄下,執行ndk-build,如果執行無誤的話,會如下圖所示。

  • 執行完上一步之后,會生成一個與jni同級的目錄libs,將libs下的文件拷貝到app/src/main/jniLibs目錄下。

  • 在類A中,加入以下靜態語句塊,引入編譯好的native庫。

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

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

  • 到了這一步,就可以拿到native代碼中保存的值了。

  • 有啥問題不

    肯定有啊。

    試想,如果有人將我們的.so包拿到了(把apk解包就能拿到),然后自己聲明native方法,load本地庫,然后調用native方法,那么我們做的這么多是不是都白費了?是的,白費了。所以我們需要改進。

    如何改進

    有什么東西,只有你自己知道,并且有的,但是別人不能模仿的?--應用簽名。

    那么,我們在native代碼里面,先驗證一下應用的簽名是否是我們的,如果是,才返回正確的密鑰。

  • 獲取簽名唯一字符串?
    將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
  • 修改native方法的聲明,傳入Context對象。
  • public native String nativeMethod(Context context);
    • 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++来保存密钥的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。