초코비니

[Tip] 어플리케이션 서명(signing) 및 배포, 그리고 debug용 keystore 만들기 본문

안드로이드

[Tip] 어플리케이션 서명(signing) 및 배포, 그리고 debug용 keystore 만들기

초코비니 2015. 9. 16. 19:32
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.
Publishing에 대한 참고 자료::

Android App을 만든 후 apk를 생성하여 배포할 때, apk에는 반드시 서명 작업(signing)이 되어 있어야한다. 그렇지 않으면 해당 apk는 절대로 단말기에 설치 할 수 없다. apk를 생성하면서 서명하는 방법에 대해 알아보자.


배포용 APK 만들기

Android Studio에서 프로젝트를 apk로 생성하려면 상단 메뉴의 'Build - Generate Signed APK ...'를 선택하면 된다. 그러면 아래와 같은 팝업이 뜰텐데, 대충 내용은 Gradle 기반의 프로젝트는 gradle build script에서 signing 관련 정보를 정의해라, 그리고 'gradle assembleRelease'를 실행하면 build/apk 디렉토리에 APK가 생성될 것이다라는 내용이다. 본 포스트에서는 배포용 APK를 만드는 여러 방법에 대해 설명 할 것이므로 일단 OK 버튼을 눌러 다음 화면으로 이동하자.


1. Android Studio의 기능을 이용해서 배포

일단 gradle build script 말고 Android Studio에서 지원하는 방법을 사용해보자. OK 버튼을 누르면 다음과 같이 프로젝트를 선택하는 팝업이 나오는데, APK로 만들 프로젝트를 선택한 후 Next를 누른다.


그러면 또 아래와 같이 keystore에 대한 정보를 입력하라고 나오는데, 만들어 둔 keystore가 있다면 해당 keystore를 사용하면 될 것이고, 없다면 Create new... 를 선택한다.

 keystore란? (출처 : 커니의 안드로이드 이야기)
 어플리케이션 서명을 위해 사용하는 수단으로, 자신의 '서명'이라 할 수 있다. 서명은 'key'로 하게 되며, keystore는 서명을 할 수 있는 key들을 담을 수 있는 파일을 뜻한다. IDE를 통한 개발시 기본으로 디버그용 키를 사용하여 어플리케이션 패키지를 서명하게 되며, Google play store 등 외부로 어플리케이션을 배포할 때는 자신의 키를 사용하여 배포해야 한다. (배포 후 키를 잃어버리면 업데이트가 불가능하다!)

 왜 서명이 필요한가?
 악의적인 목적으로 리패키징 된 apk를 구분하기 위해서다. 어플리케이션을 배포할 때 apk로 패키징하게 되는데 이 때, 누가 패키징 했는지 정보를 기록하는게 서명 작업이다.

 signing에 대한 참고 자료
 http://developer.android.com/tools/publishing/app-signing.html



아래와 같이 필요한 정보들을 입력해준다. (Certificate 항목은 필수가 아니다. 그리고 Validity는 유효기간을 의미하므로 넉넉하게 넣어주자. 너무 큰 숫자를 넣으면 keystore가 정상적으로 생성되어도 hashcode of signature는 invalid하더라)


필요한 정보들을 입력한 후 OK 버튼을 누르면 아래와 같이 폼들이 자동으로 채워지게 된다. Next를 눌러 이동하자.


마스터 패스워드를 지정하라고 한다. 귀찮으니 keystore와 동일한 암호로 지정한다.


생성될 APK 파일의 경로를 지정하고 Proguard를 적용 할 것인지 선택 후 Finish 버튼을 누르면 APK가 생성 될 것이다.

 Keystore를 만드는 또다른 방법, keytool을 이용해 keystore를 생성해보자.
 사실 이게 오리지널이다. JDK에 포함된 keytool을 이용하면 터미널에서 keystore를 생성할 수 있다. 터미널에서 아래와 같이 입력한다.

 keytool -genkey -v -keystore 파일명.keystore -alias 별칭 -keyalg RSA -keysize 2048 -validity 10000

 그러면 아래와 같이 Android Studio와 동일한 순서로 정보를 입력 하게 되며, 마지막엔 *.keystore를 생성해 준다.

 잠깐, *.keystore와 *.jks의 차이는?

 Android Studio에서 생성한 keystore는 *.jks 확장자를 가질 것이며,  터미널에서 keytool을 이용해 직접 만든 keystore는 *.keystore 확장자를 가지는데 사실 이는 확장자만 다르지 똑같은 keystore 파일이다.(터미널에서 생성할 때 *.jks 확장자로 지정해 주면 그냥 생성된다.) jks는 Java security provider에서 제공하는 keystore의 default type을 의미한다. (자세한 내용은 여길 참고하라)


