Schema di firma dell'APK v2

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 lo schema di firma APK v2 inserisce un blocco di firma APK nel file APK immediatamente prima della sezione Directory centrale ZIP. All'interno del blocco di firma APK, le firme v2 e le informazioni sull'identità del firmatario sono archiviate in un blocco APK Signature Scheme v2 .

APK prima e dopo la firma

Figura 1. APK prima e dopo la firma

Lo schema di firma APK v2 è stato introdotto in Android 7.0 (Nougat). Per rendere installabile un APK 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 con il formato APK v1, le firme APK v2 e più recenti vengono archiviate all'interno di un blocco firma APK, un nuovo contenitore introdotto per supportare lo schema di firma APK v2. In un file APK, il blocco firma APK si trova immediatamente prima della directory centrale ZIP, che si trova alla fine del file.

Il blocco contiene coppie ID-valore racchiuse in modo da semplificare l'individuazione del blocco nell'APK. La firma v2 dell'APK viene archiviata come coppia ID-valore con ID 0x7109871a.

Formato

Il formato del blocco firma APK è il seguente (tutti i campi numerici sono little-endian):

  • size of block in byte (escluso questo campo) (uint64)
  • Sequenza di coppie ID-valore con prefisso di 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 innanzitutto l'inizio della directory centrale ZIP (trovando il record 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 della firma dell'APK. La size of block punta quindi in modo efficiente all'inizio del blocco nel file.

Le coppie ID-valore con ID sconosciuti devono essere ignorate durante l'interpretazione del blocco.

Blocco v2 dello schema di firma APK

L'APK è firmato da uno o più firmatari/identità, ciascuno rappresentato da una chiave di firma. Queste informazioni vengono archiviate come blocco v2 dello schema di firma APK. Per ciascun firmatario vengono memorizzate le seguenti informazioni:

  • tuple (algoritmo di firma, digest, firma). 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 ciascun 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 futura di metodi di firma più potenti in modo compatibile con le versioni precedenti. L'approccio suggerito è verificare la firma più forte.

Formato

Il blocco APK Signature Scheme v2 è archiviato all'interno del blocco firma APK con l'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 utilizzano uint32 per la lunghezza):

  • sequenza con prefisso di lunghezza signer con prefisso di lunghezza:
    • signed data con prefisso di lunghezza:
      • sequenza con prefisso di lunghezza di digests con prefisso di lunghezza:
      • sequenza con prefisso di lunghezza dei certificates X.509:
        • certificate X.509 con prefisso di lunghezza (modulo DER ASN.1)
      • 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 signatures con prefisso di lunghezza:
      • signature algorithm ID (uint32)
      • signature con prefisso di lunghezza sui signed data
    • public key con prefisso di lunghezza (SubjectPublicKeyInfo, modulo ASN.1 DER)

ID dell'algoritmo di firma

  • 0x0101: RSASSA-PSS con digest SHA2-256, SHA2-256 MGF1, 32 byte di sale, trailer: 0xbc
  • 0x0102—RSASSA-PSS con digest SHA2-512, SHA2-512 MGF1, 64 byte di sale, trailer: 0xbc
  • 0x0103: RSASSA-PKCS1-v1_5 con digest SHA2-256. Questo è per i sistemi di build che richiedono firme deterministiche.
  • 0x0104: RSASSA-PKCS1-v1_5 con digest SHA2-512. Questo è per i sistemi di build 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 delle 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 APK, un APK è composto da quattro sezioni:

  1. Contenuto delle voci ZIP (dall'offset 0 fino all'inizio del blocco firma APK)
  2. Blocco firma APK
  3. Elenco centrale ZIP
  4. ZIP Fine della directory centrale

Sezioni APK dopo la firma

Figura 2. Sezioni APK dopo la firma

APK Signature Scheme v2 protegge l'integrità delle sezioni 1, 3, 4 e i blocchi 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 dei loro contenuti archiviati in blocchi signed data che sono, a loro volta, protetti da una o più firme.

Il digest sulle sezioni 1, 3 e 4 viene calcolato come segue, in modo simile ad un albero Merkle a due livelli. Ogni sezione è suddivisa in blocchi consecutivi da 1 MB (2 20 byte). L'ultimo pezzo di ciascuna sezione potrebbe essere più breve. Il digest di ciascun blocco viene calcolato sulla concatenazione del byte 0xa5 , della lunghezza del blocco in byte (little-endian uint32) e del contenuto del blocco. Il digest di livello superiore 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 appaiono nell'APK. Il digest viene calcolato in blocchi per consentire di accelerare il calcolo parallelizzandolo.

