Builds für den Release signieren

In Android OS-Images werden kryptografische Signaturen an zwei Stellen verwendet:

  1. Jede .apk-Datei im Image 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, um Zugriff auf die Daten der alten Anwendung zu erhalten. Dies gilt sowohl für das Aktualisieren von Nutzeranwendungen durch Überschreiben von .apk als auch für das Überschreiben einer Systemanwendung mit einer neueren Version, die unter /data installiert ist.
    • Wenn zwei oder mehr Anwendungen eine Nutzer-ID gemeinsam nutzen möchten (damit sie z. B. Daten teilen können), müssen sie mit demselben Schlüssel signiert sein.
  2. 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

Der Android-Baum enthält unter build/target/product/security Testschlüssel. 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. Dadurch können System-Apps, die in Ihr Betriebssystem-Image integriert sind, möglicherweise ersetzt oder missbraucht 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 Verzeichnis verwenden, achten Sie aber darauf, einen gesicherten 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 generierst du 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 sign_target_files_apks-Script nimmt eine Zieldatei.zip als Eingabe und erstellt eine neue Zieldatei.zip, 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.

Update-Pakete, die vom Hauptsystem empfangen werden, werden in der Regel zweimal überprüft: einmal vom Hauptsystem mithilfe der RecoverySystem.verifyPackage()-Methode in der Android API und dann noch einmal vom Wiederherstellungssystem. Die RecoverySystem API prüft die Signatur anhand der öffentlichen Schlüssel, die im Hauptsystem in der Datei /system/etc/security/otacerts.zip (standardmäßig) 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 legen die vom Build erstellten Zieldateien .zip das OTA-Zertifikat so fest, 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 Release-Schlüsselzertifikat aus Ihrem certs-Verzeichnis 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 berücksichtigt werden sollen. Dazu legen Sie 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. Daher wird bei Systemen, die heruntergeladene Pakete korrekt überprüfen, keine Wiederherstellung für Pakete aufgerufen, die mit diesem Schlüssel signiert sind.

Zertifikate und private Schlüssel

Jeder Schlüssel besteht aus zwei Dateien: dem Zertifikat mit der Endung .x509.pem und dem privaten Schlüssel mit der Endung .pk8. Der private Schlüssel sollte geheim gehalten werden und ist zum Signieren eines Pakets erforderlich. 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, die keinen Schlüssel angeben.
Plattform
Testschlüssel für Pakete, die zur Kernplattform gehören.
geteilt
Testschlüssel für Dinge, die im Zuhause/Kontakt-Prozess geteilt wurden
Medien
Testschlüssel für Pakete, die Teil des Medien-/Downloadsystems sind.
networkstack
Testschlüssel für Pakete, die Teil des Netzwerksystems sind. Der Networkstack-Schlüssel wird zum Signieren von Binärdateien verwendet, die als modulare Systemkomponenten konzipiert sind. Wenn Ihre Modulupdates separat erstellt und als vordefinierte Elemente in Ihr Geräte-Image eingebunden sind, müssen Sie möglicherweise keinen Networkstack-Schlüssel in der Android-Quellstruktur generieren.

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 Sie das Signaturskript zum Signieren für die Veröffentlichung ausführen, können Signaturschlüssel basierend auf dem Schlüsselnamen oder APK-Namen ersetzt werden.

Verwenden Sie die Flags --key_mapping und --default_key_mappings, um die Schlüsselersetzung basierend auf Schlüsselnamen anzugeben:

  • Das Flag --key_mapping src_key=dest_key gibt den Ersatz für einen einzelnen Schlüssel an.
  • Das Flag --default_key_mappings dir gibt ein Verzeichnis mit fünf Schlüsseln an, um alle Schlüssel in build/target/product/security zu ersetzen. Das entspricht der fünfmaligen Verwendung von --key_mapping, um die Zuordnungen anzugeben.
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, werden die angegebenen APKs vom Script als vorab signiert behandelt.

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

Anschließend signieren Sie alle Apps so:

./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. Bevor Sie den Befehl ausführen, können Sie für die Umgebungsvariable ANDROID_PW_FILE auch einen temporären Dateinamen festlegen. Das Skript ruft dann den Editor auf, damit Sie Passwörter für alle Schlüssel eingeben können. So lassen sich Passwörter einfacher eingeben.

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: einem für das Minidateisystem-Image in einem APEX und einem für das gesamte APEX.

Beim Signieren für die Veröffentlichung werden die beiden Signaturschlüssel für eine APEX-Datei durch Release-Schlüssel ersetzt. Der Schlüssel für die Dateisystemnutzlast wird mit dem Flag --extra_apex_payload und der Schlüssel für die gesamte APEX-Dateisignatur 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 Releaseschlüsseln signiert (runtime_apex_container für die APEX-Datei und runtime_apex_payload für die Nutzlast des Dateibilds). com.android.tzdata.apex wird als vorab signiert behandelt. Alle anderen APEX-Dateien werden von 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

Wenn Sie den obigen Befehl ausführen, erhalten Sie folgende Logs:

        [...]
    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 schreibt die Build-Beschreibung und den Fingerabdruck in den Build-Attributdateien neu, um anzugeben, dass der Build ein signierter Build ist. 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 key
openssl 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 key
openssl 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 key
openssl pkcs8 -in temp.pem -topk8 -outform DER -out releasekey.pk8 -nocrypt

# securely delete the temp.pem file
shred --remove temp.pem

Mit dem obigen Befehl „openssl pkcs8“ wird eine .pk8-Datei ohne Passwort erstellt, die für die Verwendung mit dem Build-System geeignet ist. Wenn Sie eine mit einem Passwort gesicherte .pk8-Datei erstellen möchten (was Sie für alle Release-Schlüssel tun sollten), ersetzen Sie das Argument -nocrypt durch -passout stdin. openssl verschlüsselt dann den privaten Schlüssel mit einem Passwort, das aus der Standardeingabe gelesen wird. Es wird kein Prompt ausgegeben. Wenn stdin also das Terminal ist, scheint das Programm hängenzubleiben, obwohl es nur auf die Eingabe eines Passworts wartet. Für das Argument "passout" können andere Werte verwendet werden, um das Passwort aus anderen Speicherorten auszulesen. Weitere Informationen finden Sie in der Dokumentation zu openssl.

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 einer RAM-Disk (z. B. eine tmpfs-Partition) verwenden, damit die Zwischendateien nicht unbeabsichtigt offengelegt 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 den folgenden Befehl im Stammverzeichnis der Android-Struktur aus, um das signierte Image aus den Zieldateien zu erstellen:

img_from_target_files signed-target_files.zip signed-img.zip
Die resultierende Datei signed-img.zip enthält alle .img-Dateien. So laden Sie ein Image auf ein Gerät:
fastboot update signed-img.zip