안드로이드 애플리케이션 패키지(apk)를 설치하기 위해서는 반드시 서명(signing)을 해야합니다. 
서명되지 않은 애플리케이션을 설치하려고 하면 아래처럼 [INSTALL_PARSE_FAILED_NO_CERTIFICATES] 에러가 발생하게 됩니다.

$>adb install ActivityLifecycleProject.apk
178 KB/s (8585 bytes in 0.046s)
        pkg: /data/local/tmp/ActivityLifecycleProject.apk
Failure [INSTALL_PARSE_FAILED_NO_CERTIFICATES]

애플리케이션 서명 여부 확인하기
그럼 어떤 애플리케이션이 서명되었는지 여부는 어떻게 알 수 있을까요? 우선은 JDK에 포함된 jarsigner 명령을 통해서 확인할 수 있습니다.

## 서명안된 apk
$>jarsigner -verify ActivityLifecycleProject.apk
no manifest.
jar is unsigned. (signatures missing or not parsable)


## 서명된 apk
$>jarsigner -verify ActivityLifecycleProject.apk
jar verified.

(좀더 자세한 서명 정보는 -verfiy -verbose -certs 옵션을 추가하면 볼 수 있습니다.)

또는 간단히 apk 파일을 알집 등으로 풀어서 META-INF 폴더 안에 CERT.SF 와 같은 파일이 있으면 서명된 패키지로 볼 수 있습니다. 서명되지 않은 apk에는 이러한 파일이 없습니다.



이클립스를 통한 자동 디버그 모드 서명
앞서 애플리케이션은 반드시 서명되어야 한다고 했습니다. 그런데 우리는 이클립스(+ADT) 환경에서 안드로이드 애플리케이션을 개발하면서, 서명 작업을 의식하지 않고 자연스럽게 타겟에 애플리케이션을 설치하고 테스트합니다. 이렇게 개발자가 직접 서명을 하지 않아도 되었던 이유는 이클립스가 애플리케이션을 빌드하면서 자동으로 apk에 서명까지 함께 해주었기 때문입니다.

apk 서명에는 debug 모드와 release 모드가 있는데, 이클립스는 debug 모드로 서명을 해줍니다. 이클립스가 apk를 디버그 모드로 서명하기 위한 키/인증서(key/certificate)는 "$HOME/.android/debug.keystore" 파일에 저장되어 있습니다. 'debug.keystore' 파일은 이클립스가 필요시 자동으로 생성해줍니다. 이클립스 Preferences 창의 Android > Build 메뉴를 보면, 이 파일이 기본 디버그 키저장소(Default debug keystore) 파일로 설정되어 있는 것을 확인할 수 있습니다.



네이티브 애플리케이션은 좀 다르다
그럼 네이티브 애플리케이션도 이클립스가 자동생성한 debug.keystore 파일로 서명해서 설치가 될까요? 일단 debug.keystore 파일로 서명된 네이티브 Calculator 애플리케이션을 설치해보죠. 

D:\Android\Calculator\bin>adb install Calculator.apk
789 KB/s (88433 bytes in 0.109s)
        pkg: /data/local/tmp/Calculator.apk
Failure [INSTALL_FAILED_ALREADY_EXISTS]
 
이미 동일한 패키지가 존재한다며 에러가 떨어집니다. -r 옵션을 추가해서 재설치를 시도해봅니다.

D:\Android\Calculator\bin>adb install -r Calculator.apk
425 KB/s (88433 bytes in 0.203s)
        pkg: /data/local/tmp/Calculator.apk
Failure [INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES]

일치하지 않는 인증서 에러가 발생하면서 설치되지 않네요. 그러면 우리가 원래 하던대로 이클립스에서 [Run As > Android Application]을 실행해보겠습니다.

[2010-04-02 16:54:10 - Calculator]Android Launch!
[2010-04-02 16:54:10 - Calculator]adb is running normally.
[2010-04-02 16:54:10 - Calculator]Performing com.android.calculator2.Calculator activity launch
[2010-04-02 16:54:10 - Calculator]Automatic Target Mode: using existing emulator 'emulator-5554' running compatible AVD 'myavd'
[2010-04-02 16:54:10 - Calculator]WARNING: Application does not specify an API level requirement!
[2010-04-02 16:54:10 - Calculator]Device API version is 7 (Android 2.1)
[2010-04-02 16:54:10 - Calculator]Uploading Calculator.apk onto device 'emulator-5554'
[2010-04-02 16:54:10 - Calculator]Installing Calculator.apk...
[2010-04-02 16:54:13 - Calculator]Re-installation failed due to different application signatures.
[2010-04-02 16:54:13 - Calculator]You must perform a full uninstall of the application. WARNING: This will remove the application data!
[2010-04-02 16:54:13 - Calculator]Please execute 'adb uninstall com.android.calculator2' in a shell.
[2010-04-02 16:54:13 - Calculator]Launch canceled!

역시 애플리케이션 시그너쳐가 다르다며 재설치가 실패합니다. 이로부터 우리는 타겟에 내장되어 있는 네이티브 애플리케이션은 이클립스가 생성하는 debug.keystore를 통해 서명된 것이 아니라 다른 인증서로 서명되었음을 알 수 있습니다. 

네이티브 애플리케이션 서명용 테스트 키
MYDROID/build/target/product/security/ 에는 시스템 패키지(네이티브 애플리케이션)들을 '디버그 모드'로 서명하기 위한 4가지의 준비된 표준 테스트 키가 있습니다. 이것들은 개발 단계에서 사용되며, 에뮬레이터에 내장된 네이티브 애플리케이션은 안드로이드 소스 full 빌드 중에 이 중 한가지 키로 서명이 됩니다.

