APK Signature Scheme v3

Android 9 supports APK key rotation, which gives apps the ability to change their signing key as part of an APK update. To make rotation practical, APKs must indicate levels of trust between the new and old signing key. To support key rotation, we updated the APK signature scheme from v2 to v3 to allow the new and old keys to be used. V3 adds information about the supported SDK versions and a proof-of-rotation struct to the APK signing block.

APK Signing Block

To maintain backward-compatibility with the v1 APK format, v2 and v3 APK signatures are stored inside an APK Signing Block, located immediately before the ZIP Central Directory.

The v3 APK Signing Block format is the same as v2. The v3 signature of the APK is stored as an ID-value pair with ID 0xf05368c0.

APK Signature Scheme v3 Block

The v3 scheme is designed to be very similar to the v2 scheme. It has the same general format and supports the same signature algorithm IDs, key sizes, and EC curves.

However, the v3 scheme adds information about the supported SDK versions and the proof-of-rotation struct.

Format

APK Signature Scheme v3 Block is stored inside the APK Signing Block under ID 0xf05368c0.

The format of the APK Signature Scheme v3 Block follows that of v2:

  • length-prefixed sequence of length-prefixed signer:
    • length-prefixed signed data:
      • length-prefixed sequence of length-prefixed digests:
        • signature algorithm ID (4 bytes)
        • digest (length-prefixed)
      • length-prefixed sequence of X.509 certificates:
        • length-prefixed X.509 certificate (ASN.1 DER form)
      • minSDK (uint32) - this signer should be ignored if platform version is below this number.
      • maxSDK (uint32) - this signer should be ignored if platform version is above this number.
      • length-prefixed sequence of length-prefixed additional attributes:
        • ID (uint32)
        • value (variable-length: length of the additional attribute - 4 bytes)
        • ID - 0x3ba06f8c
        • value - Proof-of-rotation struct
    • minSDK (uint32) - duplicate of minSDK value in signed data section - used to skip verification of this signature if the current platform is not in range. Must match signed data value.
    • maxSDK (uint32) - duplicate of the maxSDK value in the signed data section - used to skip verification of this signature if the current platform is not in range. Must match signed data value.
    • length-prefixed sequence of length-prefixed signatures:
      • signature algorithm ID (uint32)
      • length-prefixed signature over signed data
    • length-prefixed public key (SubjectPublicKeyInfo, ASN.1 DER form)

Proof-of-rotation and self-trusted-old-certs structs

The proof-of rotation struct allows apps to rotate their signing cert without being blocked on other apps with which they communicate. To accomplish this, app signatures contain two new pieces of data:

  • assertion for third parties that the app's signing cert can be trusted wherever its predecessors are trusted
  • app's older signing certs which the app itself still trusts

The proof-of-rotation attribute in the signed-data section consists of a singly-linked list, with each node containing a signing certificate used to sign previous versions of the app. This attribute is meant to contain the conceptual proof-of-rotation and self-trusted-old-certs data structures. The list is ordered by version with the oldest signing cert corresponding to the root node. The proof-of-rotation data structure is built by having the cert in each node sign the next in the list, and thus imbuing each new key with evidence that it should be as trusted as the older key(s).

The self-trusted-old-certs data structure is constructed by adding flags to each node indicating its membership and properties in the set. For example, a flag may be present indicating that the signing certificate at a given node is trusted for obtaining Android signature permissions. This flag allows other apps signed by the older certificate to still be granted a signature permission defined by an app signed with the new signing certificate. Because the whole proof-of-rotation attribute resides in the signed data section of the v3 signer field, it is protected by the key used to sign the containing apk.

This format precludes multiple signing keys and convergence of different ancestor signing certificates to one (multiple starting nodes to a common sink).

Format

The proof-of-rotation is stored inside the APK Signature Scheme v3 Block under ID 0x3ba06f8c. Its format is:

  • length-prefixed sequence of length-prefixed levels:
    • length-prefixed signed data (by previous cert - if exists)
      • length-prefixed X.509 certificate (ASN.1 DER form)
      • signature algorithm ID (uint32) - algorithm used by cert in previous level
    • flags (uint32) - flags indicating whether or not this cert should be in the self-trusted-old-certs struct, and for which operations.
    • signature algorithm ID (uint32) - must match the one from the signed data section in the next level.
    • length-prefixed signature over the above signed data

Multiple certificates

Android currently treats an APK signed with multiple certificates as having a unique signing identity separate from the comprising certs. Thus, the proof-of-rotation attribute in the signed-data section forms a directed acyclic graph, that could better be viewed as a singly-linked list, with each set of signers for a given version representing one node. This adds extra complexity to the proof-of-rotation struct (multi-signer version below). In particular, ordering becomes a concern. What's more, it is no longer possible to sign APKs independently, because the proof-of-rotation structure must have the old signing certs signing the new set of certs, rather than signing them one-by-one. For example, an APK signed by key A that wishes to be signed by two new keys B and C could not have the B signer just include a signature by A or B, because that is a different signing identity than B and C. This would mean that the signers must coordinate before building up such a struct.

Multiple signers proof-of-rotation attribute

  • length-prefixed sequence of length-prefixed sets:
    • signed data (by previous set - if exists)
      • length-prefixed sequence of certificates
        • length-prefixed X.509 certificate (ASN.1 DER form)
      • Sequence of signature algorithm IDs (uint32) - one for each certificate from the previous set, in the same order.
    • flags (uint32) - flags indicating whether or not this set of certs should be in the self-trusted-old-certs struct, and for which operations.
    • length-prefixed sequence of length-prefixed signatures:
      • signature algorithm ID (uint32) - must match the one from the signed data section
      • length-prefixed signature over the above signed data

Multiple ancestors in proof-of-rotation struct

v3 scheme also doesn't handle two different keys rotating to the same signing key for the same app. This differs from the case of an acquisition, where the acquiring company would like to move the acquired app to use its signing key to share permissions. The acquisition is viewed as a supported use-case because the new app would be distinguished by its package name and could contain its own proof-of-rotation struct. The unsupported case, of the same app having two different paths to get to the same cert, breaks a lot of the assumptions made in the key rotation design.

Verification

In Android 9 and higher, APKs can be verified according to the APK Signature Scheme v3, v2 scheme, or v1 scheme. Older platforms ignore v3 signatures and try to verify v2 signatures, then v1.

APK signature verification process

Figure 1. APK signature verification process

APK Signature Scheme v3 verification

  1. Locate the APK Signing Block and verify that:
    1. Two size fields of APK Signing Block contain the same value.
    2. ZIP Central Directory is immediately followed by ZIP End of Central Directory record.
    3. ZIP End of Central Directory is not followed by more data.
  2. Locate the first APK Signature Scheme v3 Block inside the APK Signing Block. If the v3 Block is present, proceed to step 3. Otherwise, fall back to verifying the APK using v2 scheme.
  3. For each signer in the APK Signature Scheme v3 Block with a min and max SDK version that is in range of the current platform:
    1. Choose the strongest supported signature algorithm ID from signatures. The strength ordering is up to each implementation/platform version.
    2. Verify the corresponding signature from signatures against signed data using public key. (It is now safe to parse signed data.)
    3. Verify the min and max SDK versions in the signed data match those specified for the signer.
    4. Verify that the ordered list of signature algorithm IDs in digests and signatures is identical. (This is to prevent signature stripping/addition.)
    5. Compute the digest of APK contents using the same digest algorithm as the digest algorithm used by the signature algorithm.
    6. Verify that the computed digest is identical to the corresponding digest from digests.
    7. Verify that SubjectPublicKeyInfo of the first certificate of certificates is identical to public key.
    8. If the proof-of-rotation attribute exists for the signer verify that the struct is valid and this signer is the last certificate in the list.
  4. Verification succeeds if exactly one signer was found in range of the current platform and step 3 succeeded for that signer.

Validation

To test that your device supports v3 properly, run the PkgInstallSignatureVerificationTest.java CTS tests in cts/hostsidetests/appsecurity/src/android/appsecurity/cts/.