Raccolta APK

Figura 3. Digest dell'APK

La protezione della sezione 4 (Fine ZIP della directory centrale) è complicata dalla sezione contenente l'offset della directory centrale ZIP. L'offset cambia quando cambia la dimensione del blocco di firma APK, 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 di firma APK.

Protezioni di rollback

Un utente malintenzionato potrebbe tentare di verificare un APK con firma v2 come APK con firma v1 su piattaforme Android che supportano la verifica dell'APK con firma 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 file META-INF/*.SF. Il valore dell'attributo è un insieme separato da virgole di ID dello schema di firma APK (l'ID di questo schema è 2). Durante la verifica della firma v1, il verificatore APK è tenuto a rifiutare gli APK che non dispongono di una firma per lo schema di firma APK che il verificatore preferisce da questo set (ad esempio, schema v2). Questa protezione si basa sul fatto che i contenuti dei file META-INF/*.SF sono protetti dalle firme v1. Consulta la sezione sulla verifica dell'APK firmato JAR .

Un utente malintenzionato potrebbe tentare di eliminare 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 signed data protetto da ciascuna firma.

Verifica

In Android 7.0 e versioni successive, gli APK possono essere verificati in base allo schema di firma APK v2+ o alla firma JAR (schema v1). Le piattaforme più vecchie ignorano le firme v2 e verificano solo le firme v1.

Processo di verifica della firma APK

Figura 4. Processo di verifica della firma APK (nuovi passaggi in rosso)

Verifica dello schema di firma APK v2

  1. Individua il blocco firma APK e verifica che:
    1. Due campi dimensione del Blocco firma APK contengono lo stesso valore.
    2. ZIP Central Directory è immediatamente seguito dal record ZIP End of Central Directory.
    3. ZIP La fine della directory centrale non è seguita da altri dati.
  2. Individua il primo blocco APK Signature Scheme v2 all'interno del blocco di firma APK. Se è presente il blocco v2, procedi al passaggio 3. Altrimenti, torna alla verifica dell'APK utilizzando lo schema v1 .
  3. Per ogni signer nel blocco APK Signature Scheme v2:
    1. Scegli l' signature algorithm ID supportato più potente tra signatures . L'ordine di forza dipende da ciascuna versione di implementazione/piattaforma.
    2. Verificare la signature corrispondente dalle signatures rispetto signed data utilizzando public key . (Ora è sicuro analizzare signed data .)
    3. Verificare che l'elenco ordinato degli ID dell'algoritmo di firma nei digests e signatures sia identico. (Questo per evitare la rimozione/aggiunta di firme.)
    4. Calcola il digest dei contenuti APK utilizzando lo stesso algoritmo digest utilizzato dall'algoritmo di firma.
    5. Verificare che il digest calcolato sia identico al corrispondente digest dei digests .
    6. Verificare che ObjectPublicKeyInfo del primo certificate di certificates sia identico alla public key .
  4. La verifica ha esito positivo se è stato trovato almeno un signer e il passaggio 3 ha avuto esito positivo per ciascun signer 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 dove tutte le voci devono essere firmate dallo stesso insieme di firmatari. La sua integrità è verificata come segue:

  1. Ciascun firmatario è rappresentato da una voce JAR META-INF/<signer>.SF e META-INF/<signer>.(RSA|DSA|EC).
  2. <signer>.(RSA|DSA|EC) è un CMS ContentInfo PKCS #7 con struttura SignedData la cui firma è verificata sul file <signer>.SF.
  3. Il file <signer>.SF contiene un digest dell'intero file di META-INF/MANIFEST.MF e digest di ciascuna sezione di META-INF/MANIFEST.MF. Viene verificato il digest dell'intero file di MANIFEST.MF. Se ciò fallisce, viene invece verificato il digest di ciascuna sezione MANIFEST.MF.
  4. META-INF/MANIFEST.MF contiene, per ciascuna voce JAR con integrità protetta, una sezione denominata corrispondentemente contenente il digest del contenuto non compresso della voce. Tutti questi digest sono verificati.
  5. La verifica dell'APK ha esito negativo 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 <signer>.(RSA|DSA|EC) -> <signer>.SF -> MANIFEST.MF -> contenuto di ciascuna voce JAR con integrità protetta.