Signieren von Builds zur Veröffentlichung

Android OS-Images verwenden kryptografische Signaturen an zwei Stellen:

  1. Jede .apk -Datei innerhalb des Bildes muss signiert sein. Der Paket-Manager 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 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 (um Daten auszutauschen usw.), müssen sie mit demselben Schlüssel signiert werden.
  2. OTA-Aktualisierungspakete müssen mit einem der vom System erwarteten Schlüssel signiert werden, sonst werden sie vom Installationsprozess abgelehnt.

Tasten loslassen

Der Android -Baum enthält Testschlüssel unter build/target/product/security . Beim Erstellen eines Android-Betriebssystemabbilds 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, was es ihm ermöglichen kann, in Ihr Betriebssystem-Image integrierte System-Apps zu ersetzen oder zu entführen. Aus diesem Grund ist es wichtig, jedes öffentlich freigegebene oder bereitgestellte Android-Betriebssystem-Image mit einem speziellen Satz von Freigabeschlüsseln zu signieren, auf die nur Sie Zugriff haben.

Um Ihren eigenen eindeutigen 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 ein beliebiges Verzeichnis verwenden, aber achten Sie 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 Quellcodeverwaltung. 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 nimmt eine .zip -Datei mit Zieldateien als Eingabe und erzeugt eine neue .zip -Datei mit Zieldateien, 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 von OTA-Paketen

Eine signierte ZIP-Datei mit Zieldateien kann mithilfe des folgenden Verfahrens 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

Sideloading umgeht nicht den normalen Verifizierungsmechanismus der Paketsignatur von Recovery – vor der Installation eines Pakets überprüft Recovery, ob es mit einem der privaten Schlüssel signiert ist, der mit den öffentlichen Schlüsseln übereinstimmt, die in der Wiederherstellungspartition gespeichert sind, genau wie bei einem Paket, das über das Internet bereitgestellt wird -Luft.

Update-Pakete, die vom Hauptsystem empfangen werden, werden normalerweise zweimal verifiziert: einmal durch das 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 (standardmäßig) gespeichert sind. Die Wiederherstellung prüft die Signatur anhand öffentlicher Schlüssel, die auf der RAM-Disk der Wiederherstellungspartition in der Datei /res/keys gespeichert sind.

Standardmäßig legt die vom Build erstellte .zip -Zieldatei das OTA-Zertifikat so fest, dass es mit dem Testschlüssel übereinstimmt. Auf einem freigegebenen Image muss ein anderes Zertifikat verwendet werden, damit Geräte die Authentizität des Updatepakets überprüfen können. Das Übergeben des Flags -o an sign_target_files_apks , wie im vorherigen Abschnitt gezeigt, ersetzt das Testschlüsselzertifikat durch das Freigabeschlüsselzertifikat aus Ihrem Verzeichnis certs .

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 per Querladen 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 eingeschlossen 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 besteht aus zwei Dateien: 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 benötigt, um ein Paket zu signieren. Der Schlüssel selbst kann durch ein Passwort geschützt sein. Das Zertifikat hingegen enthält nur die öffentliche Hälfte des Schlüssels, sodass es weit verbreitet werden kann. 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/Contacts-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 verwendet, um Binärdateien zu signieren, die als modulare Systemkomponenten entworfen wurden. Wenn Ihre Modul-Updates separat erstellt und als vorgefertigte Komponenten 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 ganz anderen Schlüssel per 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 Signieroptionen

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. Beim Ausführen des Signaturskripts zum Signieren für die Veröffentlichung können Signaturschlüssel basierend auf dem Schlüsselnamen oder dem APK-Namen ersetzt werden.

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

  • Das --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 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 --extra_apks apk_name1,apk_name2,... = key , um die Signaturschlüsselersetzungen basierend auf APK-Namen anzugeben. Wenn der key leer gelassen wird, behandelt das Skript die angegebenen APKs als vorsigniert.

Für das hypothetische tardis-Produkt benötigen Sie sechs passwortgeschützte Schlüssel: fünf zum Ersetzen der fünf in build/target/product/security und einen zum Ersetzen des zusätzlichen Schlüssels device/yoyodyne/security/special , der von SpecialApp im obigen Beispiel 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 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 in der .zip -Datei des Eingabeziels 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 Art, Passwörter einzugeben).

Ersetzen des APEX-Signaturschlüssels

Android 10 führt das APEX-Dateiformat für die Installation von untergeordneten Systemmodulen ein. Wie in APEX-Signierung erläutert, wird jede APEX-Datei mit zwei Schlüsseln signiert: einer für das Mini-Dateisystem-Image innerhalb eines APEX und der andere für das gesamte APEX.

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

Gehen Sie für das Produkt tardis davon aus, dass Sie 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"

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 wird com.android.runtime.release.apex mit den angegebenen Freigabeschlüsseln signiert ( runtime_apex_container für die APEX-Datei und runtime_apex_payload für die Datei-Image-Payload). com.android.tzdata.apex wird als vorsigniert behandelt. Alle anderen APEX-Dateien werden von der Standardkonfiguration behandelt, 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 die folgenden Protokolle:

        [...]
    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 sign_target_files_apks schreibt die Build-Beschreibung und den Fingerabdruck in den Build-Eigenschaftendateien neu, 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.

Manuelle Generierung von Schlüsseln

Android verwendet 2048-Bit-RSA-Schlüssel mit dem öffentlichen Exponenten 3. Sie können Paare aus Zertifikat und privatem Schlüssel mit dem Werkzeug openssl 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 .pk8-Datei zu erstellen, die mit einem Passwort gesichert ist (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 zu hängen, wenn es wirklich 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; Details finden Sie in der openssl-Dokumentation .

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

Bilddateien erstellen

Nachdem Sie die Datei „signed-target-files.zip“ erstellt haben, müssen Sie das Image 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 der Android-Struktur 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