개발 관련/Android
NDK 를 이용하여 Signaiture (서명) 값 가져오기.
snoworca
2015. 4. 8. 18:40
앱의 위변조를 방지하기 위하여 서명 값을 가져와서 서버에 미리 등록된 값과 비교하는 과정이 필요하다.
하지만, 안드로이드 Java 코드상으로 제공되는 API 를 활용하는 것은 안전하지 못하다.
(안드로이드 자바 코드상에서 서명 값 가져오기 : http://www.dev.re.kr/70)
그렇기 때문에 JNI 를 활용해야 한다. context 를 인자값으로 받아서 jni 에서 제공되는 리플렉션을 사용하여 SHA1 으로 변환된 서명 값을 가져오는 코드를 첨부한다.
주의할 점은 아래 코드는 C++ 로 작성되었기 때문에 C 에서 사용하려면 약간의 수정이 필요하다.
이를테면
env->GetObjectClass(context);
를 C로 표현 하려면
(*env)->GetObjectClass(env, context);
로 변환해야 한다.
스크롤바 때문에 코드를 보기 불편할 수 있으므로 파일도 추가한다.
// Context 를 인자값을 받아서 Signature 의 값을 얻는다. char* getSignaiture(JNIEnv *env, jobject context) { jstring packageName; jobject packageManagerObj; jobject packageInfoObj; jclass contextClass = env->GetObjectClass( context); jmethodID getPackageNameMid = env->GetMethodID( contextClass, "getPackageName", "()Ljava/lang/String;"); jmethodID getPackageManager = env->GetMethodID( contextClass, "getPackageManager", "()Landroid/content/pm/PackageManager;"); jclass packageManagerClass = env->FindClass("android/content/pm/PackageManager"); jmethodID getPackageInfo = env->GetMethodID( packageManagerClass, "getPackageInfo", "(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;"); jclass packageInfoClass = env->FindClass("android/content/pm/PackageInfo"); jfieldID signaturesFid = env->GetFieldID( packageInfoClass, "signatures", "[Landroid/content/pm/Signature;"); jclass signatureClass = env->FindClass("android/content/pm/Signature"); jmethodID signatureToByteArrayMid = env->GetMethodID( signatureClass, "toByteArray", "()[B"); jclass messageDigestClass = env->FindClass("java/security/MessageDigest"); jmethodID messageDigestUpdateMid = env->GetMethodID( messageDigestClass, "update", "([B)V"); jmethodID getMessageDigestInstanceMid = env->GetStaticMethodID( messageDigestClass, "getInstance", "(Ljava/lang/String;)Ljava/security/MessageDigest;"); jmethodID digestMid = env->GetMethodID( messageDigestClass,"digest", "()[B"); jclass base64Class = env->FindClass("android/util/Base64"); jmethodID encodeToStringMid = env->GetStaticMethodID( base64Class,"encodeToString", "([BI)Ljava/lang/String;"); packageName = (jstring)env->CallObjectMethod( context, getPackageNameMid); packageManagerObj = env->CallObjectMethod(context, getPackageManager); // PackageManager.GET_SIGNATURES = 0x40 packageInfoObj = env->CallObjectMethod( packageManagerObj,getPackageInfo, packageName, 0x40); jobjectArray signatures = (jobjectArray)env->GetObjectField( packageInfoObj, signaturesFid); //int signatureLength = env->GetArrayLength(signatures); jobject signatureObj = env->GetObjectArrayElement(signatures, 0); jobject messageDigestObj = env->CallStaticObjectMethod(messageDigestClass, getMessageDigestInstanceMid, env->NewStringUTF("SHA1")); env->CallVoidMethod(messageDigestObj, messageDigestUpdateMid, env->CallObjectMethod( signatureObj,signatureToByteArrayMid)); // Base64.DEFAULT = 0 그렇기 때문에 맨 마지막 인자값은 0이다. jstring signatureHash = (jstring)env->CallStaticObjectMethod( base64Class, encodeToStringMid,env->CallObjectMethod( messageDigestObj, digestMid, signatureObj), 0); return (char*)env->GetStringUTFChars(signatureHash,0); }