Le immagini del sistema operativo Android utilizzano le firme crittografiche in due posizioni:
- Ogni file
.apk
all'interno dell'immagine deve essere firmato. Package Manager di Android utilizza una firma.apk
in due modi:- Quando un'applicazione viene sostituita, deve essere firmata con la stessa chiave della vecchia applicazione per poter accedere ai dati della vecchia applicazione. Ciò vale sia per l'aggiornamento delle app utente mediante la sovrascrittura di
.apk
, sia per l'override di un'app di sistema con una versione più recente installata in/data
. - Se due o più applicazioni vogliono condividere un ID utente (in modo che possano condividere dati, ecc.), devono essere firmate con la stessa chiave.
- Quando un'applicazione viene sostituita, deve essere firmata con la stessa chiave della vecchia applicazione per poter accedere ai dati della vecchia applicazione. Ciò vale sia per l'aggiornamento delle app utente mediante la sovrascrittura di
- I pacchetti di aggiornamento OTA devono essere firmati con una delle chiavi previste dal sistema, altrimenti il processo di installazione li rifiuterà.
Rilascia le chiavi
L'albero di Android include chiavi di test in build/target/product/security
. La creazione di un'immagine del sistema operativo Android utilizzando make
firmerà tutti i file .apk
utilizzando le chiavi di test. Poiché le chiavi di test sono pubblicamente note, chiunque può firmare i propri file .apk con le stesse chiavi, il che potrebbe consentire loro di sostituire o dirottare le app di sistema integrate nell'immagine del sistema operativo. Per questo motivo è fondamentale firmare qualsiasi immagine del sistema operativo Android rilasciata o distribuita pubblicamente con un set speciale di chiavi di rilascio a cui solo tu hai accesso.
Per generare il tuo set univoco di chiavi di rilascio, esegui questi comandi dalla radice del tuo albero Android:
subject='/C=US/ST=California/L=Mountain View/O=Android/OU=Android/CN=Android/emailAddress=android@android.com'
mkdir ~/.android-certs
for x in releasekey platform shared media networkstack; do \ ./development/tools/make_key ~/.android-certs/$x "$subject"; \ done
$subject
dovrebbe essere modificato per riflettere le informazioni della tua organizzazione. Puoi utilizzare qualsiasi directory, ma fai attenzione a scegliere una posizione di cui è stato eseguito il backup e sicura. Alcuni fornitori scelgono di crittografare la propria chiave privata con una passphrase forte e di archiviare la chiave crittografata nel controllo del codice sorgente; altri memorizzano le loro chiavi di rilascio da qualche altra parte, ad esempio su un computer con traferro.
Per generare un'immagine di rilascio, utilizzare:
make dist
sign_target_files_apks \ -o \ # explained in the next section --default_key_mappings ~/.android-certs out/dist/*-target_files-*.zip \ signed-target_files.zip
Lo script sign_target_files_apks
accetta un file di destinazione .zip
come input e produce un nuovo file di destinazione .zip
in cui tutti i file .apk
sono stati firmati con nuove chiavi. Le immagini appena firmate possono essere trovate in IMAGES/
in signed-target_files.zip
.
Firma dei pacchetti OTA
Un zip di file di destinazione firmato può essere convertito in un zip di aggiornamento OTA firmato utilizzando la procedura seguente:
ota_from_target_files \
-k (--package_key)
signed-target_files.zip \
signed-ota_update.zip
Firme e sideload
Il sideloading non ignora il normale meccanismo di verifica della firma del pacchetto del ripristino: prima di installare un pacchetto, il ripristino verificherà che sia firmato con una delle chiavi private che corrispondono alle chiavi pubbliche memorizzate nella partizione di ripristino, proprio come farebbe per un pacchetto consegnato tramite -aria.
I pacchetti di aggiornamento ricevuti dal sistema principale vengono in genere verificati due volte: una volta dal sistema principale, utilizzando il metodo RecoverySystem.verifyPackage()
nell'API Android, e poi di nuovo tramite il ripristino. L'API RecoverySystem controlla la firma rispetto alle chiavi pubbliche memorizzate nel sistema principale, nel file /system/etc/security/otacerts.zip
(per impostazione predefinita). Il ripristino verifica la firma rispetto alle chiavi pubbliche memorizzate nel disco RAM della partizione di ripristino, nel file /res/keys
.
Per impostazione predefinita, il file di destinazione .zip
prodotto dalla build imposta il certificato OTA in modo che corrisponda alla chiave di test. Su un'immagine rilasciata, è necessario utilizzare un certificato diverso in modo che i dispositivi possano verificare l'autenticità del pacchetto di aggiornamento. Il passaggio del flag -o
a sign_target_files_apks
, come mostrato nella sezione precedente, sostituisce il certificato della chiave di test con il certificato della chiave di rilascio dalla directory certs.
Normalmente l'immagine di sistema e l'immagine di ripristino memorizzano lo stesso set di chiavi pubbliche OTA. Aggiungendo una chiave solo al set di chiavi di ripristino, è possibile firmare pacchetti che possono essere installati solo tramite sideloading (supponendo che il meccanismo di download degli aggiornamenti del sistema principale stia eseguendo correttamente la verifica rispetto a otacerts.zip). Puoi specificare chiavi extra da includere solo nel ripristino impostando la variabile PRODUCT_EXTRA_RECOVERY_KEYS nella definizione del prodotto:
vendor/yoyodyne/tardis/products/tardis.mk
[...] PRODUCT_EXTRA_RECOVERY_KEYS := vendor/yoyodyne/security/tardis/sideload
Ciò include il vendor/yoyodyne/security/tardis/sideload.x509.pem
nel file delle chiavi di ripristino in modo che possa installare i pacchetti firmati con esso. Tuttavia, la chiave aggiuntiva non è inclusa in otacerts.zip, quindi i sistemi che verificano correttamente i pacchetti scaricati non invocano il ripristino per i pacchetti firmati con questa chiave.
Certificati e chiavi private
Ciascuna chiave è contenuta in due file: il certificato , con estensione .x509.pem, e la chiave privata , con estensione .pk8. La chiave privata deve essere mantenuta segreta ed è necessaria per firmare un pacchetto. La chiave stessa può essere protetta da una password. Il certificato, al contrario, contiene solo la metà pubblica della chiave, quindi può essere ampiamente distribuito. Viene utilizzato per verificare che un pacchetto sia stato firmato dalla chiave privata corrispondente.
La build standard di Android utilizza cinque chiavi, che risiedono tutte in build/target/product/security
:
- chiave di prova
- Chiave predefinita generica per i pacchetti che non specificano altrimenti una chiave.
- piattaforma
- Chiave di test per i pacchetti che fanno parte della piattaforma principale.
- condivisa
- Chiave di prova per le cose che sono condivise nel processo di casa/contatti.
- media
- Chiave di prova per i pacchetti che fanno parte del sistema multimediale/di download.
I singoli pacchetti specificano una di queste chiavi impostando LOCAL_CERTIFICATE nel loro file Android.mk. (testkey viene utilizzato se questa variabile non è impostata.) Puoi anche specificare una chiave completamente diversa in base al nome del percorso, ad esempio:
device/yoyodyne/apps/SpecialApp/Android.mk
[...] LOCAL_CERTIFICATE := device/yoyodyne/security/special
Ora la build usa la device/yoyodyne/security/special.{x509.pem,pk8}
per firmare SpecialApp.apk. La build può utilizzare solo chiavi private non protette da password.
Opzioni di firma avanzate
Sostituzione della chiave di firma dell'APK
Lo script di firma sign_target_files_apks
funziona sui file di destinazione generati per una build. Tutte le informazioni sui certificati e sulle chiavi private utilizzate in fase di compilazione sono incluse nei file di destinazione. Quando si esegue lo script di firma per firmare per il rilascio, le chiavi di firma possono essere sostituite in base al nome della chiave o al nome dell'APK.
Usa i --key_mapping
e --default_key_mappings
per specificare la sostituzione della chiave in base ai nomi delle chiavi:
- Il
--key_mapping src_key = dest_key
specifica la sostituzione di una chiave alla volta. - Il flag
--default_key_mappings dir
specifica una directory con cinque chiavi per sostituire tutte le chiavi inbuild/target/product/security
; equivale a usare--key_mapping
cinque volte per specificare le mappature.
build/target/product/security/testkey = dir/releasekey build/target/product/security/platform = dir/platform build/target/product/security/shared = dir/shared build/target/product/security/media = dir/media build/target/product/security/networkstack = dir/networkstack
Usa il flag della --extra_apks apk_name1,apk_name2,... = key
per specificare le sostituzioni della chiave di firma in base ai nomi APK. Se key
viene lasciata vuota, lo script considera gli APK specificati come prefirmati.
Per l'ipotetico prodotto tardis, sono necessarie sei chiavi protette da password: cinque per sostituire le cinque in build/target/product/security
e una per sostituire la chiave aggiuntiva device/yoyodyne/security/special
richiesta da SpecialApp nell'esempio sopra. Se le chiavi erano nei seguenti file:
vendor/yoyodyne/security/tardis/releasekey.x509.pem vendor/yoyodyne/security/tardis/releasekey.pk8 vendor/yoyodyne/security/tardis/platform.x509.pem vendor/yoyodyne/security/tardis/platform.pk8 vendor/yoyodyne/security/tardis/shared.x509.pem vendor/yoyodyne/security/tardis/shared.pk8 vendor/yoyodyne/security/tardis/media.x509.pem vendor/yoyodyne/security/tardis/media.pk8 vendor/yoyodyne/security/tardis/networkstack.x509.pem vendor/yoyodyne/security/tardis/networkstack.pk8 vendor/yoyodyne/security/special.x509.pem vendor/yoyodyne/security/special.pk8 # NOT password protected vendor/yoyodyne/security/special-release.x509.pem vendor/yoyodyne/security/special-release.pk8 # password protected
Quindi firmeresti tutte le app in questo modo:
./build/make/tools/releasetools/sign_target_files_apks \
--default_key_mappings vendor/yoyodyne/security/tardis \
--key_mapping vendor/yoyodyne/security/special=vendor/yoyodyne/security/special-release \
--extra_apks PresignedApp= \
-o tardis-target_files.zip \
signed-tardis-target_files.zip
Questo fa apparire quanto segue:
Enter password for vendor/yoyodyne/security/special-release key> Enter password for vendor/yoyodyne/security/tardis/networkstack key> Enter password for vendor/yoyodyne/security/tardis/media key> Enter password for vendor/yoyodyne/security/tardis/platform key> Enter password for vendor/yoyodyne/security/tardis/releasekey key> Enter password for vendor/yoyodyne/security/tardis/shared key> signing: Phone.apk (vendor/yoyodyne/security/tardis/platform) signing: Camera.apk (vendor/yoyodyne/security/tardis/media) signing: NetworkStack.apk (vendor/yoyodyne/security/tardis/networkstack) signing: Special.apk (vendor/yoyodyne/security/special-release) signing: Email.apk (vendor/yoyodyne/security/tardis/releasekey) [...] signing: ContactsProvider.apk (vendor/yoyodyne/security/tardis/shared) signing: Launcher.apk (vendor/yoyodyne/security/tardis/shared) NOT signing: PresignedApp.apk (skipped due to special cert string) rewriting SYSTEM/build.prop: replace: ro.build.description=tardis-user Eclair ERC91 15449 test-keys with: ro.build.description=tardis-user Eclair ERC91 15449 release-keys replace: ro.build.fingerprint=generic/tardis/tardis/tardis:Eclair/ERC91/15449:user/test-keys with: ro.build.fingerprint=generic/tardis/tardis/tardis:Eclair/ERC91/15449:user/release-keys signing: framework-res.apk (vendor/yoyodyne/security/tardis/platform) rewriting RECOVERY/RAMDISK/default.prop: replace: ro.build.description=tardis-user Eclair ERC91 15449 test-keys with: ro.build.description=tardis-user Eclair ERC91 15449 release-keys replace: ro.build.fingerprint=generic/tardis/tardis/tardis:Eclair/ERC91/15449:user/test-keys with: ro.build.fingerprint=generic/tardis/tardis/tardis:Eclair/ERC91/15449:user/release-keys using: vendor/yoyodyne/security/tardis/releasekey.x509.pem for OTA package verification done.
Dopo aver richiesto all'utente le password per tutte le chiavi protette da password, lo script firma nuovamente tutti i file APK nella destinazione di input .zip
con le chiavi di rilascio. Prima di eseguire il comando, puoi anche impostare la variabile di ambiente ANDROID_PW_FILE
su un nome file temporaneo; lo script richiama quindi il tuo editor per consentirti di inserire le password per tutte le chiavi (questo potrebbe essere un modo più conveniente per inserire le password).
Sostituzione della chiave di firma APEX
Android 10 introduce il formato di file APEX per l'installazione di moduli di sistema di livello inferiore. Come spiegato in Firma APEX , ogni file APEX è firmato con due chiavi: una per l'immagine del mini file system all'interno di un APEX e l'altra per l'intero APEX.
Quando si firma per il rilascio, le due chiavi di firma per un file APEX vengono sostituite con le chiavi di rilascio. La chiave del carico utile del file system viene specificata con il flag --extra_apex_payload
e l'intera chiave di firma del file APEX viene specificata con il flag --extra_apks
.
Per il prodotto tardis, supponi di avere la seguente configurazione della chiave per i file APEX com.android.conscrypt.apex
, com.android.media.apex
e com.android.runtime.release.apex
.
name="com.android.conscrypt.apex" public_key="PRESIGNED" private_key="PRESIGNED" container_certificate="PRESIGNED" container_private_key="PRESIGNED" name="com.android.media.apex" public_key="PRESIGNED" private_key="PRESIGNED" container_certificate="PRESIGNED" container_private_key="PRESIGNED" name="com.android.runtime.release.apex" public_key="vendor/yoyodyne/security/testkeys/com.android.runtime.avbpubkey" private_key="vendor/yoyodyne/security/testkeys/com.android.runtime.pem" container_certificate="vendor/yoyodyne/security/testkeys/com.google.android.runtime.release_container.x509.pem" container_private_key="vendor/yoyodyne/security/testkeys/com.google.android.runtime.release_container.pk8"
E hai i seguenti file che contengono le chiavi di rilascio:
vendor/yoyodyne/security/runtime_apex_container.x509.pem vendor/yoyodyne/security/runtime_apex_container.pk8 vendor/yoyodyne/security/runtime_apex_payload.pem
Il comando seguente sovrascrive le chiavi di firma per com.android.runtime.release.apex
e com.android.tzdata.apex
durante la firma del rilascio. In particolare, com.android.runtime.release.apex
è firmato con le chiavi di rilascio specificate ( runtime_apex_container
per il file APEX e runtime_apex_payload
per il payload dell'immagine del file). com.android.tzdata.apex
è considerato pre-firmato. Tutti gli altri file APEX vengono gestiti dalla configurazione predefinita come elencato nei file di destinazione.
./build/make/tools/releasetools/sign_target_files_apks \
--default_key_mappings vendor/yoyodyne/security/tardis \
--extra_apks com.android.runtime.release.apex=vendor/yoyodyne/security/runtime_apex_container \
--extra_apex_payload_key com.android.runtime.release.apex=vendor/yoyodyne/security/runtime_apex_payload.pem \
--extra_apks com.android.media.apex= \
--extra_apex_payload_key com.android.media.apex= \
-o tardis-target_files.zip \
signed-tardis-target_files.zip
L'esecuzione del comando precedente fornisce i seguenti registri:
[...] signing: com.android.runtime.release.apex container (vendor/yoyodyne/security/runtime_apex_container) : com.android.runtime.release.apex payload (vendor/yoyodyne/security/runtime_apex_payload.pem) NOT signing: com.android.conscrypt.apex (skipped due to special cert string) NOT signing: com.android.media.apex (skipped due to special cert string) [...]
Altre opzioni
Lo script di firma sign_target_files_apks
riscrive la descrizione della build e l'impronta digitale nei file delle proprietà della build per riflettere che la build è una build firmata. Il flag --tag_changes
controlla quali modifiche vengono apportate all'impronta digitale. Esegui lo script con -h
per vedere la documentazione su tutti i flag.
Generazione manuale di chiavi
Android utilizza chiavi RSA a 2048 bit con esponente pubblico 3. Puoi generare coppie certificato/chiave privata utilizzando lo strumento openssl da openssl.org :
# generate RSA keyopenssl genrsa -3 -out temp.pem 2048
Generating RSA private key, 2048 bit long modulus ....+++ .....................+++ e is 3 (0x3) # create a certificate with the public part of the keyopenssl req -new -x509 -key temp.pem -out releasekey.x509.pem -days 10000 -subj '/C=US/ST=California/L=San Narciso/O=Yoyodyne, Inc./OU=Yoyodyne Mobility/CN=Yoyodyne/emailAddress=yoyodyne@example.com'
# create a PKCS#8-formatted version of the private keyopenssl pkcs8 -in temp.pem -topk8 -outform DER -out releasekey.pk8 -nocrypt
# securely delete the temp.pem fileshred --remove temp.pem
Il comando openssl pkcs8 fornito sopra crea un file .pk8 senza password, adatto per l'uso con il sistema di compilazione. Per creare un .pk8 protetto con una password (cosa che dovresti fare per tutte le chiavi di rilascio effettive), sostituisci l'argomento -nocrypt
con -passout stdin
; quindi openssl crittograferà la chiave privata con una password letta dallo standard input. Non viene stampato alcun prompt, quindi se stdin è il terminale il programma sembrerà bloccato quando sta davvero solo aspettando che tu inserisca una password. È possibile utilizzare altri valori per l'argomento-passout per leggere la password da altre posizioni; per i dettagli, vedere la documentazione di openssl .
Il file intermedio temp.pem contiene la chiave privata senza alcun tipo di protezione tramite password, quindi eliminala con attenzione durante la generazione delle chiavi di rilascio. In particolare, l'utilità GNUshred potrebbe non essere efficace su filesystem di rete o journal. È possibile utilizzare una directory di lavoro situata in un disco RAM (come una partizione tmpfs) durante la generazione di chiavi per garantire che gli intermedi non vengano esposti inavvertitamente.
Creazione di file immagine
Una volta che hai firmato-target-files.zip, devi creare l'immagine in modo da poterla inserire su un dispositivo. Per creare l'immagine firmata dai file di destinazione, eseguire il comando seguente dalla radice dell'albero di Android:
img_from_target_files signed-target-files.zip signed-img.zipIl file risultante,
signed-img.zip
, contiene tutti i file .img. Per caricare un'immagine su un dispositivo, utilizzare l'avvio rapido come segue:fastboot update signed-img.zip