* 해당 어플리케이션이 정상적으로 서명이 되었는지 확인하는 방법.

이건 그냥 참고로 알아두면 좋은데, 잘못 만들어진 keystore로 서명할 경우 signature의 hashcode가 음수로 나오는 경우가 있다.(유효기간을 50년이상으로 하니까 hashcode가 이상하게 나오더라...) 정상적이라면 아래와 같이 나와야 한다.

1
04-29 10:58:36.387  11231-11231/com.jhlee.myapplication.app D/myapp﹕ Signature hashcode : 826338355

signature에 대한 hashcode를 얻는 코드.

1
2
3
4
5
6
7
8
9
10
11
12
13
import android.content.pm.PackageManager;
import android.content.pm.Signature;
 
...
 
try {
  Signature[] sigs = this.getPackageManager().getPackageInfo(getPackageName(), PackageManager.GET_SIGNATURES).signatures;
  for (Signature sig : sigs) {
    Log.d("myapp", "Signature hashcode : " + sig.hashCode());
  }
} catch (Exception e) {
  Log.d("myapp", e.getMessage());
}


2. Gradle Build Script를 이용해서 배포.

Android Studio에서 APK를 생성할 때 처음 팝업 화면에 아래와 같이 안내문이 있었다.

For Gradle-based projects, the signing configuration should be specified in the Gradle build scripts. 
Configure your signing configurations as described in the user guide: http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Signing-Configurations Then, run "gradle assembleRelease", and you will find your APK in the build/apk/ directory.

gradle build script에 keystore을 지정해주고 'gradle assembleRelease'를 실행하면 APK가 생성된다는 말이다. 


APK를 생성할 프로젝트의 build.gradle을 아래와 같이 편집하자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
android {
  compileSdkVersion 19
  buildToolsVersion "19.0.3"
 
  defaultConfig {
    minSdkVersion 8
    targetSdkVersion 19
    versionCode 1
    versionName "1.0"
  }
 
  // 요기랑
  signingConfigs {
    release {
      storeFile file("signature/release.jks")
      storePassword "123456"
      keyAlias "myreleasekey"
      keyPassword "123456"
    }
  }
 
  buildTypes {
    release {
      signingConfig signingConfigs.release    // 여기
      debuggable false // debugging 허용 안함
      zipAlign true  // apk 최적화
      runProguard false
      proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
    }
  }
}

signingConfigs에 release라는 이름으로 config를 선언하고 buildTypes의 release에 해당 서명을 지정해줬다.(추가로 zipAlign 옵션까지 넣어줬다)


자, 이제 APK를 생성해보자. Android Studio에 보면 Gradle tasks라는 윈도우가 있는데 아래와 같이 assembleRelease라는 task가 있을 것이다. 이 태스크를 더블클릭해보자.


해당 task를 실행하면 build.gradle로부터 signing 정보를 확인하게 되므로 따로 윈도우를 통해 keystore를 지정해 주지 않아도 된다.

task가 종료되면 프로젝트\build\apk 디렉토리가 생성될 것이며 아래와 같이 두 개의 apk가 만들어질 것이다. 두 가지의 차이점은 unaligned-apk는 최적화되지 않은 APK이고 이 APK를 바탕으로 zipAlign 작업을 수행하여 app-release.apk가 생성되는 것이다. 그러므로 app-release.apk만 배포하면 된다.


3. Release한 App을 디버깅해보자.(debug용 keystore 만들기)

자 이제 App을 배포했고 수정할 사항이 생겨서 App이 설치된 단말기에서 디버깅을 하려 한다. 그러나 아마 아래와 같은 오류가 뜰 것이다.


콘솔 에러를 보면 아래와 같다.

