태그 보관물: 키 관리

안드로이드에서 비교적 안전하게 키 사용하기

많은 앱이 상품을 팔거나 각종 서비스에 연동하는 경우에 필요한 아이디나 키를 저장해서 사용한다. 이 키들을 안전하게 사용하려면 추가적인 처리가 필요하다. 짧은 지식으로 안전함의 순위를 나열하면 아래와 같다.

1. 안전한 방법 : 암호화해서 저장
2. 비교적 안전한 방법 : 네이티브 영역에 저장
3. 안전하지 않은 방법 : 리소스 파일에 저장

우선, 위 기준에 따른 사용방법을 살펴보자.

1. 안전한 방법
1.1 대칭키를 사용해서 키를 암호화한다.
1.2 안드로이드 리소스 파일에 암호화된 키를 저장한다.
1.3 암호화된 키를 사용하기 전에 서버에서 대칭키를 받아서 복호화한다.

2. 비교적 안전한 방법
2.1 네이티브 코드에 키를 리턴하는 메서드를 구현한다.
2.2 네이티브 메서드를 호출해서 키를 받는다.

3. 안전하지 않은 방법
3.1 키를 리소스 파일에 저장한다.
3.2 저장된 키를 사용한다.

이 글에서는 현실적으로 서버를 사용할 수 없는 경우에, 비교적 안전한 방법을 채택하는 형태를 살펴본다.

기존 프로젝트의 main 폴더에 jni 폴더를 만들고 아래와 같은 파일을 만든다.

위 파일의 내용은 아래와 같다.

Android.mk

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := keys
LOCAL_SRC_FILES := keys.c

include $(BUILD_SHARED_LIBRARY)

Application.mk : 아래 코드로 모든 플랫폼에 해당하는 .so 를 만든다.

APP_ABI := all

Keys.c : 자바에서 호출할 메서드와 키를 정의한다.

#include 

JNIEXPORT jstring JNICALL
Java_net_sjava_keytest_MainActivity_getNativeKey1(JNIEnv *env, jobject instance) {
    return (*env)->  NewStringUTF(env, "First Key");
}

JNIEXPORT jstring JNICALL
Java_net_sjava_keytest_MainActivity_getNativeKey2(JNIEnv *env, jobject instance) {
    return (*env)->NewStringUTF(env, "Second Key");
}

이제 build.gradle에서 ndk를 포함시키는 코드를 추가한다.

android {
    compileSdkVersion 26
    buildToolsVersion "26.0.0"
    defaultConfig {
        applicationId "net.sjava.keytest"
        minSdkVersion 19
        targetSdkVersion 26
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }

    externalNativeBuild {
        ndkBuild {
            path 'src/main/jni/Android.mk'
        }
    }
}

그리고, MainActivity.java 에서 아래와 같이 키를 가져오는지 확인한다.

package net.sjava.keytest;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    static {
        System.loadLibrary("keys");
    }

    public native String getNativeKey1();
    public native String getNativeKey2();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        String key1 = getNativeKey1();
        String key2 = getNativeKey2();

        ((TextView)findViewById(R.id.textview)).setText("Key1-->"+key1+"\nKey2-->"+key2);
    }
}

이제, 위 코드를 실행하면 아래의 실행 결과를 볼 수 있다.

현실적으로 앱 크래킹을 완벽하게 막기는 불가능한 것 같다. 그래서 프로가드(Proguard)로 난독화 처리와, 앱에서 사용하는 키를 비교적 안전하게 관리해서 크래킹을 어렵게 만드는 게 현실적인 방법인 것 같다.

* 레퍼런스 : https://medium.com/@abhi007tyagi/storing-api-keys-using-android-ndk-6abb0adcadad