Signieren Sie Builds zur Veröffentlichung

Android-Betriebssystem-Images verwenden an zwei Stellen kryptografische Signaturen:

  1. 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, um Zugriff auf die Daten der alten Anwendung zu erhalten. Dies gilt sowohl für die Aktualisierung von Benutzer-Apps durch Überschreiben der .apk als auch für das Überschreiben einer System-App mit einer neueren Version, die unter /data installiert ist.
    • Wenn zwei oder mehr Anwendungen eine Benutzer-ID teilen möchten (damit sie Daten teilen können usw.), müssen sie mit demselben Schlüssel signiert werden.
  2. OTA-Update-Pakete müssen mit einem der vom System erwarteten Schlüssel signiert sein, sonst werden sie vom Installationsprozess abgelehnt.

Tasten loslassen

Der Android-Baum enthält Testschlüssel unter build/target/product/security . Beim Erstellen eines Android-Betriebssystem-Images mit make 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 und so möglicherweise in Ihr Betriebssystem-Image integrierte System-Apps ersetzen oder kapern. Aus diesem Grund ist es wichtig, jedes öffentlich veröffentlichte oder bereitgestellte Android-Betriebssystem-Image mit einem speziellen Satz von Freigabeschlüsseln zu signieren, auf die nur Sie Zugriff haben.

Um Ihren eigenen, einzigartigen Satz von Freigabeschlüsseln zu generieren, führen Sie diese Befehle im 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 geändert werden, um die Informationen Ihrer Organisation widerzuspiegeln. Sie können jedes beliebige Verzeichnis verwenden, achten Sie jedoch darauf, einen gesicherten und sicheren Speicherort auszuwählen. Einige Anbieter entscheiden sich dafür, ihren privaten Schlüssel mit einer starken Passphrase zu verschlüsseln und den verschlüsselten Schlüssel in der Quellcodeverwaltung zu speichern. andere speichern ihre Freigabeschlüssel ganz woanders, beispielsweise auf einem Air-Gap-Computer.

Um ein Release-Image zu generieren, verwenden Sie:

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-Datei .zip als Eingabe und erstellt eine neue ZIP-Datei .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 .

Signieren Sie OTA-Pakete

Eine signierte Zieldatei-ZIP-Datei kann mit dem folgenden Verfahren in eine signierte OTA-Update-ZIP-Datei konvertiert werden:
ota_from_target_files \
-k  (--package_key) 
signed-target_files.zip \
signed-ota_update.zip

Signaturen und Sideloading

Beim Querladen wird der normale Paketsignaturüberprüfungsmechanismus der Wiederherstellung nicht umgangen. Vor der Installation eines Pakets überprüft die Wiederherstellung, 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 Paket, das über die Wiederherstellungspartition geliefert wird -Luft.

Vom Hauptsystem empfangene Aktualisierungspakete werden in der Regel zweimal überprüft: einmal vom Hauptsystem mithilfe der RecoverySystem.verifyPackage() -Methode in der Android-API und dann noch einmal durch die Wiederherstellung. Die RecoverySystem-API prüft die Signatur anhand öffentlicher Schlüssel, die im Hauptsystem in der Datei /system/etc/security/otacerts.zip gespeichert sind (standardmäßig). Die Wiederherstellung vergleicht die Signatur mit öffentlichen Schlüsseln, die auf der RAM-Disk der Wiederherstellungspartition in der Datei /res/keys gespeichert sind.

