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)
- length-prefixed X.509
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
- length-prefixed sequence of length-prefixed
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
oversigned data
- length-prefixed
public key
(SubjectPublicKeyInfo, ASN.1 DER form)
- length-prefixed
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
- length-prefixed X.509
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 abovesigned data
- length-prefixed
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)
- length-prefixed X.509
- Sequence of
signature algorithm IDs
(uint32) - one for each certificate from the previous set, in the same order.
- length-prefixed sequence of
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 abovesigned 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 scheme v3 verification
- Locate the APK signing block and verify that:
- Two size fields of APK signing block contain the same value.
- ZIP Central Directory is immediately followed by ZIP End of Central Directory record.
- ZIP End of Central Directory is not followed by more data.
- 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.
- 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:- Choose the strongest supported
signature algorithm ID
fromsignatures
. The strength ordering is up to each implementation/platform version. - Verify the corresponding
signature
fromsignatures
againstsigned data
usingpublic key
. (It is now safe to parsesigned data
.) - Verify the min and max SDK versions in the signed data match those
specified for the
signer
. - Verify that the ordered list of signature algorithm IDs in
digests
andsignatures
is identical. (This is to prevent signature stripping/addition.) - Compute the digest of APK contents using the same digest algorithm as the digest algorithm used by the signature algorithm.
- Verify that the computed digest is identical to the corresponding
digest
fromdigests
. - Verify that SubjectPublicKeyInfo of the first
certificate
ofcertificates
is identical topublic key
. - If the proof-of-rotation attribute exists for the
signer
verify that the struct is valid and thissigner
is the last certificate in the list.
- Choose the strongest supported
- Verification succeeds if exactly one
signer
was found in range of the current platform and step 3 succeeded for thatsigner
.
Validation
To test that your device supports v3 properly, run the
PkgInstallSignatureVerificationTest.java
CTS tests in
cts/hostsidetests/appsecurity/src/android/appsecurity/cts/
.