Het bouwen van applicaties is een passie die wordt gedeeld door softwareontwikkelaars over de hele wereld. Maar de administratieve rompslomp van het beheer van code-ondertekening is saai. Hoe kunnen we het in één keer goed doen? Lewis Cianci gaat op onderzoek uit.
Laat me dit vooraf even zeggen.
Code signing is zo saai dat ik er tandpijn van krijg. Het is een concept dat bestaat met een goede reden. Ik bedoel, je wilt toch dat mensen er zeker van zijn dat je softwarepakket ook echt van jou komt, toch?! En toch is het iets waar zoveel ontwikkelaars dagelijks mee worstelen om het goed te doen. Het is alsof je je belastingen doet na een heel jaar werken en zoveel formulieren in te vullen hebt. Yippee.
Schuif naar beneden als u alleen de stap-voor-stap handleiding over Android-code ondertekenen wilt zien en niet geïnteresseerd bent in waarom we dit doen😉
Waarom we code ondertekenen
We ondertekenen onze pakketten zodat mensen die ons pakket downloaden van de Play Store ook echt weten dat wij het zijn. We doen dit door ons pakket te ondertekenen met een sleutel die we genereren. Wanneer we ons ondertekende pakket uploaden naar Google Play, onthoudt het de sleutel die werd gebruikt om het eerste pakket te uploaden en zorgt ervoor dat volgende pakketten worden ondertekend met dezelfde sleutel.
Om dit doel te bereiken, maakt Android package signing eigenlijk gebruik van een tool die afkomstig is van het Java Development Framework genaamd keytool. Keytool bestaat waarschijnlijk al net zo lang als de JDK zelf, dus het is vrij oud. Dit leent zich waarschijnlijk voor een aantal van de redenen waarom het ondertekenen van een APK of AAB (android app bundel) zo verwarrend is als het is.
Waarom kan de Play Store niet gewoon de code ondertekenen voor ons afhandelen?
We zijn geneigd om te vragen om een nirvana waar we gewoon al onze niet-ondertekende app bundels aan de Play Store kunnen geven en ze het gewoon kunnen laten uitwerken en het gewoon voor ons ondertekenen. De logica daarvan gaat echter snel verloren. Als jij een boek zou schrijven, zou je het dan door iemand anders laten signeren? Nee. Jij zou het ondertekenen omdat jij de auteur bent.
Het ondertekenen van code is tegenwoordig een stuk eenvoudiger dan vroeger. Zolang we onze pakketten altijd met dezelfde sleutel ondertekenen (de “uploadsleutel”), zal Google Play in feite onze codesigning-sleutels voor ons genereren en beheren.
Als je bijzonder ondernemend bent, kun je proberen alles hier te lezen en te begrijpen, maar ik ontwikkel nu al het grootste deel van drie jaar voor Android en ik moet helaas zeggen dat zelfs ik het niet helemaal begrijp. Alles wat ik weet is dat wanneer het breekt, het een enorme pijn is om te repareren.
Laten we de tijd nemen om niet alleen te begrijpen hoe we coderen tekenen, maar ook waarom we coderen tekenen. Als we de noodzaak van dit proces begrijpen, zal het gemakkelijker te voltooien zijn.
Wat hebben we nodig voor code signing?
De korte versie is hier. Voor code signing moeten we:
- het Java Development Kit (JDK) bestand maken;
- onze app bundel of APK ondertekenen met onze private key;
- het build.gradle aanpassen;
- pakket naar de distributeur (Google Play) sturen.
In het einde van dit artikel vindt u ook hoe u code signing kunt laten werken met Codemagic.
Nu een wat langere versie met stap-voor-stap handleiding over wat we nodig hebben voor Android code signing en hoe we dat moeten doen.
Stap-voor-stap handleiding voor Android code signing
STAP 1: De Java Development Kit (JDK)
Als je voor Android ontwikkelt, heb je deze waarschijnlijk al geïnstalleerd.
We moeten een Java Key Store (JKS) bestand maken dat onze signing informatie bevat. Bij het genereren van een JKS voor onze app, maken we eigenlijk een private sleutel op onze computer. Deze private sleutel wordt beschermd door een wachtwoord dat we instellen.
Vanaf een commando prompt, kunnen we het volgende typen om een JKS.
keytool -genkey -v -keystore %DESKTOP%/key.jks -storetype JKS -keyalg RSA -keysize 2048 -validity 10000 -alias DEVELOPERNAME
We vertellen keytool
om een Java Key Store te genereren en deze op ons bureaublad te zetten. Deze sleutel is 10.000 dagen of ongeveer 27 jaar geldig, zodat we updates kunnen pushen voor de levensduur van onze app. We zijn ook verplicht om een alias in te stellen. Ik maak hier gewoon mijn ontwikkelaarsnaam van of iets dat ik zal onthouden.
keytool
zal onderweg om verschillende stukjes informatie vragen. Het is belangrijk om deze correct op te geven, omdat we in wezen de details voor onze private sleutel definiëren.
U wordt gevraagd om:
- Wachtwoord voor de sleutelbewaarplaats – u hebt dit nodig om deze sleutelbewaarplaats in de toekomst opnieuw te ontgrendelen. Als u dit wachtwoord verliest, is het vrijwel onmogelijk om het te herstellen.
- Wachtwoord voor de sleutelbewaarplaats opnieuw invoeren
- Persoonlijke details over wat in het persoonlijke certificaat moet
We zullen gevraagd worden om enkele details over ons in te vullen. Dit zijn de gegevens die bij onze private sleutel horen, dus ze zouden enigszins relevant moeten zijn. Het is aan jou wat je in deze velden zet, maar als vuistregel zou ik het niet te gek maken.
Dit is de output van 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?:
Let op! Als je gewoon ‘Enter’ spamt tijdens dit proces, wordt de creatie steeds opnieuw herhaald terwijl je ‘nee’ antwoordt op de laatste vraag.
Door dit te doen, hebben we een JKS gemaakt en hebben we onze eigen gegenereerde privésleutel erin gestopt. Omdat we die hebben gegenereerd en het wachtwoord hebben ingesteld, kunnen we er zeker van zijn dat iedereen die dit JKS-bestand heeft, wij zijn of specifiek toestemming heeft om het te gebruiken.
Als iemand uw JKS en de juiste referenties heeft, kan hij pakketten ondertekenen als u of uw bedrijf. Houd het veilig, zet het niet op source control.
Nu hebben we onze Java Key Store, dus we zijn halverwege! Verheug u dienovereenkomstig.
STAP 2: Onze app-bundel of APK ondertekenen met onze privésleutel
Nu willen we onze app-bundel ondertekenen met die JKS die we zojuist hebben gemaakt. Het is mogelijk om onze APK of release build elke keer handmatig te coderen, maar in werkelijkheid kunnen we het beter zo configureren dat wanneer we flutter build apk --release
uitvoeren, het gewoon automatisch ons pakket ondertekent met de juiste uploadsleutel. De Flutter documentatie praat over hoe de Gradle bestanden hier te updaten, maar we zullen er langzaam doorheen gaan en het onderweg uitleggen.
Om te beginnen, laten we ons flutter_app/android/app/build.gradle
bestand openen. Op ongeveer regel 49 zien we dit:
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 }}
Het belangrijkste dat hier gebeurt is dat onze builds worden ondertekend met de debug
sleutelbewaarplaats, zodat onze release build nog steeds werkt. We willen dit veranderen zodat onze releases worden ondertekend met onze eigen sleutelbewaarplaats. Op die manier kunnen ze worden geupload naar de Google Play store.
Het eerste wat we doen is het aanmaken van een key.properties
in onze app directory. Deze maken we aan in flutter_app/android/key.properties
.
key.properties
bevat alle gegevens die we nodig hebben om ons pakket met succes te ondertekenen.
storePassword=The JKS store passwordkeyPassword=The key passwordkeyAlias=The alias for your keystoreFile=Where to look for your keystore file
Een korte opmerking over bronbeheer
U moet nadenken voordat u deze code in bronbeheer plaatst. Als kwaadwillenden toegang krijgen tot de sleutelbewaarplaats en deze referenties, en ze hebben controle over uw accounts, kunnen ze mogelijk een nieuwe update naar uw app pushen met malware of andere slechte dingen. Bij de meeste CI/CD-oplossingen kun je deze gegevens als “secrets” opgeven, maar de implementatie verschilt per platform.
STAP 3: Recap &Wijziging van build.gradle
We hebben een keystore-bestand gemaakt en een alias opgegeven, evenals een wachtwoord om de keystore te beschermen. Als we Google Play app signing gebruiken (wat je standaard gebruikt), dan fungeert de sleutel die we hebben gegenereerd als onze upload sleutel. Het eerste pakket dat we uploaden via de Google Play console zal worden ondertekend met deze sleutel. Dit bewijst aan Google dat we zijn wie we zeggen dat we zijn.
Blijkt dat logisch? Cool, laten we het ondertekenen als onderdeel van ons Flutter bouwproces.
Modify the build.gradle
Openen flutter_app/android/app/build.gradle
. Op regel 31 of zo zou u tekst als deze moeten zien:
android { compileSdkVersion 29` lintOptions { disable 'InvalidPackage' }...
We willen Gradle vertellen waar het sleutelhuis te vinden is. Dat doen we door deze details op ongeveer regel 28 te zetten, boven het android {
statement.
def keystoreProperties = new Properties()def keystorePropertiesFile = rootProject.file('key.properties')if (keystorePropertiesFile.exists()) { keystoreProperties.load(new FileInputStream(keystorePropertiesFile))}
Laten we het bovenstaande eens uit elkaar halen…
We definiëren een keystoreProperties
variabele. Vervolgens controleren we of key.properties
bestaat relatief aan de root van ons android project (niet het Flutter project).
Wanneer onze build wordt uitgevoerd, laadt het key.properties
. key.properties
identificeert de locatie van de sleutelbewaarplaats, plus de benodigde referenties om de Java Key Store te ontgrendelen om het pakket te ondertekenen. Met alle benodigde gegevens in de hand, ondertekent Gradle nu de app bundel of APK als onderdeel van onze release build.
Laten we nog even controleren of al onze bestanden op de juiste plek staan.
Onze gewijzigde build.gradle
staat in flutter_app/android/app/build.gradle
.
Ons key.jks
-bestand staat in flutter_app/android/app/key.jks
.
Ons key.properties
-bestand staat in flutter_app/android/key.properties
.
Als we zeker zijn van het bovenstaande, zouden we flutter build apk --release
nu moeten kunnen uitvoeren en het ondertekenen zou goed moeten werken.
STAP 4: Opsturen naar Google Play Store
Nu kunnen we onze APK of app bundel uploaden naar de Play Store. Wanneer we dit doen met ons ondertekende pakket, en met Google Play Signing aan (wat het standaard is), zal Google de sleutel bevestigen die we hebben gebruikt om het pakket te ondertekenen en het onthouden als onze uploadsleutel. Google zal dan onze APK of app bundel ondertekenen met hun eigen sleutel. Het is belangrijk dat alle volgende updates die we voor deze app leveren, we met deze zelfde sleutel ondertekenen. Google Play herkent deze sleutel als onze uploadsleutel en we kunnen geen updates uitbrengen zonder deze.
Ik begrijp niets van het bovenstaande en ik zou een ongelooflijk visuele illustratie op prijs stellen van wat er precies gebeurt.
Kan ik doen! Dit is wat er gebeurt.
- We genereren een supergeheime manier om onszelf te identificeren, bijna alsof we een paspoort voor onszelf maken.
- Omdat iedereen met dit ‘paspoort’ in staat zal zijn om zich positief te identificeren als ons (dat wil zeggen: ons zonder veel weerstand imiteren), vergrendelen we het achter een wachtwoord in onze kluis (de JKS, of Java Key Store).
- We maken de app bundel of APK, en vervolgens ondertekenen we het pakket met dezelfde handtekening die we op het paspoort hebben gebruikt. Om toegang te krijgen tot dit paspoort, moeten we de kluis waarin het paspoort zich bevindt ontgrendelen (door het wachtwoord en de alias aan het Gradle-bouwproces te verstrekken).
- We sturen het pakket naar de distributeur (Google Play). De distributeur, die het pakket voor de eerste keer ziet, neemt nota van onze handtekening die we op dit pakket hebben gebruikt en neemt er een kopie van.
- Wanneer we in de toekomst pakketten naar onze distributeur (Google Play) sturen, ondertekenen we deze pakketten met dezelfde gegevens die we in eerste instantie hebben gebruikt. Onze distributeur, die zich de gegevens herinnert die we aanvankelijk hebben gebruikt om het pakket te uploaden, accepteert of weigert het pakket. Als het overeenkomt (als de uploadsleutel dezelfde is als die we in eerste instantie hebben gebruikt), dan wordt het pakket geaccepteerd en gedistribueerd. Anders wordt het niet geaccepteerd.
- Onze distributeur, wetende dat het initiële pakket en mogelijke toekomstige pakketten zeker van ons afkomstig zijn, distribueert het pakket.
Code signing laten werken met Codemagic
We willen dit uiteindelijk ondertekenen als onderdeel van onze CI/CD-workflow, maar tegelijkertijd willen we onze keystore en eigenschappenbestand niet in bronbeheer inchecken. In plaats daarvan willen we dat onze CI/CD provider het pakket bouwt en het dan later in het bouwproces ondertekent met een sleutelbewaarplaats die wij leveren.
Instellen met Git
Als we een geheel nieuwe Flutter app hebben, dan kunnen we naar de map gaan en git init
intypen om bronbeheer te gaan gebruiken met de app.
Als standaard, checken we gewoon vrolijk ons keystore en keystore eigenschappen bestand in, wat een slecht idee is vanuit een veiligheidsperspectief.
Je zou dit vanaf het begin goed moeten doen
Als je per ongeluk je keystore eigenschappen en keystore bestand incheckt en die veranderingen pushed, dan kunnen mensen die bestanden er op ieder moment in de toekomst uitplukken door in je Git geschiedenis te kijken. Je kunt bestanden in de toekomst handmatig uit Git verwijderen, of je kunt je repository opnieuw initialiseren zonder die bestanden, maar het is beter om ze in de eerste plaats gewoon niet te controleren.
We willen deze regels toevoegen aan het einde van ons .gitignore
bestand:
# Don't check in the keystore files or equivalent*.jkskey*.properties
Geen Java KeyStore (JKS) of eigenschappen voor code signing zullen worden ingecheckt in de broncontrole. Hoe mooi.
Maken dat build.gradle niet tekent als het op CI/CD wordt uitgevoerd
Terwijl uw project aan het bouwen is, zijn de keystore en instellingen niet beschikbaar. We willen dat de build nog steeds een release build produceert, ook al is deze niet ondertekend.
Dit is het gedeelte van mijn build.gradle
dat dit mogelijk maakt:
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 }}
Codemagic instellen om onze builds te ondertekenen
In uw buildproces vindt u de sectie Android-code ondertekenen (deze bevindt zich in de sectie Publiceren). Het ziet er als volgt uit:
Nu uploaden we onze sleutelbewaarplaats en stellen we ons wachtwoord, sleutelalias en sleutelwachtwoord in (die hetzelfde zijn als wat we in eerste instantie hebben ingesteld in ons bestand keystore.properties).
Daarna klikken we op “Opslaan”. Wanneer Codmagic ons bouwproces uitvoert, zal het automatisch een ondertekende APK of App Bundle voor ons produceren.
En dat is het zo’n beetje! Met deze ondertekende APK of App Bundle, kunt u uw app implementeren in de Play Store.
U kunt mijn Git repo bekijken voor een voorbeeld hier (uiteraard, zonder de keystore of eigenschappen).
Dat is het.
Als je nog steeds de weg kwijt bent, voel je vrij om het me te laten weten op @azimuthapps en ik zal proberen om te helpen. Het kan frustrerend zijn om het goed te krijgen, maar als je het eenmaal hebt, zou het voor de nabije toekomst moeten werken.
Lewis Cianci is een software ontwikkelaar in Brisbane, Australië. Zijn eerste computer had een tapedrive. Hij ontwikkelt al minstens tien jaar software, en heeft in die tijd al heel wat frameworks voor mobiele ontwikkeling gebruikt (zoals Ionic en Xamarin Forms). Maar na zijn overstap naar Flutter gaat hij nooit meer terug. Je kunt hem bereiken op zijn blog, lezen over andere niet-flutterige dingen op Medium, of misschien een glimp van hem opvangen in de dichtstbijzijnde en meest chique coffeeshop met hem en zijn lieve vrouw.
Meer artikelen van Lewis: