Signature de code
Signez et notarisez les installateurs de votre app sur macOS, signez avec un certificat PFX ou Azure Artifact Signing sur Windows, et signez les paquets DEB/RPM en GPG sur Linux.
Dans ce tutoriel, vous allez signer les installateurs de votre app Nucleus sur macOS, Windows et Linux,
et notariser le build macOS auprès d'Apple. La signature se configure dans le même DSL
nativeDistributions { } que le packaging : un bloc signing { } par système d'exploitation, ou
un seul bloc unifié pour les trois.
Avant de commencer
La signature est livrée avec le plugin Gradle, il n'y a donc rien de plus à installer. Ce dont vous avez besoin dépend de la plateforme visée :
- macOS — un compte Apple Developer avec un certificat Developer ID Application, plus des
identifiants de notarisation (un mot de passe app-specific d'Apple ID, un profil keychain
notarytool, ou une clé API App Store Connect). - Windows — un certificat de signature
.pfx/.p12, ou un compte Azure Trusted Signing. - Linux — une paire de clés GPG. Aucun certificat payant ni autorité n'est requis.
Signer et notariser sur macOS
Les formats de distribution directe (DMG, ZIP) sont signés avec une identité
Developer ID Application puis soumis à Apple pour notarisation. Configurez l'identité dans
signing { } et les identifiants dans notarization { } :
macOS {
signing {
sign.set(true)
identity.set("Developer ID Application: My Company (TEAMID)")
}
notarization {
appleID.set("dev@example.com")
password.set(System.getenv("MAC_NOTARIZATION_PASSWORD"))
teamID.set("TEAMID")
}
}Les builds Mac App Store (PKG) suivent un autre chemin : un certificat 3rd Party Mac Developer Application avec des provisioning profiles, et pas de notarisation — Apple review l'envoi via Transporter.
Choisir un mode de notarisation
La notarisation propose trois modes d'authentification mutuellement exclusifs. En configurer plusieurs dans le même build est rejeté à la validation : choisissez-en un.
-
Apple ID et mot de passe app-specific :
notarization { appleID.set("dev@example.com") password.set(System.getenv("MAC_NOTARIZATION_PASSWORD")) teamID.set("TEAMID") } -
Un profil keychain
notarytool. Stockez les identifiants une fois avecxcrun notarytool store-credentials AC_PASSWORD …, puis référencez le profil :notarization { keychainProfile.set("AC_PASSWORD") } -
Une clé API App Store Connect, le mode recommandé en CI :
notarization { apiKey.set("/path/to/AuthKey_ABC123.p8") apiKeyId.set("ABC123") apiIssuer.set("12345678-90ab-cdef-1234-567890abcdef") }
Signer les binaires universels
lipo invalide toutes les signatures existantes, donc les builds universels re-signent après la
fusion dans un ordre inside-out strict : les fichiers .dylib et .jnilib avec runtime
entitlements, les exécutables principaux avec app entitlements, puis le runtime bundle, puis le
.app lui-même. Tous les appels codesign utilisent --options runtime --timestamp. L'action
composite build-macos-universal s'en charge en CI — voir CI/CD.
Signer sur Windows
Utilisez un certificat .pfx ou .p12, éventuellement protégé par mot de passe, avec un serveur de
timestamping. L'algorithme par défaut est SigningAlgorithm.Sha256 :
windows {
signing {
enabled = true
certificateFile.set(file(System.getenv("WIN_CSC_LINK") ?: "certs/certificate.pfx"))
certificatePassword = System.getenv("WIN_CSC_KEY_PASSWORD")
algorithm = SigningAlgorithm.Sha256
timestampServer = "http://timestamp.digicert.com"
}
}Serveurs de timestamping courants : DigiCert (http://timestamp.digicert.com), Sectigo
(http://timestamp.sectigo.com) et GlobalSign (http://timestamp.globalsign.com).
Pour signer via un HSM cloud plutôt qu'avec un fichier de certificat local, utilisez Azure Artifact Signing :
windows {
signing {
enabled = true
publisherName = "My Publisher"
azureTenantId = System.getenv("AZURE_TENANT_ID")
azureEndpoint = "https://eastus.codesigning.azure.net"
azureCertificateProfileName = "my-profile"
azureCodeSigningAccountName = "my-account"
}
}Signer sur Linux
Sur Linux, il n'y a pas de gatekeeper OS. La signature permet à quiconque télécharge votre .deb/.rpm
en direct — hors dépôt apt/dnf — de vérifier qu'il vient bien de vous et n'a pas été altéré. Vous signez
avec votre propre clé GPG, sans certificat payant ni autorité.
-
Générez une clé une fois, si vous n'en avez pas déjà une :
gpg --full-generate-key # RSA 4096, votre nom et votre email gpg --list-secret-keys --keyid-format=long # sec rsa4096/AB12CD34EF56 ... l'id de la clé est "AB12CD34EF56" -
Activez la signature avec cet id de clé :
build.gradle.kts linux { signing { enabled.set(true) keyId.set("AB12CD34EF56") // passphrase.set(System.getenv("LINUX_GPG_PASSPHRASE")) // seulement si la clé en a une } }La clé est lue depuis votre trousseau GPG local. En CI, pointez plutôt
keyFilesur une clé exportée — voir Gérer les secrets de signature en CI. -
Buildez. Chaque paquet sort signé, avec sa clé publique écrite à côté :
MyApp-1.0.0-linux-x64.deb MyApp-1.0.0-linux-x64.deb.asc # signature détachée (DEB) MyApp-1.0.0-linux-x64.deb.pub.asc # votre clé publique MyApp-1.0.0-linux-x64.rpm # signature intégrée au header RPM MyApp-1.0.0-linux-x64.rpm.pub.ascPubliez chaque paquet avec son
.pub.asc(et le.deb.ascpour le DEB) sur votre page de téléchargement. -
Les utilisateurs vérifient un téléchargement avant de lui faire confiance :
# DEB — signature détachée gpg --import MyApp-1.0.0-linux-x64.deb.pub.asc gpg --verify MyApp-1.0.0-linux-x64.deb.asc MyApp-1.0.0-linux-x64.deb # Good signature from "Your Name <you@example.com>" # RPM — signature du header sudo rpm --import MyApp-1.0.0-linux-x64.rpm.pub.asc rpm -K MyApp-1.0.0-linux-x64.rpm # digests signatures OK
La signature ne change rien à l'installation. Un paquet signé s'installe exactement comme un non
signé, avec ou sans la clé importée. La vérification est une étape optionnelle que l'utilisateur
lance avant d'installer, et ne devient obligatoire que s'il l'impose lui-même (par exemple une policy
debsig-verify, ou une vérification RPM stricte).
Choisir une méthode de signature DEB
debMethod sélectionne comment le DEB est signé, via une valeur DebSignMethod :
Detached(défaut) écrit un<pkg>.deb.ascdétaché, vérifié avecgpg --verify <pkg>.deb.asc <pkg>.deb. Il ne nécessite quegpg, donc marche sur toutes les distributions. C'est le défaut parce quedpkg-siga été retiré des versions récentes de Debian et Ubuntu.DpkgSigintègre un membre_gpgoriginviadpkg-sig, vérifié avec un simplegpg --verify <pkg>.deb. Il nécessitedpkg-sigau moment du build.Debsigintègre un membre_gpgoriginviadebsigs, vérifié avecdebsig-verify. Il nécessite en plus une policy et un keyring par clé côté client.
linux {
signing {
enabled.set(true)
keyId.set("AB12CD34EF56")
debMethod = DebSignMethod.Detached // Detached | DpkgSig | Debsig
}
}La clé de signature ne touche jamais votre vrai trousseau pendant le build : Nucleus importe keyFile
dans un GNUPGHOME jetable, supprimé ensuite.
Activer la mise à jour sans mot de passe
Une fois vos paquets signés, l'app peut appliquer les mises à jour sans prompt de mot de passe root,
car l'install est conditionnée à la signature. Ajoutez silentUpdate.set(true) au bloc signing
Linux ; voir mises à jour sans mot de passe
pour le flux complet et le modèle de sécurité.
Configurer toutes les plateformes dans un seul bloc
Pour garder la signature dans un seul point d'entrée, enveloppez les blocs par OS dans un unique
signing { } sous nativeDistributions. Il délègue aux mêmes instances macOS.signing,
windows.signing et linux.signing, donc vous pouvez mélanger les deux styles librement :
nativeDistributions {
signing {
macOS { sign.set(true); identity.set("Developer ID Application: My Company (TEAMID)") }
windows { enabled = true; certificateFile.set(file("certs/certificate.pfx")) }
linux { enabled.set(true); keyId.set("AB12CD34EF56") }
}
}Gérer les secrets de signature en CI
Ne commitez jamais de certificats ni de clés privées. Sur GitHub Actions, encodez le PFX en base64 et
décodez-le au build. Pour macOS, l'action composite setup-macos-signing crée un keychain temporaire
et importe les certificats depuis les secrets — voir CI/CD.
Pour Linux, exportez la clé privée et stockez-la en secret :
gpg --armor --export-secret-keys AB12CD34EF56 # à coller dans le secret LINUX_GPG_PRIVATE_KEYAjoutez LINUX_GPG_PRIVATE_KEY, LINUX_GPG_KEY_ID et (si définie) LINUX_GPG_PASSPHRASE. Le
workflow de release écrit un fichier de clé temporaire et les passe au build via les propriétés
compose.desktop.linux.signing.* ; quand les secrets sont absents, le build est laissé non signé.
Configurez le build pour les lire :
linux {
signing {
enabled.set(true)
keyId.set(System.getenv("LINUX_GPG_KEY_ID"))
keyFile.set(file(System.getenv("LINUX_GPG_KEY_FILE") ?: "build/signing-key.asc"))
passphrase.set(System.getenv("LINUX_GPG_PASSPHRASE"))
}
}Référence
macOS.signing { }—sign,identity,keychain,prefixmacOS.notarization { }—appleID+password+teamID, oukeychainProfile(+keychainPath), ouapiKey+apiKeyId+apiIssuerwindows.signing { }—enabled,certificateFile,certificatePassword,certificateSha1,certificateSubjectName,algorithm,timestampServer, plus les champs Azurelinux.signing { }—enabled,keyId,keyFile,passphrase,debMethod(Detached|DpkgSig|Debsig),silentUpdatesigning { macOS { } windows { } linux { } }— bloc unifié sur les mêmes settings par OS
Le DSL complet est documenté dans la référence Gradle DSL.
Notes
- Entitlements pour le hardened runtime. Les entitlements JVM minimales sont
com.apple.security.cs.allow-jit,com.apple.security.cs.allow-unsigned-executable-memoryetcom.apple.security.cs.allow-dyld-environment-variables. Voir sandboxing pour les entitlements du PKG sandboxé. - Propriétés Gradle équivalentes. Chaque champ de notarisation a une propriété Gradle
compose.desktop.mac.notarization.<name>, utile quand vous passez les secrets avec-Pplutôt qu'en variables d'environnement. La signature Linux exposecompose.desktop.linux.signetcompose.desktop.linux.signing.{keyId,keyFile,passphrase}. - La signature Linux est opt-in. Elle reste désactivée tant que
linux.signing.enabledn'est pastrueavec unkeyIddéfini. La signature au niveau dépôt (apt/dnfRelease.gpg,InRelease) est un sujet distinct, hors périmètre — ici on signe le paquet autonome pour le téléchargement direct. - Rotation du
.pfx. Mettez à jour le certificat au moins 30 jours avant son expiration. Un binaire signé mais expiré échoue SmartScreen jusqu'à ce qu'une nouvelle signature timestampée soit appliquée.
Et ensuite
- CI/CD — signez les builds dans GitHub Actions avec les actions composites.
- Mise à jour automatique — livrez des mises à jour signées, dont les mises à jour sans mot de passe sur Linux.
- Sandboxing — entitlements et builds au format store.
- Référence Gradle DSL — la surface complète de
nativeDistributions { }.
Construire pour Linux
Configurez une application Nucleus pour produire des paquets DEB, RPM, AppImage et pacman, et publier sur les stores Snap et Flatpak.
Certificats CA de confiance
Importez des certificats de CA racines privées dans le JDK embarqué avec votre application empaquetée pour que ses appels HTTPS leur fassent confiance.