Waiting for device. 
Target device: samsung-sm_g900k-92b54d52 
Uploading file 
  local path: /Users/jhlee/Documents/Workspace/AndroidStudio/MyApplication/app/build/apk/app-debug-unaligned.apk 
  remote path: /data/local/tmp/com.jhlee.myapplication.app 
Installing com.jhlee.myapplication.app 
DEVICE SHELL COMMAND: pm install -r "/data/local/tmp/com.jhlee.myapplication.app" 
pkg: /data/local/tmp/com.jhlee.myapplication.app 
Failure [INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES]

에러가 발생하는 이유는 간단하다. 디버깅을 위해 만들어진 app-debug-unaligned.apk의 signing 정보와 이미 단말기에 배포된 app-release.apk 정보가 다르기 때문이다. signing을 하는 이유는 위에서 설명했듯이 이런식으로 다른 인증을 통해 리패키징 된 어플리케이션을 구분하기 위해 사용하며, 이런 에러가 발생하는 것은 당연하다. 물론 해결책은 있다. 팝업 메세지처럼 이미 배포된(설치된) 앱을 지우고 디버깅을 시작하면 된다. 그러나 귀찮다. 그리고 우린 개발자다. 뭔가 다른 방법이 없을까.


방법은 간단하다. release용 keystore로부터 debug용 keystore를 만들어서 build.gradle에 지정 해주면 된다. debug용 keystore는 아래와 같이 keytool을 이용해 생성 할 수 있다.

(명령어 출처 : http://sunphiz.me/wp/archives/114)

1
keytool -importkeystore -v -srckeystore [릴리즈용 키스토어 파일이름] -destkeystore [디버그용 키스토어 파일이름] -srcstorepass [릴리즈용 키스토어 비밀번호] -deststorepass android -srcalias [alias 이름] -destalias androiddebugkey -srckeypass [alias 비밀번호] -destkeypass android

releaes keystore가 저장된 폴더로 이동하여 위 명령어를 실행한다. 그러면 debug용 keystore가 생성 될 것이다.

 debug용 keystore의 비밀번호와 alias는 고정이어야 하는가?
 위 명령어를 살펴보면 debug용 비밀번호는 android, alias는 androiddebugkey로 지정되어 있는데, 사실 이클립스나 Android Studio에서 디버그를 할 때 자동으로 서명되는 keystore의 비밀번호와 alias랑 동일한 것이다. 왜 동일하게 했냐면 build.gradle에서 debug용 keystore 지정시, 비밀번호와 alias를 지정해주지 않으면 IDE는 자동으로 위 비밀번호와 alias를 사용하기 때문이다. (편의상 동일하게 사용 하는 것으로 이해하면 된다.)

 IDE에서 자동으로 서명하는 keystore의 파일 위치
 Eclipse의 경우 Preferences -> Android -> build에서 default debug keystore의 경로를 확인 할 수 있다.


이제 build.gradle을 편집하자. 아래와 같이 debug일 경우에 사용 할 keystore 파일과 builTypes.debug시 사용 할 서명 방식을 지정해준다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
  signingConfigs {
    debug {
      storeFile file("signatrue/debug.jks")
//      storePassword "android"
//      keyAlias "androiddebugkey"
//      keyPassword "android"
    }
    release {
      storeFile file("signature/release.jks")
      storePassword "123456"
      keyAlias "myreleasekey"
      keyPassword "123456"
    }
  }
 
  buildTypes {
    debug {
      signingConfig signingConfigs.debug
    }
    release {
      signingConfig signingConfigs.release
      runProguard false
      proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
      zipAlign true
      debuggable false
    }
  }

다시 디버깅을 실행해보면 잘 될 것이다. 콘솔 메세지로 아래와 같이 나올 것이다.

Waiting for device. 
Target device: samsung-sm_g900k-92b54d52 
Uploading file
  local path: /Users/jhlee/Documents/Workspace/AndroidStudio/MyApplication/app/build/apk/app-debug-unaligned.apk
  remote path: /data/local/tmp/com.jhlee.myapplication.app
Installing com.jhlee.myapplication.app 
DEVICE SHELL COMMAND: pm install -r "/data/local/tmp/com.jhlee.myapplication.app" 
pkg: /data/local/tmp/com.jhlee.myapplication.app 
Success


출처 : http://warmz.tistory.com/945


Comments