- media.pk8 , media.x509.pem
- platform.pk8 , platform.x509.pem
- shared.pk8 , shared.x509.pem
- testkey.pk8 , testkey.x509.pem

4가지 키에 대한 간략한 설명은 다음과 같습니다(README 파일 참조).
  • testkey -- a generic key for packages that do not otherwise specify a key.
  • platform -- a test key for packages that are part of the core platform.
  • shared -- a test key for things that are shared in the home/contacts process.
  • media -- a test key for packages that are part of the media/download system.

네이티브 애플리케이션이 어떤 테스트 키로 서명되는지는 해당 애플리케이션 소스 루트의 Android.mk를 보면 알 수 있습니다. 네이티브 Contacts 애플리케이션의 Android.mk 파일을 보면 LOCAL_CERTIFICATE := shared 로 설정돼있습니다. 즉 shared 테스트 키로 서명을 한다는 것이죠.

LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := user
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_STATIC_JAVA_LIBRARIES := googlelogin-client
LOCAL_PACKAGE_NAME := Contacts
LOCAL_CERTIFICATE := shared
include $(BUILD_PACKAGE)
# Use the folloing include to make our test apk.
include $(call all-makefiles-under,$(LOCAL_PATH))

Calculator의 Android.mk를 보면 LOCAL_CERTIFICATE 변수가 정의돼 있지 않은데, 그러면 기본적으로 testkey 키로 서명하게 됩니다.

네이티브 애플리케이션 자동 서명용 디버그 키저장소 만들기
에뮬레이터에 내장된 네이티브 애플리케이션이 어떤 키/인증서로 서명되었는지 알았으니, 커스텀 디버그 키저장소(Custom debug keystore) 파일을 만들고, 이것을 이클립스에 등록하여 이클립스가 자동으로 네이티브 애플리케이션에 서명하도록 할 수 있습니다.
키저장소를 만들기 전에 알아둘 사항이 있는데, 생성된 java keystore 파일을 이클립스에서 사용하기 위해서는 반드시 alias는 androiddebugkey, password는 android이어야 한다는 겁니다. 그리고 openssl 과 JDK(1.6 권장)가 설치돼 있어야 합니다.
자 그럼, testkey.pk8 및 testkey.x509.pem 파일로부터 testkey.jks 파일을 만들어봅시다. 

  • 키 파일 : testkey.pk8
  • 인증서 파일 : testkey.x509.pem
  • 키 알리아스 : androiddebugkey
  • 키 패스워드 : android
  • 키저장소 패스워드 : android
  • 생성할 키저장소 파일 : testkey.jks

1. PK8 유형의 키 파일을 PEM 유형 키로 변환

$ openssl pkcs8 -inform DER -nocrypt -in testkey.pk8 -out testkey.pem

2. 키와 인증서를 포함하는 PKCS#12 포맷의 저장소 생성

$ openssl pkcs12 -export -in testkey.x509.pem -inkey testkey.pem -out testkey.p12 -password pass:android
-name androiddebugkey

3. PKCS#12 포맷 저장소 파일을 자바 키저장소 포맷으로 변환
(1) JDK 1.5인 경우 jetty 라이브러리에 포함된 PKCS12Import 클래스를 이용

$ java -classpath jetty-core-6.1.14.jar org.mortbay.jetty.security.PKCS12Import testkey.p12 testkey.jks 

(2) JDK 1.6인 경우 JDK에서 제공하는 keytool을 이용

$ keytool -importkeystore -deststorepass android -destkeystore testkey.jks -srckeystore testkey.p12
-srcstoretype PKCS12 -srcstorepass android

* 참고로 위의 1 ~ 3까지의 작업을 단순화시킨 keytool-importkeypair라는 스크립트가 있습니다. 아래와 같이 사용합니다

$ keytool-importkeypair -k testkey.jks -p android -pk8 testkey.pk8 -cert testkey.x509.pem -alias androiddebugkey

드디어 네이티브 애플리케이션을 이클립스를 통해 자동으로 디버그 모드 서명하기 위한 키저장소 파일을 만들었습니다.

이클립스에 커스텀 디버그 키저장소 파일 설정하기
생성한 키저장소 파일 testkey.jks를 적당한 곳에 두고 이클립스 Preferences 창의 Android > Build 메뉴에서 커스텀 디버그 키저장소(Custom debug keystore) 필드에 설정해줍니다.



OK 버튼을 누르고 아까 설치 실패했던 Calculator 프로젝트에 대해서 [Run As > Android Application]을 다시 실행합니다.

[2010-03-29 13:30:31 - Calculator]Uploading Calculator.apk onto device 'emulator-5554'
[2010-03-29 13:30:32 - Calculator]Installing Calculator.apk...
[2010-03-29 13:30:36 - Calculator]Success!
[2010-03-29 13:30:36 - Calculator]Starting activity com.android.calculator2.Calculator on device
[2010-03-29 13:30:41 - Calculator]ActivityManager: Starting: Intent { cmp=com.android.calculator2/.Calculator }

성공이네요. 이제 우리는 (이클립스 환경에서) 네이티브 애플리케이션도 평범한 유저 애플리케이션처럼 서명 작업을 의식하지 않고, Run As 또는 Debug As > Android Application을 실행하면서 자연스럽게 개발/디버깅/테스트할 수 있게 되었습니다.

Posted by 에코지오
,