Standardmäßig wird durch die vom Build erstellten .zip -Zieldateien das OTA-Zertifikat so eingestellt, dass es mit dem Testschlüssel übereinstimmt. Auf einem freigegebenen 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 speichern das System-Image und das Wiederherstellungs-Image denselben Satz öffentlicher OTA-Schlüssel. Durch das Hinzufügen eines Schlüssels nur zum Wiederherstellungsschlüsselsatz ist es möglich, Pakete zu signieren, die nur über Seitenladen installiert werden können (vorausgesetzt, der Update-Download-Mechanismus des Hauptsystems führt die Überprüfung anhand von otacerts.zip korrekt durch). Sie können zusätzliche Schlüssel angeben, die nur in die Wiederherstellung einbezogen werden sollen, indem Sie die Variable PRODUCT_EXTRA_RECOVERY_KEYS in Ihrer Produktdefinition festlegen:

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 Wiederherstellungsschlüsseldatei, damit damit signierte Pakete installiert werden können. Der zusätzliche Schlüssel ist jedoch nicht in otacerts.zip enthalten, sodass Systeme, die heruntergeladene Pakete korrekt überprüfen, keine Wiederherstellung für mit diesem Schlüssel signierte Pakete aufrufen.

Zertifikate und private Schlüssel

Jeder Schlüssel liegt in zwei Dateien vor: 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 selbst kann durch ein Passwort geschützt sein. Das Zertifikat hingegen enthält nur die öffentliche Hälfte des Schlüssels und kann daher weit verbreitet werden. Es wird verwendet, um zu überprüfen, ob ein Paket mit dem entsprechenden privaten Schlüssel signiert wurde.

Der Standard-Android-Build verwendet fünf Schlüssel, die sich alle in build/target/product/security befinden:

Testschlüssel
Allgemeiner Standardschlüssel für Pakete, die ansonsten keinen Schlüssel angeben.
Plattform
Testschlüssel für Pakete, die Teil der Kernplattform sind.
geteilt
Testschlüssel für Dinge, die im Home-/Kontakte-Prozess geteilt werden.
Medien
Testschlüssel für Pakete, die Teil des Medien-/Downloadsystems sind.
Netzwerkstack
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 Modulaktualisierungen separat erstellt und als vorgefertigte Elemente in Ihr Geräte-Image integriert werden, müssen Sie möglicherweise keinen Networkstack-Schlüssel im Android-Quellbaum generieren.

Einzelne Pakete geben einen dieser Schlüssel an, indem sie LOCAL_CERTIFICATE in ihrer Android.mk-Datei festlegen. (testkey wird verwendet, wenn diese Variable nicht gesetzt ist.) Sie können auch einen völlig anderen Schlüssel über den Pfadnamen 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} um SpecialApp.apk zu signieren. Der Build kann nur private Schlüssel verwenden, die nicht passwortgeschützt sind.

Erweiterte Signierungsoptionen

Ersetzen des APK-Signaturschlüssels

Das Signaturskript sign_target_files_apks arbeitet mit den für einen Build generierten Zieldateien. Alle Informationen zu Zertifikaten und privaten Schlüsseln, die zur Erstellungszeit verwendet werden, sind in den Zieldateien enthalten. Wenn Sie das Signaturskript zum Signieren zur Freigabe ausführen, können Signaturschlüssel basierend auf dem Schlüsselnamen oder dem 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 die Ersetzung für jeweils einen Schlüssel an.
  • Das --default_key_mappings dir gibt ein Verzeichnis mit fünf Schlüsseln an, um alle Schlüssel in build/target/product/security zu ersetzen; Dies entspricht der fünfmaligen Verwendung --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 --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 Tardis-Produkt benötigen Sie sechs passwortgeschützte Schlüssel: fünf als Ersatz für die fünf in build/target/product/security und einen als Ersatz für den zusätzlichen Schlüssel device/yoyodyne/security/special von SpecialApp im obigen Beispiel benötigt wird. Wenn die Schlüssel in den folgenden Dateien wären:

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 wie folgt 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

Dies bringt Folgendes hervor:

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 Benutzer zur Eingabe von Passwörtern für alle passwortgeschützten Schlüssel aufgefordert wurde, signiert das Skript alle APK-Dateien im Eingabeziel .zip erneut mit den Freigabeschlüsseln. Bevor Sie den Befehl ausführen, können Sie auch die Umgebungsvariable ANDROID_PW_FILE auf einen temporären Dateinamen setzen; Das Skript ruft dann Ihren Editor auf, damit Sie Passwörter für alle Schlüssel eingeben können (dies ist möglicherweise eine bequemere Möglichkeit, Passwörter einzugeben).

Austausch des APEX-Signaturschlüssels

Android 10 führt das APEX-Dateiformat für die Installation untergeordneter Systemmodule ein. Wie unter APEX-Signierung erläutert, wird jede APEX-Datei mit zwei Schlüsseln signiert: einem für das Mini-Dateisystem-Image innerhalb eines APEX und dem anderen für das gesamte APEX.

Beim Signieren zur Freigabe werden die beiden Signaturschlüssel für eine APEX-Datei durch Freigabeschlüssel ersetzt. Der Dateisystem-Payload-Schlüssel wird mit dem Flag --extra_apex_payload angegeben und der gesamte APEX-Dateisignaturschlüssel wird mit dem Flag --extra_apks angegeben.

Gehen Sie für das Tardis-Produkt davon aus, dass Sie über die folgende Schlüsselkonfiguration für die APEX-Dateien com.android.conscrypt.apex , com.android.media.apex und com.android.runtime.release.apex verfügen.

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"

Und Sie haben die folgenden Dateien, die die Freigabeschlüssel enthalten:

vendor/yoyodyne/security/runtime_apex_container.x509.pem
vendor/yoyodyne/security/runtime_apex_container.pk8
vendor/yoyodyne/security/runtime_apex_payload.pem

Der folgende Befehl überschreibt die Signaturschlüssel für com.android.runtime.release.apex und com.android.tzdata.apex während der Release-Signierung. 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 vorsigniert 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, werden die folgenden Protokolle angezeigt:

        [...]
    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)
        [...]

Andere Optionen

Das Signaturskript sign_target_files_apks schreibt die Build-Beschreibung und den Fingerabdruck in den Build-Eigenschaftendateien um, um anzuzeigen, dass es sich bei dem Build um einen signierten Build handelt. Das Flag --tag_changes steuert, welche Änderungen am Fingerabdruck vorgenommen werden. Führen Sie das Skript mit -h aus, um die Dokumentation zu allen Flags anzuzeigen.

Schlüssel manuell generieren

Android verwendet 2048-Bit-RSA-Schlüssel mit öffentlichem Exponenten 3. Sie können Zertifikat/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

Der oben angegebene Befehl openssl pkcs8 erstellt eine .pk8-Datei ohne Passwort, die für die Verwendung mit dem Build-System geeignet ist. Um eine mit einem Passwort gesicherte .pk8-Datei zu erstellen (was Sie für alle tatsächlichen Freigabeschlüssel tun sollten), ersetzen Sie das Argument -nocrypt durch -passout stdin ; Dann 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 hängen zu bleiben, obwohl es eigentlich nur darauf wartet, dass Sie ein Passwort eingeben. Andere Werte können für das Argument „passout“ verwendet werden, um das Passwort von anderen Orten zu lesen; Einzelheiten finden Sie in der OpenSSL-Dokumentation .

Die Zwischendatei temp.pem enthält den privaten Schlüssel ohne jeglichen Passwortschutz. Entsorgen Sie ihn daher sorgfältig, wenn Sie Freigabeschlüssel generieren. Insbesondere ist das Dienstprogramm GNUshred möglicherweise nicht auf Netzwerk- oder Journaldateisystemen wirksam. Sie können beim Generieren von Schlüsseln ein Arbeitsverzeichnis auf einer RAM-Disk (z. B. einer tmpfs-Partition) verwenden, um sicherzustellen, dass die Zwischenprodukte nicht versehentlich offengelegt werden.

Bilddateien erstellen

Wenn Sie signed-target_files.zip haben, müssen Sie das Bild erstellen, damit Sie es auf ein Gerät übertragen können. Um das signierte Image aus den Zieldateien zu erstellen, führen Sie den folgenden Befehl im Stammverzeichnis des Android-Baums aus:

img_from_target_files signed-target_files.zip signed-img.zip
Die resultierende Datei, signed-img.zip , enthält alle .img Dateien. Um ein Image auf ein Gerät zu laden, verwenden Sie Fastboot wie folgt:
fastboot update signed-img.zip