APK 서명 체계 v3

Android 9는 APK 키 회전을 지원합니다. 앱은 키 회전을 통해 서명 키를 APK 업데이트의 일부로 변경할 수 있습니다. 실용적인 회전을 위해 APK는 신규 및 기존 서명 키 간의 신뢰도를 나타내야 합니다. 키 회전을 지원하고 신규 및 기존 키를 사용할 수 있도록 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(unit32)
        • value(가변 길이: 추가 속성 길이 - 4바이트)
        • ID - 0x3ba06f8c
        • value - Proof-of-rotation 구조
    • minSDK(uint32) - 서명된 데이터 섹션의 minSDK 값의 중복 - 현재 플랫폼이 범위에서 벗어난 경우 이 서명의 유효성 검사를 건너뜁니다. 서명된 데이터 값과 일치해야 합니다.
    • maxSDK(uint32) - 서명된 데이터 섹션의 maxSDK 값의 중복 - 현재 플랫폼이 범위에서 벗어난 경우 이 서명의 유효성 검사를 건너뜁니다. 서명된 데이터 값과 일치해야 합니다.
    • 길이가 접두사로 지정된 signatures의 길이가 접두사로 지정된 시퀀스:
      • signature algorithm ID(unit32)
      • signed data에 대한 길이가 접두사로 지정된 signature
    • 길이가 접두사로 지정된 public key(SubjectPublicKeyInfo, ASN.1 DER 형식)

Proof-of-rotation alc 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 구조에 새로운 인증서를 하나씩 서명하는 대신 새로운 인증서 집합을 서명하는 기존의 서명 인증서가 있어야 하기 때문입니다. 예를 들어 키 A에 의해 서명되고 2개의 새로운 키인 B 및 C에 의해 서명되고 싶은 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 struct 구조에 위치해야 하는지, 어떤 작업에 사용되는지를 나타내는 플래그입니다.
    • 길이가 접두사로 지정된 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 APK 서명 체계 v3 블록 내 각 signer에 대해 다음을 수행합니다.
    1. signatures에서 가장 강하게 지원되는 signature algorithm ID를 선택합니다. 강도 순서 지정은 각 구현/플랫폼 버전에 따라 결정됩니다.
    2. signed data에서 signatures에 해당하는 signature를 확인하고, 이때 public key를 사용합니다. 이제 signed data를 안전하게 파싱할 수 있습니다.
    3. 서명된 데이터의 최소 및 최대 SK 버전이 signer에 대해 지정된 버전과 일치하는지 확인합니다.
    4. digestssignatures의 정렬된 서명 알고리즘 ID 목록이 동일한지 확인합니다. 이는 서명 제거/추가를 방지하기 위함입니다.
    5. 동일한 다이제스트 알고리즘을 서명 알고리즘에 의해 사용되는 다이제스트 알고리즘으로 사용하여 APK 콘텐츠 다이제스트를 컴퓨팅합니다.
    6. 컴퓨팅된 다이제스트가 digests의 해당하는 digest와 동일한지 확인합니다.
    7. certificates의 첫 번째 certificate의 SubjectPublicKeyInfo가 public key와 동일한지 확인합니다.
    8. signer에 대해 proof-of-rotation 속성이 존재하는 경우 구조가 유효한지, 그리고 이 signer가 목록의 마지막 인증서인지 확인합니다.
  4. 현재 플랫폼의 범위 내에서 정확히 1개의 signer가 발견되었고 이 signer에 대해 3단계가 성공하면 인증이 정상적으로 이루어집니다.

유효성 검사

기기가 v3을 제대로 지원하는지 테스트하려면 cts/hostsidetests/appsecurity/src/android/appsecurity/cts/에서 PkgInstallSignatureVerificationTest.java CTS 테스트를 실행하세요.