Regular Motion

개발자가 상팔자

[Android] 누구나 비밀은 있다.

Android 개발을 하면서 늘 고민되던 부분 한가지는 ‘코드에서 감추고 싶은 정보들을 어떻게 감출 수 있을까?’ 하는 부분이다.

감추고 싶은 정보라하면 AWS나 GAE 등의 Cloud Service를 Private하게 사용하기 위해 필요한 Access Key나, Twitter, Facebook 연동을 위해 필요한 Consumer Secret, 혹은 서버와 데이터를 암호화하여 주고받을 때 복호화하기 위해 사용하는 Key등이 될 수 있겠다.

이 같은 정보들은 누군가 Decompile 해서 보려 해도 Plain Text로 노출되면 안되는 정보들이다.  보여주고 싶지 않은 속옷과 같다.

 

즉, 누구나 감추고 싶은 비밀은 있다.

어떻게 하면 저런 고급 정보들을 Decompile시에 Plain Text로 노출시키지 않을 수 있을까? 라는 고민을 해보니, Encrypted 된 상태로 코드에 삽입한 뒤, 필요할 때 복호화해서 사용할 수 있으면 될 것 같다는 생각이 들었다.  이를 위한 요구사항은 아래 3가지다.

 

1. Plain Text로 노출되면 안되는 정보(eg. Access Key, Consumer Secret…)들은 Encrypted 된 상태로 코드에 존재하며, 복호화 가능해야 한다.
2. 복호화를 위해 사용할 Key는 코드내에 Plain Text로 존재하면 안된다.
3. Application을 Sign하는데 사용하는 KeyStore로 부터 Key를 생성할 수 있다면 Best.

 

위의 요구사항을 만족시키기 위한 시나리오는 아래와 같다.

1. KeyStore로 부터 hash 값을 추출한다.
2. KeyStore의 hash 값으로 원하는 정보 (eg. Access Key, Consumer Secret…)들을 암호화 한다. 반드시 복호화 가능한 알고리즘을 사용한다.
3. (2)의 결과로 암호화된 값을 코드 혹은 resource에 삽입 한다.
4. Access Key, Consumer Secret이 필요할 때 복호화한 뒤 사용한다.

위의 시나리오에서 1,2,3번은 준비 과정에 해당하고, 4번이 실제 사용하는 과정에 해당한다.

 

1. KeyStore의 signature를 해싱하여 암호화에 사용할 Key를 생성하는 함수. (사용하려는 AES 알고리즘이 256bit key를 필요로하기 때문에 SHA-256 함수를 사용했다.)

 

2. Access Key, Consumer Secret등 노출하고 싶지 않은 정보들을 암호화 한다.

 

3. (2) 결과로 암호화 된 값을 코드에서 접근 가능하도록 저장한다.
(이때 debug.keystore로 암호화 한 값과 release시에 사용하는 keystore로 암호화한 값을 모두 저장한 뒤, Runtime에 현재 KeyStore로 암호화된 값을 사용해야 한다.)

 

4. 필요한 값을 복호화하여 사용한다. 요청이 빈번한 경우, 복호화 과정이 부담이 될 수 있기에 결과값을 캐싱하는 것도 좋은 대안이 될 수 있다.

 

* 보완사항

다수가 공동으로 작업하는 Project의 경우 서로 다른 debug.keystore를 사용하기 때문에 해당 내용이 고려 되야 될 것 같다.

6 Comments

  1. debug.keystore를 동일하게 가져가는 방법은 http://warmz.tistory.com/945 을 참고하셔도 좋을 것 같습니다.

  2. 잘봤습니다~! 제가 엄청 고민하고 있던 부분입니다~!
    저도 님 처럼 요청사항 1,2,3을 생각해냈는데…
    런타임중에 비밀키를 어떻게 만들어내야 할까 엄청 고민을 했습니다…

    현재는 어떤방법을 사용하고 계신가요?그대로 이렇게 추진중이신가요???
    더 좋은 방법이 나왔나요? 님의 글을 읽기전까지 전 소스내에 평문형태로 몇개의 비밀키를 두어서 해커들을 그냥 혼란스럽게(어차피 뚫릴거라면 하다가 지치게…ㅎ)
    할 생각이었었거든요..

    님의 글을 읽으니 좀 더 현명한 방법 같아서 이방법을 써보려고 합니다.
    글을 몇번 읽으며 궁금해진게 있는데요 답변 주실수 있나요??
    kestore라는것이 apk를 만들때 사용하는것이니 kestore(.jks)파일은 불변한거죠??
    apk가 업데이트 되더라도 계속 이 비밀키를 이용할수 있겠군용?

    • REGULARMOTION

      2017년 1월 2일 at 4:26 오후

      프로젝트별로 조금 다른데,

      개인 프로젝트의 경우 keystore의 hash 값으로 암호화한 정보를 resource 폴더에 넣어놓은 뒤, 런타임에 복호화해서 사용하고 있습니다.

      일부 프로젝트는 주요 정보를 *.so로 만든 뒤, JNI를 이용하여 접근하도록 설정한 프로젝트도 있습니다.

      고민을 해봐도 하나의 완벽한 답은 없는 것 같고, 경우에 따라 Token Vending Machine을 사용하거나 아니면 위의 옵션 중 하나를 사용하거나 하는게 좋을 것 같습니다;

  3. PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(), PackageManager.GET_SIGNATURES);

    GET_SIGNATURES부분이 KeyStore의 hash 값인가요?

    Keystore라는것을 잘 모르고 있었기에 요새 한참 읽고 있는데 말이죠…
    안드로이드에서 기본으로 제공???해주는
    C:Users.android에 위치한 debug.keystore이 파일의
    해시를 받아온다는건가요???

    해쉬 알고리즘을 쓰는 것도 모두 이해가 되었는데,
    키스토어와 관련된 부분들은 생각을 하면 할수록 어려워지는군요

    안드로이드 프로젝트마다 다른 키스토어를 가지고 있을것이라 예상했고
    그로인해 해시를 만들면 당연히 모두 다른 값들을 가져올것이라 예상했었는데
    그렇지 않았습니다.

    처음엔 테스트 프로젝트에서
    님의 소스를 이용하여 암호화도 잘되고 복호화도 잘되어
    본 프로젝트에도 님의 소스를 이용하였습니다.
    그러다 이상한걸 발견했습니다. 기존 프로젝트와 본 프로젝트에서
    모두 같은 해시값을 만들어내고 모두 같은 암호문을 만들어내는것입니다.

    해시 알고리즘 구현하는것도 모두 이해가 되었고
    암복호화도 모두 이해가 되었는데…
    그래도 문제라면 키스토어가 문제일거라고 생각이 되었습니다.

    이부분을 의논하고 싶은데 꼭 좀 답변주시면 감사하겠습니다.

    • REGULARMOTION

      2017년 1월 2일 at 4:31 오후

      답변이 좀 늦은 것 같아서 죄송하네요;

      KeyStore가 여러 종류가 있는데, 앱 배포시에 사용하는 KeyStore를 제외하고,

      개발할 때 사용하는 KeyStore는 프로젝트와 무관하게 동일한 debug.keystore를 사용하기때문에 동일한 해시값이 생성되는게 아닌가 추측됩니다.

답글 남기기

© 2017 Regular Motion

Theme by Anders NorenUp ↑