3.2.0.0에 추가된 Dex 부분 암호화 기능에 대한 설명입니다.
DEX 부분 암호화 기능을 안전하고 효과적으로 사용하려면 아래 네 가지 기준으로 암호화 대상 목록을 구성하십시오. 대상 선정이 적절하지 않으면 앱이 정상적으로 실행되지 않거나(크래시), 런타임 크래시를 피하기 위해 암호화되지 않을 수 있습니다.
개발자 콘솔에서 부분암호화를 설정하는 방법은 아래 문서를 확인해 주시기 바랍니다.
1. 앱 진입점에서 직접 참조되는 클래스는 피하십시오
Android 런타임은 클래스를 실행하기 전에 그 클래스가 참조하는 다른 클래스까지 함께 검증합니다. 암호화된 클래스는 보안 모듈의 복호화가 끝난 뒤에야 사용할 수 있으므로, Application이나 Activity 등 진입점에서 직접 생성·호출되는 클래스와 그 클래스가 참조하는 클래스는 실행 안정성을 위해 자동으로 암호화 대상에서 제외됩니다.
class MainActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// ServerManager는 Activity에서 직접 참조되므로
// 부팅 안정성을 위해 평문으로 유지됩니다.
ServerManager.init(this)
}
}
반대로 아래처럼 화면 시작 코드 어디에서도 직접 참조되지 않고, 사용자가 특정 기능을 실제로 사용할 때에만 동작하는 비즈니스 로직이 암호화에 적합합니다.
// Application/Activity 시작 코드에서 참조되지 않고
// 사용자가 실제로 해당 기능을 사용할 때만 실행됩니다.
class LicenseCalculator {
fun calculate(input: Input): Result { ... }
}핵심 알고리즘, 과금 로직, 콘텐츠 처리 로직처럼 보호 가치는 높지만 앱 시작과는 무관한 코드가 가장 좋은 암호화 대상입니다.
2. R8/ProGuard 등 난독화 도구를 사용하는 경우, 빌드마다 목록을 갱신하십시오
난독화가 적용된 APK에는 원본 소스 코드의 클래스 이름이 남아 있지 않습니다. 예를 들어 빌드 시 생성되는 mapping.txt에 아래와 같이 기록되었다면,
# mapping.txt (빌드 시 R8/ProGuard가 생성) com.example.app.DataModule -> a.b:
목록에 원본 이름(DataModule)을 그대로 지정하면 실제 APK에는 a.b라는 이름만 존재하므로 매칭되지 않아 조용히 무시됩니다. 난독화된 이름은 빌드마다 달라질 수 있으므로, 목록은 매 빌드의 mapping.txt 기준으로 갱신하거나 해당 클래스를 R8/ProGuard keep 규칙으로 이름을 고정한 뒤 지정하십시오.
3. 리플렉션·JNI로 접근되는 클래스와 서드파티 라이브러리는 제외하십시오
아래처럼 클래스가 코드가 아닌 문자열로만 참조되는 경우, 정적 분석은 이 의존 관계를 확인할 수 없습니다.
// 의존 관계가 문자열로만 존재하여
// 정적 분석으로는 탐지할 수 없습니다.
val tracker = Class.forName("com.example.analytics.Tracker")// JNI도 마찬가지로, // 네이티브 코드가 런타임에 이름으로 클래스를 조회합니다. jclass cls = (*env)->FindClass(env, "com/example/analytics/Tracker");
이런 클래스가 암호화 대상에 포함되면 해당 코드가 실행되는 순간 크래시가 발생할 수 있습니다. 분석 SDK·크래시 리포팅 SDK 등 서드파티 라이브러리는 내부적으로 리플렉션과 JNI를 광범위하게 사용하므로, 암호화 대상은 직접 작성한 비즈니스 코드로 한정하십시오.
4. 목록 변경 후에는 반드시 실제 기기에서 테스트하십시오
정적 분석 기반의 자동 판단은 문자열 기반 접근까지 모두 예측할 수는 없습니다. 암호화 대상 목록을 변경한 뒤에는 실제 기기에서 부팅과 주요 기능 시나리오(변경한 대상과 관련된 화면·기능 포함)를 테스트하여 정상 동작 여부를 확인하십시오.