Created
January 18, 2018 10:37
-
-
Save kaiyan910/70d36da89e971dc314c4a86a180ba5d1 to your computer and use it in GitHub Desktop.
Protecting your Secret by using NDK
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
android { | |
defaultConfig { | |
externalNativeBuild { | |
cmake { | |
arguments '-DANDROID_TOOLCHAIN=clang' | |
} | |
} | |
ndk { | |
moduleName 'secret_lib' | |
abiFilters 'x86', 'x86_64', 'armeabi', 'armeabi-v7a', 'arm64-v8a' | |
} | |
} | |
externalNativeBuild { | |
cmake { | |
path "src/main/cpp/CMakeLists.txt" | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
cmake_minimum_required(VERSION 3.4.1) | |
add_library(secret_lib SHARED | |
secret_lib.c) | |
target_link_libraries(secret_lib | |
android | |
log) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.app | |
public class Secret { | |
static { | |
System.loadLibrary("secret_lib"); | |
} | |
public static native void init(Context context); | |
public static native String secret(); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <jni.h> | |
#include <stdbool.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <android/log.h> | |
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, "NDK", __VA_ARGS__) | |
static bool is_valid = false; | |
const char *app_signature_sha1_debug = "<--- ADD THE DEBUG APP KEY HERE --->"; | |
const char *app_signature_sha1 = "<--- ADD THE APP RELEASE KEY HERE --->"; | |
const char HexCode[] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; | |
JNIEXPORT void JNICALL | |
Java_com_app_Secret_init(JNIEnv *env, jclass clz, jobject context_object) { | |
jclass context_class = (*env)->GetObjectClass(env, context_object); | |
//context.getPackageManager() | |
jmethodID methodId = (*env)->GetMethodID(env, context_class, "getPackageManager", "()Landroid/content/pm/PackageManager;"); | |
jobject package_manager_object = (*env)->CallObjectMethod(env, context_object, methodId); | |
if (package_manager_object == NULL) { | |
LOGE("getPackageManager() Failed!"); | |
return; | |
} | |
//context.getPackageName() | |
methodId = (*env)->GetMethodID(env, context_class, "getPackageName", "()Ljava/lang/String;"); | |
jstring package_name_string = (jstring) (*env)->CallObjectMethod(env, context_object, methodId); | |
if (package_name_string == NULL) { | |
LOGE("getPackageName() Failed!"); | |
return; | |
} | |
(*env)->DeleteLocalRef(env, context_class); | |
//PackageManager.getPackageInfo(Sting, int) | |
jclass pack_manager_class = (*env)->GetObjectClass(env, package_manager_object); | |
methodId = (*env)->GetMethodID(env, pack_manager_class, "getPackageInfo", "(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;"); | |
(*env)->DeleteLocalRef(env, pack_manager_class); | |
jobject package_info_object = (*env)->CallObjectMethod(env, package_manager_object, methodId, package_name_string, 0x40); | |
if (package_info_object == NULL) { | |
LOGE("getPackageInfo() Failed!"); | |
return; | |
} | |
(*env)->DeleteLocalRef(env, package_manager_object); | |
//PackageInfo.signatures[0] | |
jclass package_info_class = (*env)->GetObjectClass(env, package_info_object); | |
jfieldID fieldId = (*env)->GetFieldID(env, package_info_class, "signatures", "[Landroid/content/pm/Signature;"); | |
(*env)->DeleteLocalRef(env, package_info_class); | |
jobjectArray signature_object_array = (jobjectArray) (*env)->GetObjectField(env, package_info_object, fieldId); | |
if (signature_object_array == NULL) { | |
LOGE("PackageInfo.signatures[] is null"); | |
return; | |
} | |
jobject signature_object = (*env)->GetObjectArrayElement(env, signature_object_array, 0); | |
(*env)->DeleteLocalRef(env, package_info_object); | |
//Signature.toByteArray() | |
jclass signature_class = (*env)->GetObjectClass(env, signature_object); | |
methodId = (*env)->GetMethodID(env, signature_class, "toByteArray", "()[B"); | |
(*env)->DeleteLocalRef(env, signature_class); | |
jbyteArray signature_byte = (jbyteArray) (*env)->CallObjectMethod(env, signature_object, methodId); | |
//new ByteArrayInputStream | |
jclass byte_array_input_class = (*env)->FindClass(env, "java/io/ByteArrayInputStream"); | |
methodId = (*env)->GetMethodID(env, byte_array_input_class, "<init>", "([B)V"); | |
jobject byte_array_input = (*env)->NewObject(env, byte_array_input_class, methodId, signature_byte); | |
//CertificateFactory.getInstance("X.509") | |
jclass certificate_factory_class = (*env)->FindClass(env, "java/security/cert/CertificateFactory"); | |
methodId = (*env)->GetStaticMethodID(env, certificate_factory_class, "getInstance", "(Ljava/lang/String;)Ljava/security/cert/CertificateFactory;"); | |
jstring x_509_jstring = (*env)->NewStringUTF(env, "X.509"); | |
jobject cert_factory = (*env)->CallStaticObjectMethod(env, certificate_factory_class, methodId, x_509_jstring); | |
//certFactory.generateCertificate(byteIn); | |
methodId = (*env)->GetMethodID(env, certificate_factory_class, "generateCertificate", ("(Ljava/io/InputStream;)Ljava/security/cert/Certificate;")); | |
jobject x509_cert = (*env)->CallObjectMethod(env, cert_factory, methodId, byte_array_input); | |
(*env)->DeleteLocalRef(env, certificate_factory_class); | |
//cert.getEncoded() | |
jclass x509_cert_class = (*env)->GetObjectClass(env, x509_cert); | |
methodId = (*env)->GetMethodID(env, x509_cert_class, "getEncoded", "()[B"); | |
jbyteArray cert_byte = (jbyteArray) (*env)->CallObjectMethod(env, x509_cert, methodId); | |
(*env)->DeleteLocalRef(env, x509_cert_class); | |
//MessageDigest.getInstance("SHA1") | |
jclass message_digest_class = (*env)->FindClass(env, "java/security/MessageDigest"); | |
methodId = (*env)->GetStaticMethodID(env, message_digest_class, "getInstance", "(Ljava/lang/String;)Ljava/security/MessageDigest;"); | |
jstring sha1_jstring = (*env)->NewStringUTF(env, "SHA1"); | |
jobject sha1_digest = (*env)->CallStaticObjectMethod(env, message_digest_class, methodId, sha1_jstring); | |
//sha1.digest (certByte) | |
methodId = (*env)->GetMethodID(env, message_digest_class, "digest", "([B)[B"); | |
jbyteArray sha1_byte = (jbyteArray) (*env)->CallObjectMethod(env, sha1_digest, methodId, cert_byte); | |
(*env)->DeleteLocalRef(env, message_digest_class); | |
//toHexString | |
jsize array_size = (*env)->GetArrayLength(env, sha1_byte); | |
jbyte *sha1 = (*env)->GetByteArrayElements(env, sha1_byte, NULL); | |
char hex_sha[array_size*2+1]; | |
for (int i = 0; i < array_size; ++i) { | |
hex_sha[2*i] = HexCode[((unsigned char)sha1[i])/16]; | |
hex_sha[2*i+1] = HexCode[((unsigned char)sha1[i])%16]; | |
} | |
hex_sha[array_size * 2] = '\0'; | |
LOGE("sha1=[%s]", hex_sha); // remember to hide this log for production | |
if (strcmp(hex_sha, app_signature_sha1) == 0 || strcmp(hex_sha, app_signature_sha1_debug) == 0) { | |
LOGE("Alright you are authorized to use this library."); | |
is_valid = true; | |
} else { | |
LOGE("Sorry you have no permission to use this library."); | |
} | |
return; | |
} | |
JNIEXPORT jstring JNICALL | |
Java_com_app_Secret_secret(JNIEnv *env, jclass type) { | |
if (is_valid) { | |
return (*env)->NewStringUTF(env, "secret"); | |
} else { | |
return (*env)->NewStringUTF(env, ""); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment