Skip to content

Instantly share code, notes, and snippets.

@kaiyan910
Created January 18, 2018 10:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kaiyan910/70d36da89e971dc314c4a86a180ba5d1 to your computer and use it in GitHub Desktop.
Save kaiyan910/70d36da89e971dc314c4a86a180ba5d1 to your computer and use it in GitHub Desktop.
Protecting your Secret by using NDK
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"
}
}
}
cmake_minimum_required(VERSION 3.4.1)
add_library(secret_lib SHARED
secret_lib.c)
target_link_libraries(secret_lib
android
log)
package com.app
public class Secret {
static {
System.loadLibrary("secret_lib");
}
public static native void init(Context context);
public static native String secret();
}
#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