APK Signature Scheme v2 è uno schema di firma dell'intero file che aumenta la velocità di verifica e rafforza le garanzie di integrità rilevando eventuali modifiche alle parti protette dell'APK.
La firma utilizzando APK Signature Scheme v2 inserisce un APK Signing Block nel file APK immediatamente prima della sezione ZIP Central Directory. All'interno dell'APK Signing Block, le firme v2 e le informazioni sull'identità del firmatario sono archiviate in un APK Signature Scheme v2 Block .
Figura 1. APK prima e dopo la firma
APK Signature Scheme v2 è stato introdotto in Android 7.0 (Nougat). Per rendere un APK installabile su Android 6.0 (Marshmallow) e dispositivi precedenti, l'APK deve essere firmato utilizzando la firma JAR prima di essere firmato con lo schema v2.
Blocco firma APK
Per mantenere la compatibilità con le versioni precedenti del formato APK v1, le firme APK v2 e più recenti vengono archiviate all'interno di un APK Signing Block, un nuovo contenitore introdotto per supportare APK Signature Scheme v2. In un file APK, l'APK Signing Block si trova immediatamente prima della directory centrale ZIP, che si trova alla fine del file.
Il blocco contiene coppie di valori ID racchiuse in modo da semplificare l'individuazione del blocco nell'APK. La firma v2 dell'APK viene archiviata come coppia di valori ID con ID 0x7109871a.
Formato
Il formato dell'APK Signing Block è il seguente (tutti i campi numerici sono little-endian):
-
size of block
in byte (escluso questo campo) (uint64) - Sequenza di coppie di valori ID con prefisso lunghezza uint64:
-
ID
(uint32) -
value
(lunghezza variabile: lunghezza della coppia - 4 byte)
-
-
size of block
in byte: uguale al primo campo (uint64) -
magic
"APK Sig Block 42" (16 byte)
L'APK viene analizzato trovando prima l'inizio della directory centrale ZIP (trovando il record di fine ZIP della directory centrale alla fine del file, quindi leggendo l'offset iniziale della directory centrale dal record). Il valore magic
fornisce un modo rapido per stabilire che ciò che precede la directory centrale è probabilmente il blocco firma APK. La size of block
punta quindi in modo efficiente all'inizio del blocco nel file.
Le coppie di valori ID con ID sconosciuti devono essere ignorate durante l'interpretazione del blocco.
Blocco schema firma APK v2
L'APK è firmato da uno o più firmatari/identità, ciascuno rappresentato da una chiave di firma. Queste informazioni vengono archiviate come un blocco APK Signature Scheme v2. Per ogni firmatario vengono memorizzate le seguenti informazioni:
- (algoritmo di firma, digest, firma) tuple. Il digest viene archiviato per separare la verifica della firma dal controllo dell'integrità dei contenuti dell'APK.
- Catena di certificati X.509 che rappresenta l'identità del firmatario.
- Attributi aggiuntivi come coppie chiave-valore.
Per ogni firmatario, l'APK viene verificato utilizzando una firma supportata dall'elenco fornito. Le firme con algoritmi di firma sconosciuti vengono ignorate. Spetta a ciascuna implementazione scegliere quale firma utilizzare quando vengono rilevate più firme supportate. Ciò consente l'introduzione di metodi di firma più efficaci in futuro in modo compatibile con le versioni precedenti. L'approccio suggerito consiste nel verificare la firma più forte.
Formato
Il blocco APK Signature Scheme v2 è archiviato all'interno del blocco firma APK con ID 0x7109871a
.
Il formato del blocco APK Signature Scheme v2 è il seguente (tutti i valori numerici sono little-endian, tutti i campi con prefisso di lunghezza usano uint32 per la lunghezza):
- sequenza con prefisso di lunghezza del
signer
con prefisso di lunghezza:-
signed data
con prefisso di lunghezza:- sequenza con prefisso di lunghezza di
digests
con prefisso di lunghezza:-
signature algorithm ID
(uint32) -
digest
(prefissato dalla lunghezza): vedere Contenuti protetti dall'integrità
-
- sequenza prefissata di lunghezza di
certificates
X.509:-
certificate
X.509 con prefisso di lunghezza (modulo ASN.1 DER)
-
- sequenza con prefisso di lunghezza di
additional attributes
con prefisso di lunghezza:-
ID
(uint32) -
value
(lunghezza variabile: lunghezza dell'attributo aggiuntivo - 4 byte)
-
- sequenza con prefisso di lunghezza di
- sequenza con prefisso di lunghezza di
signatures
con prefisso di lunghezza:-
signature algorithm ID
(uint32) -
signature
con lunghezza prefissata suisigned data
-
-
public key
con prefisso di lunghezza (SubjectPublicKeyInfo, modulo ASN.1 DER)
-
ID algoritmo di firma
- 0x0101—RSASSA-PSS con SHA2-256 digest, SHA2-256 MGF1, 32 byte di sale, trailer: 0xbc
- 0x0102—RSASSA-PSS con SHA2-512 digest, SHA2-512 MGF1, 64 byte di sale, trailer: 0xbc
- 0x0103—RSASSA-PKCS1-v1_5 con digest SHA2-256. Questo è per i sistemi di compilazione che richiedono firme deterministiche.
- 0x0104—RSASSA-PKCS1-v1_5 con digest SHA2-512. Questo è per i sistemi di compilazione che richiedono firme deterministiche.
- 0x0201: ECDSA con digest SHA2-256
- 0x0202: ECDSA con digest SHA2-512
- 0x0301: DSA con digest SHA2-256
Tutti gli algoritmi di firma di cui sopra sono supportati dalla piattaforma Android. Gli strumenti di firma possono supportare un sottoinsieme degli algoritmi.
Dimensioni chiavi e curve EC supportate:
- RSA: 1024, 2048, 4096, 8192, 16384
- CE: NIST P-256, P-384, P-521
- DSA: 1024, 2048, 3072
Contenuti protetti dall'integrità
Ai fini della protezione dei contenuti dell'APK, un APK è composto da quattro sezioni:
- Contenuto delle voci ZIP (dall'offset 0 fino all'inizio del blocco firma APK)
- Blocco firma APK
- Directory centrale ZIP
- ZIP Fine della directory centrale
Figura 2. Sezioni APK dopo la firma
APK Signature Scheme v2 protegge l'integrità delle sezioni 1, 3, 4 e i blocchi di signed data
del blocco APK Signature Scheme v2 contenuti nella sezione 2.
L'integrità delle sezioni 1, 3 e 4 è protetta da uno o più riassunti del loro contenuto archiviati in blocchi di signed data
che sono, a loro volta, protetti da una o più firme.
Il riassunto delle sezioni 1, 3 e 4 viene calcolato come segue, in modo simile a un albero Merkle a due livelli . Ogni sezione è suddivisa in blocchi consecutivi da 1 MB (2 20 byte). L'ultimo pezzo in ogni sezione potrebbe essere più corto. Il digest di ogni blocco viene calcolato sulla concatenazione del byte 0xa5
, la lunghezza del blocco in byte (little-endian uint32) e il contenuto del blocco. Il digest di primo livello viene calcolato sulla concatenazione del byte 0x5a
, il numero di blocchi (little-endian uint32) e la concatenazione dei digest dei blocchi nell'ordine in cui i blocchi vengono visualizzati nell'APK. Il digest viene calcolato in blocchi per consentire di accelerare il calcolo parallelizzandolo.
Figura 3. Digest APK
La protezione della sezione 4 (ZIP End of Central Directory) è complicata dalla sezione contenente l'offset della ZIP Central Directory. L'offset cambia quando la dimensione del blocco firma APK cambia, ad esempio, quando viene aggiunta una nuova firma. Pertanto, quando si calcola il digest sull'estremità ZIP della directory centrale, il campo contenente l'offset della directory centrale ZIP deve essere considerato come contenente l'offset del blocco firma APK.
Protezioni di rollback
Un utente malintenzionato potrebbe tentare di far verificare un APK firmato v2 come APK firmato v1 su piattaforme Android che supportano la verifica dell'APK firmato v2. Per mitigare questo attacco, gli APK firmati v2 che sono anche firmati v1 devono contenere un attributo X-Android-APK-Signed nella sezione principale dei relativi file META-INF/*.SF. Il valore dell'attributo è un insieme separato da virgole di ID schema di firma APK (l'ID di questo schema è 2). Quando verifica la firma v1, il verificatore APK deve rifiutare gli APK che non dispongono di una firma per lo schema di firma APK che il verificatore preferisce da questo set (ad es. schema v2). Questa protezione si basa sul fatto che i file META-INF/*.SF dei contenuti sono protetti dalle firme v1. Vedi la sezione sulla verifica dell'APK firmato JAR .
Un utente malintenzionato potrebbe tentare di rimuovere firme più forti dal blocco APK Signature Scheme v2. Per mitigare questo attacco, l'elenco degli ID dell'algoritmo di firma con cui è stato firmato l'APK viene archiviato nel blocco di signed data
protetto da ciascuna firma.
Verifica
In Android 7.0 e versioni successive, gli APK possono essere verificati in base all'APK Signature Scheme v2+ o alla firma JAR (schema v1). Le piattaforme precedenti ignorano le firme v2 e verificano solo le firme v1.
Figura 4. Processo di verifica della firma APK (nuovi passaggi in rosso)
Verifica dello schema di firma APK v2
- Individua il blocco firma APK e verifica che:
- Due campi di dimensioni del Blocco firma APK contengono lo stesso valore.
- ZIP Central Directory è immediatamente seguito dal record ZIP End of Central Directory.
- ZIP La fine della directory centrale non è seguita da altri dati.
- Individua il primo blocco APK Signature Scheme v2 all'interno del blocco firma APK. Se il blocco v2 è presente, vai al passaggio 3. In caso contrario, torna alla verifica dell'APK utilizzando lo schema v1 .
- Per ogni
signer
nel blocco APK Signature Scheme v2:- Scegli l'
signature algorithm ID
di firma più forte supportato dallesignatures
. L'ordine di forza dipende da ciascuna versione di implementazione/piattaforma. - Verificare la
signature
corrispondente dallesignatures
rispetto aisigned data
utilizzandopublic key
. (Ora è sicuro analizzaresigned data
.) - Verificare che l'elenco ordinato degli ID dell'algoritmo di firma nei
digests
e nellesignatures
sia identico. (Questo serve per impedire la rimozione/aggiunta della firma.) - Calcola il digest dei contenuti APK utilizzando lo stesso algoritmo digest dell'algoritmo digest utilizzato dall'algoritmo di firma.
- Verificare che il digest calcolato sia identico al corrispondente
digest
daidigests
. - Verificare che SubjectPublicKeyInfo del primo
certificate
dicertificates
sia identico allapublic key
.
- Scegli l'
- La verifica ha esito positivo se è stato trovato almeno un
signer
e il passaggio 3 è riuscito per ognisigner
trovato.
Nota : l'APK non deve essere verificato utilizzando lo schema v1 se si verifica un errore nel passaggio 3 o 4.
Verifica APK firmata JAR (schema v1)
L'APK firmato JAR è un JAR firmato standard , che deve contenere esattamente le voci elencate in META-INF/MANIFEST.MF e in cui tutte le voci devono essere firmate dallo stesso gruppo di firmatari. La sua integrità è verificata come segue:
- Ciascun firmatario è rappresentato da una voce JAR META-INF/<signer>.SF e META-INF/<signer>.(RSA|DSA|EC).
- <signer>.(RSA|DSA|EC) è un CMS ContentInfo PKCS #7 con struttura SignedData la cui firma è verificata sul file <signer>.SF.
- Il file <firmatario>.SF contiene un digest dell'intero file di META-INF/MANIFEST.MF e digest di ciascuna sezione di META-INF/MANIFEST.MF. L'intero file digest di MANIFEST.MF viene verificato. Se ciò non riesce, viene invece verificato il digest di ciascuna sezione MANIFEST.MF.
- META-INF/MANIFEST.MF contiene, per ogni voce JAR protetta dall'integrità, una sezione con il nome corrispondente contenente il digest del contenuto non compresso della voce. Tutti questi digest sono verificati.
- La verifica dell'APK non riesce se l'APK contiene voci JAR che non sono elencate in MANIFEST.MF e non fanno parte della firma JAR.
La catena di protezione è quindi <firmatario>.(RSA|DSA|EC) -> <firmatario>.SF -> MANIFEST.MF -> contenuto di ciascuna voce JAR protetta dall'integrità.