NDK 實踐-應用簽名校驗。java
Android 應用簽名是應用打包過程的重要步驟之一,Google 要求全部的應用必須被簽名才能夠安裝到 Android 操做系統中。android
應用簽名不能保證 APK 不被篡改,只是爲了可以校驗出 APK 是否被篡改。在系統安裝過程當中,若是發現 APK 被篡改,安裝就會失敗。git
爲了相對安全,一些敏感操做每每會使用 Native 的方式來實現。可是別人能夠經過 APK 文件獲取到咱們的 .so
文件,進而使用咱們的 .so
。github
可是應用簽名的證書只有咱們持有,咱們能夠經過 Native 校驗簽名來判斷是不是咱們本身的應用,若是不是能夠返回錯誤或直接退出應用。算法
像以前同樣建立一個 Native C++ 模板項目數組
新建的 Android 項目,默認的簽名證書在用戶根目錄的 .android 目錄中 ~/.android/debug.keystore
安全
$ keytool -list -v -keystore debug.keystore 輸入密鑰庫口令: android
public class SignatureUtil { public static String getSignatureStr(Context context) { Signature signature = getSignature(context); byte[] cert = signature.toByteArray(); try { MessageDigest md5 = MessageDigest.getInstance("MD5"); MessageDigest sha1 = MessageDigest.getInstance("SHA1"); MessageDigest sha256 = MessageDigest.getInstance("SHA256"); byte[] md5Key = md5.digest(cert); byte[] sha1Key = sha1.digest(cert); byte[] sha256Key = sha256.digest(cert); return String.format("MD5: %s\n\nSHA1: %s\n\nSHA-256: %s", byteArrayToString(md5Key), byteArrayToString(sha1Key), byteArrayToString(sha256Key) ); } catch (Exception e) { return ""; } } public static Signature getSignature(Context argContext) { Signature signature = null; try { String packageName = argContext.getPackageName(); PackageManager packageManager = argContext.getPackageManager(); PackageInfo packageInfo = packageManager.getPackageInfo(packageName, GET_SIGNATURES); Signature[] signatures = packageInfo.signatures; signature = signatures[0]; } catch (NameNotFoundException e) { e.printStackTrace(); } return signature; } private static String byteArrayToString(byte[] array) { StringBuilder hexString = new StringBuilder(); for (int i = 0; i < array.length; i++) { String appendString = Integer.toHexString(0xFF & array[i]).toUpperCase(); if (appendString.length() == 1) hexString.append("0"); hexString.append(appendString); if(i < array.length - 1) hexString.append(":"); } return hexString.toString(); } }
這裏用到了 HASH 算法,Android NDK JNI 入門筆記-day04-NDK實現Hash算法oracle
jbyteArray getSignatureByte(JNIEnv *env, jobject context); void hashByteArray(HASH type, const void* data, size_t numBytes, char* resultData); void formatSignature(char* data, char* resultData); extern "C" JNIEXPORT jstring JNICALL Java_com_ihubin_ndkjni_NativeUtil_getSignature(JNIEnv *env, jclass clazz, jobject context) { jbyteArray cert_byteArray = getSignatureByte(env, context); jsize size = env->GetArrayLength(cert_byteArray); jbyte* jbyteArray = new jbyte[size]; env->GetByteArrayRegion(cert_byteArray, 0, size, jbyteArray); char certMD5[128] = {0}; hashByteArray(HASH_MD5, jbyteArray, size, certMD5); char certSHA1[128] = {0}; hashByteArray(HASH_SHA1, jbyteArray, size, certSHA1); char certSHA256[128] = {0}; hashByteArray(HASH_SHA256, jbyteArray, size, certSHA256); LOGD("MD5: %s", certMD5); LOGD("SHA1: %s", certSHA1); LOGD("SHA256: %s", certSHA256); char resultStr[1000] = {0}; strcat(resultStr, "MD5: "); strcat(resultStr, certMD5); strcat(resultStr, "\n\nSHA1: "); strcat(resultStr, certSHA1); strcat(resultStr, "\n\nSHA256: "); strcat(resultStr, certSHA256); return env->NewStringUTF(resultStr); } // Native 從 Context 中獲取簽名 jbyteArray getSignatureByte(JNIEnv *env, jobject context) { // Context 的類 jclass context_clazz = env->GetObjectClass(context); // 獲得 getPackageManager 方法的 ID jmethodID methodID_getPackageManager = env->GetMethodID(context_clazz, "getPackageManager", "()Landroid/content/pm/PackageManager;"); // 得到 PackageManager 對象 jobject packageManager = env->CallObjectMethod(context, methodID_getPackageManager); // 得到 PackageManager 類 jclass packageManager_clazz=env->GetObjectClass(packageManager); // 獲得 getPackageInfo 方法的 ID jmethodID methodID_getPackageInfo=env->GetMethodID(packageManager_clazz,"getPackageInfo", "(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;"); // 獲得 getPackageName 方法的 ID jmethodID methodID_getPackageName = env->GetMethodID(context_clazz,"getPackageName", "()Ljava/lang/String;"); // 得到當前應用的包名 jobject application_package_obj = env->CallObjectMethod(context, methodID_getPackageName); jstring application_package = static_cast<jstring>(application_package_obj); const char* package_name = env->GetStringUTFChars(application_package, 0); LOGD("packageName: %s", package_name); // 得到 PackageInfo jobject packageInfo = env->CallObjectMethod(packageManager, methodID_getPackageInfo, application_package, 64); jclass packageinfo_clazz = env->GetObjectClass(packageInfo); // 獲取簽名 jfieldID fieldID_signatures = env->GetFieldID(packageinfo_clazz, "signatures", "[Landroid/content/pm/Signature;"); jobjectArray signature_arr = (jobjectArray)env->GetObjectField(packageInfo, fieldID_signatures); // Signature 數組中取出第一個元素 jobject signature = env->GetObjectArrayElement(signature_arr, 0); // 讀 signature 的 ByteArray jclass signature_clazz = env->GetObjectClass(signature); jmethodID methodID_byteArray = env->GetMethodID(signature_clazz, "toByteArray", "()[B"); jobject cert_obj = env->CallObjectMethod(signature, methodID_byteArray); jbyteArray cert_byteArray = static_cast<jbyteArray>(cert_obj); return cert_byteArray; } // 得到簽名的 MD5 SHA1 SHA256 void hashByteArray(HASH type, const void* data, size_t numBytes, char* resultData){ if(type == HASH_MD5) { MD5 md5; std::string md5String = md5(data, numBytes); int len = md5String.length()+1; char * tabStr = new char [md5String.length()+1]; strcpy(tabStr, md5String.c_str()); formatSignature(tabStr, resultData); } else if(type == HASH_SHA1) { SHA1 sha1; std::string sha1String = sha1(data, numBytes); char * tabStr = new char [sha1String.length()+1]; strcpy(tabStr, sha1String.c_str()); formatSignature(tabStr, resultData); } else if(type == HASH_SHA256) { SHA256 sha256; std::string sha256String = sha256(data, numBytes); char * tabStr = new char [sha256String.length()+1]; strcpy(tabStr, sha256String.c_str()); formatSignature(tabStr, resultData); } } // 格式化輸出 void formatSignature(char* data, char* resultData) { int resultIndex = 0; int length = strlen(data); for(int i = 0; i < length; i++) { resultData[resultIndex] = static_cast<char>(toupper(data[i])); if(i % 2 == 1 && i != length -1) { resultData[resultIndex+1] = ':'; resultIndex+=2; } else { resultIndex++; } } }
至此,咱們已經學會了在 Android 項目中 Native 進行簽名校驗,應用安全提高了。
代碼:app
參考資料: