Die Erstellung von Anwendungen ist eine Leidenschaft, die Softwareentwickler auf der ganzen Welt teilen. Aber der administrative Aufwand für die Verwaltung von Code Signing ist langweilig. Wie können wir es gleich beim ersten Mal richtig machen? Lewis Cianci geht der Sache auf den Grund.
Lassen Sie mich das gleich vorweg sagen.
Code Signing ist so langweilig, dass mir die Zähne weh tun. Es ist ein Konzept, das aus einem guten Grund existiert. Ich meine, Sie wollen doch, dass die Leute sicher sind, dass Ihr Softwarepaket tatsächlich von Ihnen stammt, oder?! Und doch ist es etwas, das so viele Entwickler tagtäglich nicht richtig hinbekommen. Es ist, als ob man nach einem ganzen Jahr Arbeit seine Steuererklärung macht und so viele Formulare ausfüllen muss. Yippee.
Scroll nach unten, wenn du nur die Schritt-für-Schritt-Anleitung zum Android Code Signing sehen willst und dich nicht dafür interessierst, warum wir das tun😉
Warum wir Code Signing betreiben
Wir signieren unsere Pakete, damit die Leute, die unser Paket aus dem Play Store herunterladen, auch wissen, dass wir es sind. Dazu signieren wir unser Paket mit einem Schlüssel, den wir selbst generieren. Wenn wir unser signiertes Paket zu Google Play hochladen, merkt es sich den Schlüssel, der zum Hochladen des ersten Pakets verwendet wurde, und stellt sicher, dass nachfolgende Pakete mit demselben Schlüssel signiert werden.
Um dieses Ziel zu erreichen, nutzt die Android-Paketsignierung ein Tool aus dem Java Development Framework namens keytool. Keytool gibt es wahrscheinlich schon so lange wie das JDK selbst, es ist also ziemlich alt. Dies ist wahrscheinlich einer der Gründe, warum das Signieren einer APK oder eines AAB (Android App Bundle) so verwirrend ist, wie es ist.
Warum kann der Play Store das Signieren des Codes nicht einfach für uns erledigen?
Wir wären versucht, nach einem Nirwana zu fragen, in dem wir all unsere unsignierten App-Bundles einfach dem Play Store übergeben könnten, damit sie es für uns ausarbeiten und signieren. Die Logik dieses Gedankens wird jedoch schnell durchbrochen. Wenn du ein Buch schreiben würdest, würdest du es von jemand anderem signieren lassen? Nein. Sie würden es signieren, weil Sie der Autor sind.
Heutzutage ist das Signieren von Code viel einfacher als früher. Solange wir unsere Pakete immer mit demselben Schlüssel (dem „Upload-Schlüssel“) signieren, generiert und verwaltet Google Play unsere Code Signing Keys für uns.
Wenn Sie besonders unternehmungslustig sind, können Sie versuchen, alles hier zu lesen und zu verstehen, aber ich entwickle seit fast drei Jahren für Android und muss leider sagen, dass selbst ich es nicht ganz verstehe. Alles, was ich weiß, ist, dass es eine große Qual ist, es zu reparieren, wenn es kaputt geht.
Lassen Sie uns die Zeit nehmen, nicht nur zu verstehen, wie man Zeichen codiert, sondern auch, warum wir Zeichen codieren. Wenn wir die Notwendigkeit dieses Prozesses verstehen, wird es einfacher, ihn zu vollenden.
Was brauchen wir für Code Signing?
Die kurze Version ist hier. Für das Code Signing müssen wir:
- die Java Development Kit (JDK)-Datei erstellen;
- unser App-Bundle oder APK mit unserem privaten Schlüssel signieren;
- die build.gradle modifizieren;
- das Paket an den Distributor (Google Play) senden.
Am Ende dieses Artikels finden Sie außerdem, wie Sie Code Signing mit Codemagic zum Laufen bringen.
Nun eine etwas längere Version mit einer Schritt-für-Schritt-Anleitung, was wir für das Android Code Signing brauchen und wie wir es machen.
Schritt-für-Schritt-Anleitung für Android Code Signing
SCHRITT 1: Das Java Development Kit (JDK)
Wenn Sie für Android entwickeln, haben Sie diese wahrscheinlich schon installiert.
Wir müssen eine Java Key Store (JKS) Datei erstellen, die unsere Signierinformationen enthält. Wenn wir eine JKS-Datei für unsere Anwendung erstellen, erzeugen wir eigentlich einen privaten Schlüssel auf unserem Computer. Dieser private Schlüssel ist durch ein von uns festgelegtes Kennwort geschützt.
An einer Eingabeaufforderung können wir Folgendes eingeben, um einen JKS zu erhalten.
keytool -genkey -v -keystore %DESKTOP%/key.jks -storetype JKS -keyalg RSA -keysize 2048 -validity 10000 -alias DEVELOPERNAME
Wir weisen keytool
an, einen Java Key Store zu generieren und ihn auf unserem Desktop abzulegen. Dieser Schlüssel ist 10.000 Tage oder etwa 27 Jahre lang gültig und ermöglicht es uns, während der gesamten Lebensdauer unserer Anwendung Aktualisierungen durchzuführen. Wir müssen auch einen Alias festlegen. Ich schreibe einfach meinen Entwicklernamen oder etwas, das ich mir merken kann.
keytool
wird im Laufe des Vorgangs verschiedene Informationen abfragen. Es ist wichtig, diese korrekt anzugeben, da wir im Wesentlichen die Details für unseren privaten Schlüssel definieren.
Sie werden nach folgendem gefragt:
- Keystore-Passwort – Sie benötigen dieses, um den Keystore in Zukunft wieder zu entsperren. Wenn Sie dieses Kennwort verlieren, ist es so gut wie unmöglich, es wiederzuerlangen.
- Keystore-Kennwort erneut eingeben
- Persönliche Angaben darüber, was in das persönliche Zertifikat aufgenommen werden soll
Wir werden aufgefordert, einige Angaben über uns zu machen. Dies sind die Angaben, die mit unserem privaten Schlüssel verknüpft sind, sie sollten also einigermaßen relevant sein. Es liegt an Ihnen, was Sie in diese Felder eingeben, aber als Faustregel würde ich es nicht zu verrückt machen.
Das ist die Ausgabe von keytool
.
C:\code\signingtest\android\app>keytool -genkey -v -keystore key.jks -storetype JKS -keyalg RSA -keysize 2048 -validity 10000 -alias androidappsEnter keystore password:Re-enter new password:What is your first and last name?: Codemagic Article DudeWhat is the name of your organizational unit?: Fantastic Apps And Where To Find ThemWhat is the name of your organization?: GreatappsWhat is the name of your City or Locality?: EstoniaWhat is the name of your State or Province?: TartuWhat is the two-letter country code for this unit?: EEIs CN=Codemagic Article Dude, OU=Fantastic Apps And Where To Find Them, O=Greatapps, L=Estonia, ST=Tartu, C=ES correct?:
Aufpassen! Wenn Sie während dieses Prozesses einfach die Eingabetaste drücken, wird die Erstellung immer wieder wiederholt, während Sie die letzte Frage mit „Nein“ beantworten.
Wir haben dabei einen JKS erstellt und unseren eigenen generierten privaten Schlüssel hineingesetzt. Da wir ihn generiert und das Kennwort festgelegt haben, können wir sicher sein, dass jeder, der diese JKS-Datei hat, entweder wir sind oder die ausdrückliche Erlaubnis hat, sie zu verwenden.
Wenn jemand Ihre JKS und die richtigen Anmeldeinformationen hat, kann er Pakete als Sie oder Ihr Unternehmen unterzeichnen. Bewahren Sie es sicher auf, legen Sie es nicht in die Versionskontrolle.
Jetzt haben wir unseren Java Key Store, also sind wir halbwegs fertig! Freuen Sie sich entsprechend.
SCHRITT 2: Signieren unseres App-Bündels oder APK mit unserem privaten Schlüssel
Nun wollen wir unser App-Bündel mit dem JKS signieren, den wir gerade erstellt haben. Es ist möglich, unsere APK oder unser Release-Build jedes Mal manuell zu signieren, aber in Wirklichkeit wäre es besser, es so zu konfigurieren, dass es unser Paket automatisch mit dem richtigen Upload-Schlüssel signiert, wenn wir flutter build apk --release
ausführen. Die Flutter-Dokumentation spricht darüber, wie man die Gradle-Dateien hier aktualisiert, aber wir werden es langsam durchgehen und es auf dem Weg erklären.
Um zu beginnen, öffnen wir unsere flutter_app/android/app/build.gradle
-Datei. Etwa in Zeile 49 sehen wir Folgendes:
buildTypes { release { // TODO: Add your own signing config for the release build. // Signing with the debug keys for now, so flutter run --release works. signingConfig signingConfigs.debug }}
Das Wichtigste, was hier passiert, ist, dass unsere Builds mit dem debug
Keystore signiert werden, so dass unser Release-Build immer noch funktioniert. Wir wollen dies ändern, so dass unsere Releases mit unserem eigenen Keystore signiert werden. Auf diese Weise können sie in den Google Play Store hochgeladen werden.
Als erstes erstellen wir einen key.properties
in unserem App-Verzeichnis. Dieses erstellen wir in flutter_app/android/key.properties
.
key.properties
enthält alle Details, die wir benötigen, um unser Paket erfolgreich zu signieren.
storePassword=The JKS store passwordkeyPassword=The key passwordkeyAlias=The alias for your keystoreFile=Where to look for your keystore file
Eine kurze Anmerkung zur Versionskontrolle
Du solltest nachdenken, bevor du diesen Code in die Versionskontrolle eincheckst. Wenn böswillige Akteure Zugriff auf den Keystore und diese Anmeldeinformationen erhalten und die Kontrolle über Ihre Konten haben, könnten sie möglicherweise ein neues Update für Ihre Anwendung mit Malware oder anderen schädlichen Dingen einspielen. Bei den meisten CI/CD-Lösungen können Sie diese Details als „Geheimnisse“ angeben, aber die Implementierung unterscheidet sich von Plattform zu Plattform.
STEP 3: Rekapitulation & Ändern der build.gradle
Wir haben eine Keystore-Datei erstellt und einen Alias sowie ein Passwort zum Schutz des Keystore angegeben. Wenn wir die App-Signierung von Google Play verwenden (was standardmäßig der Fall ist), dann dient der von uns generierte Schlüssel als Upload-Schlüssel. Das erste Paket, das wir über die Google Play-Konsole hochladen, wird mit diesem Schlüssel signiert. Das beweist Google, dass wir die sind, für die wir uns ausgeben.
Macht das Sinn? Cool, dann lassen wir es als Teil unseres Flutter-Build-Prozesses signieren.
Modifizieren Sie die build.gradle
Öffnen Sie flutter_app/android/app/build.gradle
. Ungefähr in Zeile 31 sollten Sie einen Text wie diesen sehen:
android { compileSdkVersion 29` lintOptions { disable 'InvalidPackage' }...
Wir wollen Gradle mitteilen, wo der Schlüsselspeicher zu finden ist. Das tun wir, indem wir diese Details etwa in Zeile 28, oberhalb der android {
-Anweisung, einfügen.
def keystoreProperties = new Properties()def keystorePropertiesFile = rootProject.file('key.properties')if (keystorePropertiesFile.exists()) { keystoreProperties.load(new FileInputStream(keystorePropertiesFile))}
Lassen Sie uns das Obige aufschlüsseln…
Wir definieren eine keystoreProperties
-Variable. Dann prüfen wir, ob key.properties
relativ zur Wurzel unseres Android-Projekts (nicht des Flutter-Projekts) existiert.
Wenn unser Build läuft, lädt es key.properties
. key.properties
identifiziert den Speicherort des Schlüsselspeichers sowie die erforderlichen Anmeldeinformationen, um den Java Key Store zum Signieren des Pakets zu entsperren. Mit allen erforderlichen Details in der Hand signiert Gradle nun das App-Bundle oder APK als Teil unseres Release-Builds.
Lassen Sie uns noch einmal überprüfen, dass sich alle unsere Dateien an der richtigen Stelle befinden.
Unser modifiziertes build.gradle
ist in flutter_app/android/app/build.gradle
.
Unsere key.jks
-Datei befindet sich in flutter_app/android/app/key.jks
.
Unsere key.properties
-Datei befindet sich in flutter_app/android/key.properties
.
Wenn wir uns über die oben genannten Punkte sicher sind, sollten wir jetzt in der Lage sein, flutter build apk --release
auszuführen, und das Signieren sollte problemlos funktionieren.
STEP 4: Senden an den Google Play Store
Jetzt können wir unsere APK oder unser App-Bundle in den Play Store hochladen. Wenn wir dies mit unserem signierten Paket tun und Google Play Signing aktiviert ist (was standardmäßig der Fall ist), erkennt Google den Schlüssel, den wir zum Signieren des Pakets verwendet haben, und speichert ihn als unseren Upload-Schlüssel. Google signiert dann unsere APK oder unser App-Paket mit seinem eigenen Schlüssel. Es ist wichtig, dass wir alle nachfolgenden Updates, die wir für diese App bereitstellen, mit demselben Schlüssel signieren. Google Play erkennt diesen Schlüssel als unseren Upload-Schlüssel und wir können keine Updates ohne ihn veröffentlichen.
Ich verstehe das alles nicht und wäre dankbar für eine visuelle Veranschaulichung, was genau passiert.
Kann ich machen! Das ist es, was passiert.
- Wir generieren eine supergeheime Art und Weise, uns zu identifizieren, fast so, als ob wir einen Pass für uns selbst machen würden.
- Da jeder, der diesen „Pass“ besitzt, sich eindeutig als wir identifizieren kann (d.h. uns ohne großen Widerstand nachahmen kann), schließen wir ihn hinter einem Passwort in unserem Tresor (dem JKS oder Java Key Store) ein.
- Wir erstellen das App-Bundle oder APK und signieren das Paket mit der gleichen Signatur, die wir für den Pass verwendet haben. Um auf diesen Pass zuzugreifen, müssen wir den Safe, in dem sich der Pass befindet, öffnen (indem wir dem Gradle-Build-Prozess das Passwort und den Alias mitteilen).
- Wir senden das Paket an den Distributor (Google Play). Der Distributor, der das Paket zum ersten Mal sieht, notiert sich unsere Signatur, die wir auf diesem Paket verwendet haben, und macht eine Kopie davon.
- Wenn wir in Zukunft Pakete an unseren Distributor (Google Play) senden, signieren wir diese Pakete mit denselben Details, die wir ursprünglich verwendet haben. Unser Verteiler, der sich an die Daten erinnert, die wir ursprünglich zum Hochladen des Pakets verwendet haben, akzeptiert das Paket oder lehnt es ab. Wenn sie übereinstimmen (wenn der Upload-Schlüssel derselbe ist wie der, den wir ursprünglich verwendet haben), wird das Paket angenommen und verteilt. Andernfalls wird es nicht akzeptiert.
- Unser Verteiler weiß, dass das ursprüngliche Paket und potenzielle künftige Pakete definitiv von uns stammen, und verteilt das Paket.
Making code signing work with Codemagic
Wir möchten dies letztendlich als Teil unseres CI/CD-Workflows signieren, aber gleichzeitig möchten wir unsere Keystore- und Eigenschaftsdatei nicht in die Versionskontrolle einchecken. Stattdessen wollen wir, dass unser CI/CD-Anbieter das Paket erstellt und es später im Erstellungsprozess mit einem von uns bereitgestellten Schlüsselspeicher signiert.
Einrichten mit Git
Wenn wir eine völlig neue Flutter-App haben, können wir in den Ordner wechseln und git init
eingeben, um die Quellcodekontrolle mit der App zu verwenden.
Standardmäßig checken wir einfach fröhlich unsere Keystore- und Keystore-Eigenschaften-Datei ein, was aus der Sicherheitsperspektive eine schlechte Idee ist.
Sie sollten das von Anfang an richtig machen
Wenn Sie versehentlich Ihre Keystore-Eigenschaften und Ihre Keystore-Datei einchecken und diese Änderungen pushen, können andere Leute diese Dateien jederzeit in der Zukunft herausnehmen, indem sie Ihren Git-Verlauf durchsehen. Sie können die Dateien in Zukunft manuell aus Git entfernen oder Ihr Repository ohne diese Dateien neu initialisieren, aber es ist besser, sie gar nicht erst einzuchecken.
Wir möchten diese Zeilen am Ende unserer .gitignore
-Datei hinzufügen:
# Don't check in the keystore files or equivalent*.jkskey*.properties
Kein Java KeyStore (JKS) oder Eigenschaften für die Codesignierung werden in die Versionskontrolle eingecheckt. Wie schön.
Build.gradle nicht signieren, wenn es unter CI/CD läuft
Während das Projekt gebaut wird, sind der Keystore und die Einstellungen nicht verfügbar. Wir wollen, dass der Build immer noch einen Release-Build erzeugt, auch wenn er nicht signiert ist.
Dies ist der Teil meines build.gradle
, der dies ermöglicht:
signingConfigs { file(rootProject.file('key.properties')).with { propFile -> if (propFile.canRead()) { release { keyAlias keystoreProperties keyPassword keystoreProperties storeFile file(keystoreProperties) storePassword keystoreProperties } } else { print('not signed') } }}buildTypes { release { file(rootProject.file('key.properties')).with { propFile -> if (propFile.canRead()) { // because we can read the keystore // we are building locally // so sign locally // otherwise build an unsigned apk for later signing by the CI/CD provider signingConfig signingConfigs.release } } applicationVariants.all { variant -> variant.outputs.all { output -> output.outputFileName = "app-release.apk" } } // TODO: Add your own signing config for the release build. // Signing with the debug keys for now, so `flutter run --release` works. // signingConfig signingConfigs.release }}
Einrichten von Codemagic, um unsere Builds zu signieren
In Ihrem Build-Prozess finden Sie den Abschnitt Android Code Signing (er befindet sich im Abschnitt Publish). Es sieht wie folgt aus:
Nun laden wir unseren Keystore hoch und setzen unser Passwort, den Schlüssel-Alias und das Schlüssel-Passwort (die die gleichen sind, wie die, die wir anfangs in unserer keystore.properties Datei gesetzt haben).
Dann klicken wir auf „Speichern“. Wenn Codmagic unseren Build-Prozess ausführt, wird es automatisch eine signierte APK oder ein App-Bundle für uns erstellen.
Und das war’s auch schon! Mit dieser signierten APK oder dem App-Bündel können Sie Ihre App im Play Store bereitstellen.
Sie können mein Git-Repositorium für ein Beispiel hier überprüfen (natürlich ohne den Keystore oder die Eigenschaften).
Das war’s.
Wenn Sie immer noch nicht weiter wissen, lassen Sie es mich unter @azimuthapps wissen und ich werde versuchen, Ihnen zu helfen. Es kann frustrierend sein, es richtig hinzubekommen, aber wenn man es einmal geschafft hat, sollte es in absehbarer Zeit funktionieren.
Lewis Cianci ist ein Softwareentwickler in Brisbane, Australien. Sein erster Computer hatte ein Bandlaufwerk. Er entwickelt seit mindestens zehn Jahren Software und hat in dieser Zeit eine ganze Reihe von mobilen Entwicklungsframeworks (wie Ionic und Xamarin Forms) verwendet. Nachdem er auf Flutter umgestiegen ist, gibt es für ihn kein Zurück mehr. Sie können ihn auf seinem Blog erreichen, auf Medium über andere Dinge lesen, die nichts mit Flutter zu tun haben, oder ihn vielleicht in Ihrem nächsten und ausgefallensten Café mit ihm und seiner lieben Frau treffen.
Weitere Artikel von Lewis: