Bei Android-Betriebssystem-Images werden an zwei Stellen kryptografische Signaturen verwendet:
- Jede
.apk
-Datei im Bild muss signiert sein. Der Paketmanager von Android verwendet eine.apk
-Signatur auf zwei Arten:- Wenn eine Anwendung ersetzt wird, muss sie mit demselben Schlüssel wie die alte Anwendung signiert werden, damit sie auf die Daten der alten Anwendung zugreifen kann. Das gilt sowohl für das Aktualisieren von Nutzer-Apps durch Überschreiben der
.apk
als auch für das Überschreiben einer System-App durch eine neuere Version, die unter/data
installiert ist. - Wenn zwei oder mehr Anwendungen eine Nutzer-ID teilen möchten (um z. B. Daten freigeben zu können), müssen sie mit demselben Schlüssel signiert sein.
- Wenn eine Anwendung ersetzt wird, muss sie mit demselben Schlüssel wie die alte Anwendung signiert werden, damit sie auf die Daten der alten Anwendung zugreifen kann. Das gilt sowohl für das Aktualisieren von Nutzer-Apps durch Überschreiben der
- OTA-Update-Pakete müssen mit einem der vom System erwarteten Schlüssel signiert sein, da sie andernfalls vom Installationsprozess abgelehnt werden.
Release-Schlüssel
Die Android-Struktur enthält test-keys unter build/target/product/security
. Wenn Sie ein Android-Betriebssystem-Image mit make
erstellen, werden alle .apk
-Dateien mit den Testschlüsseln signiert. Da die Testschlüssel öffentlich bekannt sind, kann jeder seine eigenen .apk-Dateien mit denselben Schlüsseln signieren. So können System-Apps, die in Ihrem Betriebssystem-Image eingebunden sind, ersetzt oder manipuliert werden. Aus diesem Grund ist es wichtig, jedes öffentlich veröffentlichte oder bereitgestellte Android-Betriebssystem-Image mit einer speziellen Reihe von Release-Schlüsseln zu signieren, auf die nur Sie Zugriff haben.
Wenn Sie Ihre eigenen Release-Schlüssel generieren möchten, führen Sie die folgenden Befehle aus dem Stammverzeichnis Ihres Android-Baums aus:
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
sollte entsprechend den Informationen Ihrer Organisation geändert werden. Sie können jedes beliebige Verzeichnis verwenden. Achten Sie aber darauf, einen gesicherten und sicheren Speicherort auszuwählen. Einige Anbieter verschlüsseln ihren privaten Schlüssel mit einer starken Passphrase und speichern den verschlüsselten Schlüssel in der Versionskontrolle. Andere speichern ihre Release-Schlüssel an einem ganz anderen Ort, z. B. auf einem Air-Gapped-Computer.
So generieren Sie ein Release-Image:
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
Das Skript sign_target_files_apks
verwendet eine .zip
-Zieldatei als Eingabe und erzeugt eine neue .zip
-Zieldatei, in der alle .apk
-Dateien mit neuen Schlüsseln signiert wurden. Die neu signierten Bilder finden Sie unter IMAGES/
in signed-target_files.zip
.
OTA-Pakete signieren
Eine signierte ZIP-Datei mit Zieldateien kann mithilfe des folgenden Verfahrens in eine signierte ZIP-Datei mit OTA-Update konvertiert werden:
ota_from_target_files \
-k (--package_key)
signed-target_files.zip \
signed-ota_update.zip
Signaturen und Sideloading
Beim Sideloading wird der normale Mechanismus zur Überprüfung der Paketsignatur der Wiederherstellung nicht umgangen. Vor der Installation eines Pakets wird geprüft, ob es mit einem der privaten Schlüssel signiert ist, die mit den in der Wiederherstellungspartition gespeicherten öffentlichen Schlüsseln übereinstimmen, genau wie bei einem per WLAN übertragenen Paket.
Vom Hauptsystem empfangene Updatepakete werden in der Regel zweimal überprüft: einmal vom Hauptsystem, mit der Methode RecoverySystem.verifyPackage()
in der Android API und dann noch einmal durch die Wiederherstellung. Die RecoverySystem API vergleicht die Signatur mit öffentlichen Schlüsseln, die standardmäßig in der Datei /system/etc/security/otacerts.zip
im Hauptsystem gespeichert sind. Bei der Wiederherstellung wird die Signatur mit öffentlichen Schlüsseln verglichen, die in der Datei /res/keys
auf der RAM-Disk der Wiederherstellungspartition gespeichert sind.
Standardmäßig wird in den vom Build erstellten Zieldateien .zip
das OTA-Zertifikat so festgelegt, dass es mit dem Testschlüssel übereinstimmt. Bei einem veröffentlichten Image muss ein anderes Zertifikat verwendet werden, damit Geräte die Authentizität des Update-Pakets überprüfen können. Wenn Sie das Flag -o
an sign_target_files_apks
übergeben, wie im vorherigen Abschnitt gezeigt, wird das Testschlüsselzertifikat durch das Releaseschlüsselzertifikat aus Ihrem Verzeichnis „certs“ ersetzt.
Normalerweise werden im System- und im Wiederherstellungs-Image dieselben öffentlichen OTA-Schlüssel gespeichert. Wenn Sie einen Schlüssel nur dem Schlüsselsatz für die Wiederherstellung hinzufügen, können Sie Pakete signieren, die nur per Sideloading installiert werden können, vorausgesetzt, der Update-Downloadmechanismus des Hauptsystems führt die Überprüfung korrekt anhand von otacerts.zip durch. Sie können zusätzliche Schlüssel angeben, die nur bei der Wiederherstellung enthalten sein sollen. Legen Sie dazu die Variable PRODUCT_EXTRA_RECOVERY_KEYS in Ihrer Produktdefinition fest:
vendor/yoyodyne/tardis/products/tardis.mk
[...] PRODUCT_EXTRA_RECOVERY_KEYS := vendor/yoyodyne/security/tardis/sideload
Dazu gehört der öffentliche Schlüssel vendor/yoyodyne/security/tardis/sideload.x509.pem
in der Datei mit den Wiederherstellungsschlüsseln, damit damit signierte Pakete installiert werden können. Der zusätzliche Schlüssel ist jedoch nicht in otacerts.zip enthalten. Systeme, die heruntergeladene Pakete korrekt überprüfen, rufen also keine Wiederherstellung für Pakete auf, die mit diesem Schlüssel signiert sind.
Zertifikate und private Schlüssel
Jeder Schlüssel wird in zwei Dateien geliefert: dem Zertifikat mit der Erweiterung „.x509.pem“ und dem privaten Schlüssel mit der Erweiterung „.pk8“. Der private Schlüssel sollte geheim gehalten werden und wird zum Signieren eines Pakets benötigt. Der Schlüssel kann selbst durch ein Passwort geschützt sein. Das Zertifikat enthält dagegen nur die öffentliche Hälfte des Schlüssels und kann daher weit verbreitet verteilt werden. Damit wird geprüft, ob ein Paket mit dem entsprechenden privaten Schlüssel signiert wurde.
Die Standard-Android-Builds verwenden fünf Schlüssel, die sich alle in
build/target/product/security
befinden:
- testkey
- Generischer Standardschlüssel für Pakete, für die kein anderer Schlüssel angegeben ist.
- Plattform
- Testschlüssel für Pakete, die Teil der Hauptplattform sind.
- geteilt
- Testschlüssel für Elemente, die im Prozess „Zuhause/Kontakte“ freigegeben werden.
- Medien
- Testschlüssel für Pakete, die Teil des Medien-/Downloadsystems sind.
Einzelne Pakete geben einen dieser Schlüssel an, indem sie LOCAL_CERTIFICATE in ihrer Android.mk-Datei festlegen. Wenn diese Variable nicht festgelegt ist, wird „testkey“ verwendet. Sie können auch einen völlig anderen Schlüssel über den Pfad angeben, z.B.:
device/yoyodyne/apps/SpecialApp/Android.mk
[...] LOCAL_CERTIFICATE := device/yoyodyne/security/special
Jetzt verwendet der Build den Schlüssel device/yoyodyne/security/special.{x509.pem,pk8}
zum Signieren von SpecialApp.apk. Für den Build können nur private Schlüssel verwendet werden, die nicht passwortgeschützt sind.
Erweiterte Signaturoptionen
Ersatz des APK-Signaturschlüssels
Das Signatur-Script sign_target_files_apks
arbeitet mit den Zieldateien, die für einen Build generiert wurden. Alle Informationen zu Zertifikaten und privaten Schlüsseln, die zum Zeitpunkt des Builds verwendet wurden, sind in den Zieldateien enthalten. Wenn du das Signaturskript zum Signieren für den Release ausführst, können Signaturschlüssel anhand des Schlüsselnamens oder APK-Namens ersetzt werden.
Verwenden Sie die Flags --key_mapping
und --default_key_mappings
, um den Schlüsselersatz anhand der Schlüsselnamen anzugeben:
- Das Flag
--key_mapping src_key=dest_key
gibt den Ersatz für jeweils einen Schlüssel an. - Das Flag
--default_key_mappings dir
gibt ein Verzeichnis mit fünf Schlüsseln an, um alle Schlüssel inbuild/target/product/security
zu ersetzen. Dies entspricht der fünfmaligen Verwendung von--key_mapping
zur Angabe der Zuordnungen.
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
Verwenden Sie das Flag --extra_apks apk_name1,apk_name2,...=key
, um die Signaturschlüsselersetzungen basierend auf APK-Namen anzugeben. Wenn key
leer bleibt, behandelt das Skript die angegebenen APKs als vorsigniert.
Für das hypothetische Produkt „Tardis“ benötigen Sie sechs passwortgeschützte Schlüssel: fünf, um die fünf in build/target/product/security
zu ersetzen, und einen, um den zusätzlichen Schlüssel device/yoyodyne/security/special
zu ersetzen, der im Beispiel oben von SpecialApp benötigt wird. Wenn sich die Schlüssel in den folgenden Dateien befanden:
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
Dann würden Sie alle Apps so signieren:
./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
Daraufhin wird Folgendes angezeigt:
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.
Nachdem der Nutzer zur Eingabe von Passwörtern für alle passwortgeschützten Schlüssel aufgefordert wurde, signiert das Skript alle APK-Dateien im Eingabeziel-.zip
mit den Releaseschlüsseln neu. Vor dem Ausführen des Befehls können Sie die Umgebungsvariable ANDROID_PW_FILE
auch auf einen temporären Dateinamen festlegen. Das Script ruft dann Ihren Editor auf, damit Sie Passwörter für alle Schlüssel eingeben können. Dies ist möglicherweise eine praktischere Möglichkeit, Passwörter einzugeben.
Ersatz des APEX-Signaturschlüssels
Mit Android 10 wird das APEX-Dateiformat für die Installation von Systemmodulen der unteren Ebene eingeführt. Wie unter APEX-Signatur erläutert, wird jede APEX-Datei mit zwei Schlüsseln signiert: einer für das Mini-Dateisystem-Image in einer APEX-Datei und der andere für die gesamte APEX-Datei.
Beim Signieren für den Release werden die beiden Signaturschlüssel für eine APEX-Datei durch Releaseschlüssel ersetzt. Der Nutzlastschlüssel des Dateisystems wird mit dem Flag --extra_apex_payload
angegeben und der gesamte APEX-Dateisignaturschlüssel wird mit dem Flag --extra_apks
angegeben.
Angenommen, Sie haben für das Tardis-Produkt die folgende Schlüsselkonfiguration für die APEX-Dateien com.android.conscrypt.apex
, com.android.media.apex
und 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"
Außerdem haben Sie die folgenden Dateien mit den Release-Schlüsseln:
vendor/yoyodyne/security/runtime_apex_container.x509.pem vendor/yoyodyne/security/runtime_apex_container.pk8 vendor/yoyodyne/security/runtime_apex_payload.pem
Mit dem folgenden Befehl werden die Signaturschlüssel für com.android.runtime.release.apex
und com.android.tzdata.apex
bei der Release-Signatur überschrieben. Insbesondere ist com.android.runtime.release.apex
mit den angegebenen Release-Schlüsseln signiert (runtime_apex_container
für die APEX-Datei und runtime_apex_payload
für die Datei-Image-Nutzlast).
com.android.tzdata.apex
wird als vorab signiert behandelt. Alle anderen APEX-Dateien werden gemäß der Standardkonfiguration verarbeitet, die in den Zieldateien aufgeführt ist.
./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
Die Ausführung des Befehls oben führt zu den folgenden Protokollen:
[...] 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) [...]
Sonstige Optionen
Das Signaturskript sign_target_files_apks
überschreibt die Build-Beschreibung und den Fingerabdruck in den Build-Eigenschaftsdateien, um anzugeben, dass es sich um einen signierten Build handelt. Das Flag --tag_changes
steuert, welche Änderungen am Fingerabdruck vorgenommen werden. Führen Sie das Script mit -h
aus, um die Dokumentation zu allen Flags aufzurufen.
Schlüssel manuell generieren
Android verwendet 2048-Bit-RSA-Schlüssel mit dem öffentlichen Exponenten 3. Sie können Zertifikats-/private Schlüsselpaare mit dem OpenSSL-Tool von openssl.org generieren:
# 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
Mit dem oben angegebenen openssl-pkcs8-Befehl wird eine PK8-Datei mit kein Passwort erstellt, die sich für die Verwendung mit dem Build-System eignet. Ersetzen Sie das Argument -nocrypt
durch -passout stdin
, um eine mit einem Passwort gesicherte PK8-Datei zu erstellen, was Sie für alle tatsächlichen Releaseschlüssel tun sollten .Anschließend verschlüsselt openssl den privaten Schlüssel mit einem Passwort, das aus der Standardeingabe gelesen wird. Es wird keine Eingabeaufforderung ausgegeben. Wenn also stdin das Terminal ist, scheint das Programm aufzuhängen, obwohl es nur auf die Eingabe eines Passworts wartet. Andere Werte können für das Argument „-passout“ verwendet werden, um das Passwort an anderen Stellen zu lesen. Weitere Informationen finden Sie in der
OpenSSL-Dokumentation.
Die Zwischendatei „temp.pem“ enthält den privaten Schlüssel ohne Passwortschutz. Entfernen Sie sie daher sorgfältig, wenn Sie Release-Schlüssel generieren. Insbesondere ist das GNUshred-Dienstprogramm möglicherweise nicht effektiv bei Netzwerk- oder protokollierten Dateisystemen. Sie können beim Generieren von Schlüsseln ein Arbeitsverzeichnis auf einem RAM-Laufwerk (z. B. einer tmpfs-Partition) verwenden, um zu verhindern, dass Zwischenschlüssel versehentlich freigelegt werden.
Bilddateien erstellen
Wenn Sie signed-target_files.zip
haben, müssen Sie das Image erstellen, damit Sie es auf ein Gerät übertragen können.
Führen Sie zum Erstellen des signierten Images aus den Zieldateien den folgenden Befehl im Stammverzeichnis des Android-Baums aus:
img_from_target_files signed-target_files.zip signed-img.zip
signed-img.zip
enthält alle .img
-Dateien.
So laden Sie ein Image auf ein Gerät mit Fastboot:
fastboot update signed-img.zip