APK 서명 체계 v3

Android 9에서는 APK 키 순환을 지원하므로 앱에서 서명 키를 APK 업데이트의 일부로 변경할 수 있습니다. 실용적인 순환을 위해 APK는 신규 및 기존 서명 키 간의 신뢰도를 나타내야 합니다. 키 순환을 지원하기 위해 Google에서는 새 키 및 이전 키를 사용할 수 있도록 APK 서명 체계를 v2에서 v3으로 업데이트했습니다. v3은 지원되는 SDK 버전과 proof-of-rotation 구조에 관한 정보를 APK 서명 블록에 추가합니다.

APK 서명 블록

v1 APK 형식과 호환성을 유지하기 위해 v2 및 v3 APK 서명은 ZIP 중앙 디렉터리 바로 앞에 위치한 APK 서명 블록 내에 저장됩니다.

v3 APK 서명 블록 형식은 v2와 동일합니다. APK의 v3 서명은 ID가 0xf05368c0인 ID 값 쌍으로 저장됩니다.

APK 서명 체계 v3 블록

v3 체계는 v2 체계와 매우 유사하게 설계되었으며 동일한 일반 형식을 취하고 동일한 서명 알고리즘 ID, 키 크기 및 EC 곡선을 지원합니다.

하지만 v3 체계는 지원되는 SDK 버전과 proof-of-rotation 구조에 관한 정보를 추가합니다.

형식

APK 서명 체계 v3 블록은 ID가 0xf05368c0인 APK 서명 블록 내에 저장됩니다.

APK 서명 체계 v3 블록의 형식은 v2의 형식을 따릅니다.

  • 길이가 접두사로 지정된 signer의 길이가 접두사로 지정된 시퀀스:
    • 길이가 접두사로 지정된 signed data:
      • 길이가 접두사로 지정된 digests의 길이가 접두사로 지정된 시퀀스:
        • signature algorithm ID(4바이트)
        • digest(길이가 접두사로 지정됨)
      • X.509 certificates의 길이가 접두사로 지정된 시퀀스:
        • 길이가 접두사로 지정된 X.509 certificate(ASN.1 DER 형식)
      • minSDK(uint32) - 플랫폼 버전이 이 숫자보다 낮으면 이 서명자를 무시해야 합니다.
      • maxSDK(uint32) - 플랫폼 버전이 이 숫자보다 높으면 이 서명자를 무시해야 합니다.
      • 길이가 접두사로 지정된 additional attributes의 길이가 접두사로 지정된 시퀀스:
        • ID(uint32)
        • value(가변 길이: 추가 속성 길이 - 4바이트)
        • ID - 0x3ba06f8c
        • value - - Proof-of-rotation 구조
    • minSDK(uint32) - 서명된 데이터 섹션의 minSDK 값의 중복 - 현재 플랫폼이 범위 내에 있지 않은 경우 이 서명 인증을 건너뜁니다. 서명된 데이터 값과 일치해야 합니다.
    • maxSDK(uint32) - 서명된 데이터 섹션의 maxSDK 값의 중복 - 현재 플랫폼이 범위 내에 있지 않은 경우 이 서명 인증을 건너뜁니다. 서명된 데이터 값과 일치해야 합니다.
    • 길이가 접두사로 지정된 signatures의 길이가 접두사로 지정된 시퀀스:
      • signature algorithm ID(uint32)
      • signed data를 통한 길이가 접두사로 지정된 signature
    • 길이가 접두사로 지정된 public key(SubjectPublicKeyInfo, ASN.1 DER 형식)

Proof-of-rotation 및 self-trusted-old-certs 구조

proof-of rotation 구조는 앱이 통신하는 다른 앱에서 차단당하지 않고도 서명 인증서를 회전할 수 있게 해줍니다. 이를 위해서는 앱 서명에 두 가지 데이터가 포함되어야 합니다.

  • 이전 서명 인증서를 신뢰할 수 있을 때마다 앱의 서명 인증서를 신뢰할 수 있다는 타사용 어설션
  • 앱 자체에서 여전히 신뢰하는 앱의 이전 서명 인증서

