Builds für die Veröffentlichung signieren

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

  1. Jede .apk-Datei im Image muss signiert sein. Der Android-Paketmanager 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. Das gilt sowohl für das Aktualisieren von Nutzer-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 Nutzer-ID gemeinsam nutzen möchten (damit sie Daten usw. gemeinsam nutzen können), müssen sie mit demselben Schlüssel signiert werden.
  2. OTA-Updatepakete müssen mit einem der vom System erwarteten Schlüssel signiert werden, da sie sonst vom Installationsprozess abgelehnt werden.

Release-Schlüssel

Der Android-Baum 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. Dadurch können System-Apps, die in Ihr Betriebssystem-Image integriert sind, ersetzt oder manipuliert werden. Aus diesem Grund ist es wichtig, dass Sie alle öffentlich veröffentlichten oder bereitgestellten Android-Betriebssystem-Images mit einer speziellen Gruppe von Release-Schlüsseln signieren, auf die nur Sie Zugriff haben.

Führen Sie die folgenden Befehle im Stammverzeichnis Ihres Android-Baums aus, um Ihre eigenen eindeutigen Releaseschlüssel zu generieren:

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 so geändert werden, dass die Informationen Ihrer Organisation enthalten sind. Sie können ein beliebiges Verzeichnis verwenden, sollten aber darauf achten, dass es gesichert und sicher ist. 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 Releaseschlüssel an einem ganz anderen Ort, z. B. auf einem Computer ohne Netzwerkverbindung.

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 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 Sideloading wird der normale Mechanismus zur Überprüfung der Paketsignatur des Recovery-Modus nicht umgangen. Vor der Installation eines Pakets wird im Recovery-Modus geprüft, ob es mit einem der privaten Schlüssel signiert ist, die den in der Recovery-Partition gespeicherten öffentlichen Schlüsseln entsprechen. Das ist genauso wie bei einem Paket, das Over-the-Air bereitgestellt wird.

Aktualisierungspakete, die vom Hauptsystem empfangen werden, werden in der Regel zweimal überprüft: einmal vom Hauptsystem mit der Methode RecoverySystem.verifyPackage() in der Android API und dann noch einmal von der Wiederherstellung. Die RecoverySystem API prüft die Signatur anhand von öffentlichen Schlüsseln, die im Hauptsystem in der Datei /system/etc/security/otacerts.zip (Standard) gespeichert sind. Bei der Wiederherstellung wird die Signatur anhand der öffentlichen Schlüssel geprüft, die in der RAM-Disk der Wiederherstellungspartition in der Datei /res/keys gespeichert sind.

Standardmäßig wird durch die vom Build erstellten Zieldateien .zip das OTA-Zertifikat so festgelegt, dass es mit dem Testschlüssel übereinstimmt. Für ein veröffentlichtes Image muss ein anderes Zertifikat verwendet werden, damit Geräte die Authentizität des Updatepakets ü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-Image und im Recovery-Image dieselben öffentlichen OTA-Schlüssel gespeichert. Wenn Sie dem nur für die Wiederherstellung vorgesehenen Schlüsselsatz einen Schlüssel hinzufügen, können Sie Pakete signieren, die nur per Sideloading installiert werden können. Das setzt voraus, dass der Update-Downloadmechanismus des Hauptsystems die Überprüfung anhand von „otacerts.zip“ korrekt durchführt. Sie können zusätzliche Schlüssel angeben, die nur in der Wiederherstellung enthalten sein 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 auch der öffentliche Schlüssel vendor/yoyodyne/security/tardis/sideload.x509.pem in der Datei mit den Wiederherstellungsschlüsseln, damit Pakete installiert werden können, die damit signiert sind. Der zusätzliche Schlüssel ist jedoch nicht in „otacerts.zip“ enthalten. Systeme, die heruntergeladene Pakete korrekt überprüfen, rufen die Wiederherstellung für Pakete, die mit diesem Schlüssel signiert sind, daher nicht auf.

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 ist zum Signieren eines Pakets erforderlich. Der Schlüssel selbst kann durch ein Passwort geschützt sein. Das Zertifikat enthält dagegen nur die öffentliche Hälfte des Schlüssels und kann daher weit verbreitet werden. Er wird verwendet, um zu überprüfen, ob ein Paket mit dem entsprechenden privaten Schlüssel signiert wurde.

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

testkey
Allgemeiner Standardschlüssel für Pakete, für die kein anderer Schlüssel angegeben ist.
Plattform
Testschlüssel für Pakete, die Teil der Kernplattform sind
freigegebenen
Testschlüssel für Elemente, die im Prozess „Zuhause/Kontakte“ geteilt werden.
Medien
Testsignatur für Pakete, die Teil des Media-/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 Modul-Updates separat erstellt und als Prebuilts in Ihr Geräte-Image eingebunden werden, müssen Sie möglicherweise keinen networkstack-Schlüssel im Android-Quellbaum generieren.

In einzelnen Paketen wird einer dieser Schlüssel durch Festlegen von LOCAL_CERTIFICATE in der Datei „Android.mk“ angegeben. („testkey“ wird verwendet, wenn diese Variable nicht festgelegt ist.) Sie können auch einen völlig anderen Schlüssel nach Pfadnamen angeben, z.B.:

device/yoyodyne/apps/SpecialApp/Android.mk
 [...]

LOCAL_CERTIFICATE := device/yoyodyne/security/special

Beim Build wird jetzt der device/yoyodyne/security/special.{x509.pem,pk8} -Schlüssel zum Signieren von „SpecialApp.apk“ verwendet. Für den Build können nur private Schlüssel verwendet werden, die nicht passwortgeschützt sind.

Erweiterte Signaturoptionen

Ersetzen des APK-Signaturschlüssels

Das Signierungsskript sign_target_files_apks wird für die Zieldateien ausgeführt, die für einen Build generiert werden. Alle Informationen zu Zertifikaten und privaten Schlüsseln, die zur Build-Zeit verwendet werden, sind in den Zieldateien enthalten. Wenn Sie das Signierungsskript ausführen, um die App für die Veröffentlichung zu signieren, können Signaturschlüssel anhand des Schlüsselnamens oder des APK-Namens ersetzt werden.

Verwenden Sie die Flags --key_mapping und --default_key_mappings, um den Schlüsseltausch anhand von 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, mit denen alle Schlüssel in build/target/product/security ersetzt werden. Es entspricht der fünfmaligen Verwendung von --key_mapping zum Angeben 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 Ersetzungen des Signaturschlüssels basierend auf APK-Namen anzugeben. Wenn key leer gelassen wird, behandelt das Skript die angegebenen APKs als vorab signiert.

Für das hypothetische Tardis-Produkt benötigen Sie sechs passwortgeschützte Schlüssel: fünf zum Ersetzen der fünf Schlüssel in build/target/product/security und einen zum Ersetzen des zusätzlichen Schlüssels device/yoyodyne/security/special, der im obigen Beispiel 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

Dadurch 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, werden alle APK-Dateien im Eingabeziel .zip mit den Releaseschlüsseln neu signiert. Bevor Sie den Befehl ausführen, können Sie auch die Umgebungsvariable ANDROID_PW_FILE auf einen temporären Dateinamen festlegen. Das Skript ruft dann Ihren Editor auf, damit Sie Passwörter für alle Schlüssel eingeben können.

APEX-Signaturschlüssel ersetzen

Mit Android 10 wird das APEX-Dateiformat für die Installation von Systemmodulen auf niedriger Ebene eingeführt. Wie unter APEX-Signierung beschrieben, wird jede APEX-Datei mit zwei Schlüsseln signiert: einem für das Mini-Dateisystem-Image in einem APEX und dem anderen für das gesamte APEX.

Beim Signieren für die Veröffentlichung werden die beiden Signaturschlüssel für eine APEX-Datei durch Veröffentlichungsschlüssel ersetzt. Der Nutzlastschlüssel für das Dateisystem wird mit dem Flag --extra_apex_payload angegeben und der gesamte APEX-Dateisignaturschlüssel mit dem Flag --extra_apks.

Nehmen Sie für das Produkt „Tardis“ an, 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 haben.

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"

Sie haben die folgenden Dateien mit den Releaseschlü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 während der Release-Signierung überschrieben. Insbesondere wird 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 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

Wenn Sie den obigen Befehl ausführen, erhalten Sie die folgenden 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 Signierungsskript 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 Skript mit -h aus, um die Dokumentation zu allen Flags aufzurufen.

Schlüssel manuell generieren

Unter Android werden 2048-Bit-RSA-Schlüssel mit dem öffentlichen Exponenten 3 verwendet. Sie können Zertifikat-/Privatschlü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 oben angegebenen openssl pkcs8-Befehl 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 tatsächlichen Releaseschlü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 kein Prompt ausgegeben. Wenn stdin das Terminal ist, scheint das Programm also zu hängen, obwohl es nur darauf wartet, dass Sie ein Passwort eingeben. Für das Argument „-passout“ können auch andere Werte verwendet werden, um das Passwort aus anderen Quellen zu lesen. Weitere Informationen finden Sie in der OpenSSL-Dokumentation.

Die temporäre Datei „temp.pem“ enthält den privaten Schlüssel ohne Passwortschutz. Sie sollten sie daher nach dem Generieren von Releaseschlüsseln sorgfältig entsorgen. Insbesondere ist das GNUshred-Dienstprogramm möglicherweise nicht für Netzwerk- oder Journaling-Dateisysteme geeignet. Sie können ein Arbeitsverzeichnis auf einer RAM-Disk (z. B. einer tmpfs-Partition) verwenden, wenn Sie Schlüssel generieren, um zu verhindern, dass die Zwischenschritte versehentlich 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 des Android-Baums 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 mit fastboot auf ein Gerät:
fastboot update signed-img.zip