서명된 데이터 섹션의 proof-of-rotation 속성은 단일 연결 목록으로 구성되며 각 노드에는 이전 버전의 앱에 서명하는 데 사용한 서명 인증서가 포함되어 있습니다. 이 속성은 개념적 proof-of-rotation 및 self-trusted-old-certs 데이터 구조를 포함하도록 되어 있습니다. 목록은 버전을 기준으로 순서가 지정되며 가장 오래된 서명 인증서가 루트 노드에 상응합니다. proof-of-rotation 데이터 구조는 각 노드의 인증서가 목록의 다음 인증서를 서명하도록 하는 방식으로 빌드됩니다. 따라서 새로운 각 키에는 키가 기존 키로 신뢰되어야 한다는 증거가 삽입됩니다.

self-trusted-old-certs 데이터 구조는 집합에서 멤버십 및 속성을 나타내는 각 노드에 플래그를 추가하는 방식으로 구성됩니다. 예를 들면 주어진 노드에 위치한 서명 인증서를 Android 서명 권한 획득과 관련하여 신뢰할 수 있음을 나타내는 플래그가 존재할 수 있습니다. 이 플래그를 사용하면 이전 인증서로 서명된 다른 앱에 새 서명 인증서로 서명된 앱에서 정의한 서명 권한을 계속 부여할 수 있습니다. 전체 proof-of-rotation 속성은 v3 signer 필드의 서명된 데이터 섹션에 있으므로 포함된 APK에 서명하는 데 사용된 키로 보호됩니다.

이 형식에서는 여러 서명 키를 사용할 수 없으며 다른 상위 서명 인증서를 하나로 모을 수 없습니다(여러 시작 노드를 공통 싱크로).

형식

proof-of-rotation은 ID가 0x3ba06f8c인 APK 서명 체계 v3 블록 내에 저장됩니다. 형식은 다음과 같습니다.

  • 길이가 접두사로 지정된 levels의 길이가 접두사로 지정된 시퀀스:
    • 있는 경우 이전 증명서로 길이가 접두사로 지정된 signed data
      • 길이가 접두사로 지정된 X.509 certificate(ASN.1 DER 형식)
      • signature algorithm ID(uint32) - 이전 수준의 인증서에서 사용한 알고리즘
    • flags(uint32) - 이 인증서가 self-trusted-old-certs 구조에 있어야 하는지, 어떤 작업에 사용되는지를 나타내는 플래그
    • signature algorithm ID(uint32) - 다음 수준의 서명된 데이터 섹션의 항목과 일치해야 함
    • signed data를 통해 길이가 접두사로 지정된 signature

여러 인증서

Android는 현재 여러 인증서로 서명된 APK에 구성 인증서와 별도의 고유 서명 ID가 포함되어 있다고 간주합니다. 따라서 서명된 데이터 섹션의 proof-of-rotation 속성은 방향성 비순환 그래프를 형성합니다. 이 그래프는 단일 연결 목록으로 보는 것이 훨씬 수월하며, 주어진 버전의 각 서명자 집합이 하나의 노드를 나타냅니다. 이는 proof-of-rotation 구조(아래의 다중 서명자 버전)를 더욱 복잡하게 만듭니다. 특히 순서가 우려사항이 됩니다. 뿐만 아니라 이제는 더 이상 APK를 독립적으로 서명할 수 없습니다. 새로운 인증서에 하나씩 서명하는 대신 새로운 인증서 집합에 서명하는 이전 서명 인증서가 proof-of-rotation 구조에 있어야 하기 때문입니다. 예를 들어 새로운 두 키인 B와 C로 서명되려는 키 A로 서명된 APK에서는 B 서명자가 A 또는 B에 의한 서명을 포함하도록 할 수 없습니다. B 및 C와는 다른 서명 ID이기 때문입니다. 즉, 서명자가 이러한 구조를 빌드하기 전에 조율해야 합니다.

다중 서명자 proof-of-rotation 속성

  • 길이가 접두사로 지정된 sets의 길이가 접두사로 지정된 시퀀스:
    • signed data(있는 경우 이전 집합 기준)
      • certificates의 길이가 접두사로 지정된 시퀀스
        • 길이가 접두사로 지정된 X.509 certificate(ASN.1 DER 형식)
      • signature algorithm IDs (uint32)의 시퀀스 - 이전 집합의 인증서별로 하나씩(동일 순서)
    • flags (uint32) - 이 인증서 집합이 self-trusted-old-certs 구조에 있어야 하는지, 어떤 작업에 사용되는지를 나타내는 플래그
    • 길이가 접두사로 지정된 signatures의 길이가 접두사로 지정된 시퀀스:
      • signature algorithm ID(uint32) - 서명된 데이터 섹션의 항목과 일치해야 함
      • signed data를 통해 길이가 접두사로 지정된 signature

proof-of-rotation 구조의 여러 상위 요소

v3 체계는 동일한 앱의 동일한 서명 키로 순환하는 두 개의 다른 키도 처리하지 않습니다. 이는 인수 회사에서 자체 서명 키를 사용하여 권한을 공유하도록 인수한 앱을 이동하려는 인수 사례와는 다릅니다. 인수가 지원되는 사용 사례로 간주되는 이유는 새 앱이 패키지 이름으로 구분되고 자체 proof-of-rotation 구조를 포함할 수 있기 때문입니다. 동일한 인증서에 도달하기 위해 두 개의 다른 경로를 보유하는 동일한 앱의 지원되지 않는 사례는 키 순환 설계에서 이루어진 다수의 가정을 파괴합니다.

인증

Android 9 이상에서는 APK 서명 체계 v3, v2 체계 또는 v1 체계에 따라 APK를 확인할 수 있습니다. 이전 플랫폼은 v3 서명을 무시하고 v2 서명에 이어 v1을 확인하려고 시도합니다.

APK 서명 인증 프로세스

그림 1. APK 서명 인증 프로세스

APK 서명 체계 v3 인증

  1. APK 서명 블록을 찾아 다음을 확인합니다.
    1. APK 서명 블록의 두 크기 필드 값이 같아야 합니다.
    2. ZIP 중앙 디렉터리 바로 뒤에 ZIP 중앙 디렉터리 끝의 기록이 위치해야 합니다.
    3. ZIP 중앙 디렉터리의 끝에 붙는 추가 데이터가 없습니다.
  2. APK 서명 블록 내에서 첫 번째 APK 서명 체계 v3 블록을 찾습니다. v3 블록이 있으면 3단계로 넘어갑니다. 아니면 v2 체계를 사용하여 APK 인증으로 돌아갑니다.
  3. 현재 플랫폼의 범위에 있는 최소 및 최대 SDK 버전이 포함된 APK 서명 체계 v3 블록 내 각 signer의 경우 다음을 실행합니다.
    1. 지원되는 가장 강력한 signature algorithm IDsignatures에서 선택합니다. 강도 순서 지정은 각 구현/플랫폼 버전에 따라 결정됩니다.
    2. public key를 사용하여 signatures에서 signed data에 상응하는 signature를 확인합니다. 이제 signed data를 안전하게 파싱할 수 있습니다.
    3. 서명된 데이터의 최소 및 최대 SDK 버전이 signer에 지정된 버전과 일치하는지 확인합니다.
    4. digestssignatures의 순서가 지정된 서명 알고리즘 ID 목록이 동일한지 확인합니다. 이는 서명 제거/추가를 방지하기 위함입니다.
    5. 서명 알고리즘에서 사용하는 다이제스트 알고리즘과 동일한 다이제스트 알고리즘을 사용하여 APK 콘텐츠 다이제스트를 계산합니다.
    6. 계산된 다이제스트가 digests의 상응하는 digest와 동일한지 확인합니다.
    7. certificates의 첫 번째 certificate의 SubjectPublicKeyInfo가 public key와 동일한지 확인합니다.
    8. signer에 proof-of-rotation 속성이 있다면 구조가 유효한지, 이 signer가 목록에서 마지막 인증서인지 확인합니다.
  4. 현재 플랫폼의 범위 내에서 정확히 signer 하나가 발견되었고 이 signer에 3단계가 성공했다면 인증이 정상적으로 이루어집니다.

유효성 검사

기기가 v3를 올바르게 지원하는지 테스트하려면 cts/hostsidetests/appsecurity/src/android/appsecurity/cts/에서 PkgInstallSignatureVerificationTest.java CTS 테스트를 실행합니다.