Thème
Licences Superviseur
Une licence autorise un poste à exécuter le logiciel. Elle est signée par Desautel avec une clé privée RSA et vérifiée par les logiciels livrés avec la clé publique correspondante, embarquée dans le client de supervision et le gestionnaire de service.
Répartition des rôles
| Composant | Rôle | Chez qui |
|---|---|---|
dotnet/shared/Licensing | Format, signature vérifiable, emplacement du fichier, demande .licreq | Partagé |
| Client de supervision, gestionnaire de service | Produire la demande, appliquer la licence reçue | Chez le client |
dotnet/license-generator | Émettre et signer les licences, tenir le registre | Desautel uniquement |
L'outil d'émission ne quitte jamais Desautel. Le poste à équiper n'a besoin d'aucun outil : le client de supervision et le gestionnaire de service collectent eux-mêmes ce que l'opérateur de licences doit connaître, et savent absorber la licence reçue.
Circuit d'activation
Sur le poste — le client de supervision affiche sa fenêtre d'activation quand aucune licence valide n'est installée ; le gestionnaire de service offre la même chose par son menu Licence…, ce qui reste accessible même lorsque le client refuse de démarrer. Le technicien renseigne client, site et contact, puis Enregistrer la demande… : un fichier
.licreqporte l'empreinte du poste, son nom réseau et la version installée.Chez Desautel —
Superviseur-Licences.exeouvre la demande (bouton ou glisser-déposer) : elle pré-remplit l'empreinte et le client. L'opérateur choisit une validité perpétuelle ou une date de fin, génère, puis renvoie le fichier.key.Sur le poste — le technicien colle la licence, importe le fichier ou le dépose sur la fenêtre. L'activation est vérifiée immédiatement ; le client de supervision démarre sans redémarrage.
L'empreinte peut aussi être saisie à la main (option Empreinte saisie) et une licence peut être émise pour la machine de l'opérateur (option Cette machine), utile en démonstration.
Format d'une licence
Un jeton tient sur une ligne : SUPV2.<contenu>.<signature>, les deux segments en base64url. Le contenu est un JSON qui porte l'identifiant d'émission, le logiciel, l'empreinte autorisée, le client, le site, la date d'émission et, le cas échéant, le dernier jour de validité. La signature couvre les octets du contenu tels qu'ils sont transportés.
La vérification refuse une licence dont la signature ne correspond pas, qui vise un autre poste ou un autre logiciel, dont la validité est dépassée, ou dont l'empreinte du poste est illisible. Le format SUPV2 est le seul accepté : une signature nue sans préfixe est rejetée avec un diagnostic explicite.
Emplacement sur le poste
La licence est liée au matériel, pas à une session Windows. Elle est cherchée d'abord dans %ProgramData%\Superviseur\license.key, qui couvre tous les comptes du poste, puis dans %AppData%\Superviseur\license.key, utilisé en repli quand le premier emplacement n'est pas accessible en écriture. Une licence refusée n'écrase jamais celle en place.
Fournir la clé privée à l'outil
Superviseur-Licences.exe résout la clé privée (format PKCS#8, base64) au démarrage, dans cet ordre — l'origine retenue est affichée en bas de sa fenêtre :
Ressource embarquée dans le binaire — incluse à la compilation si
dotnet/license-generator/license-private.keyétait présent sur la machine de build (cf.LicenseGenerator.csproj). C'est ce qui produit un.exeautonome « tout dedans ». Le dépôt git, lui, ne contient jamais la clé.Variable d'environnement
SUPERVISEUR_LICENSE_PRIVATE_KEY— pratique pour un poste de génération dédié.powershell$env:SUPERVISEUR_LICENSE_PRIVATE_KEY = "<clé PKCS#8 base64>" dotnet run --project dotnet/license-generator/LicenseGenerator.csprojFichier local non versionné
license-private.key, cherché à côté de l'exécutable puis dans le répertoire courant. Il est ignoré par git (.gitignore:license-private.key,*.key).
Sans aucune de ces sources, l'outil s'ouvre mais l'émission est désactivée, avec le motif affiché.
Compiler l'outil (techniciens)
Avec dotnet/license-generator/license-private.key présent sur la machine de build :
powershell
dotnet publish dotnet/license-generator/LicenseGenerator.csproj -c Release -r win-x64 `
--self-contained true `
-p:PublishSingleFile=true `
-p:IncludeNativeLibrariesForSelfExtract=true `
-p:EnableCompressionInSingleFile=true `
-p:DebugType=none `
-p:GenerateDocumentationFile=false `
-o staging/license-toolLa sortie est un fichier unique de ~60 Mo, Superviseur-Licences.exe, sans aucun fichier annexe :
| Option | Rôle |
|---|---|
IncludeNativeLibrariesForSelfExtract | Absorbe les cinq bibliothèques natives de WPF (wpfgfx_cor3, PresentationNative_cor3, D3DCompiler_47_cor3, PenImc_cor3, vcruntime140_cor3), que PublishSingleFile laisse à côté de l'exécutable. L'exécutable les extrait dans un répertoire temporaire à son premier lancement, d'où un démarrage initial un peu plus lent. |
EnableCompressionInSingleFile | Ramène le binaire de ~123 Mo à ~60 Mo. |
DebugType=none | Écarte les symboles de débogage (.pdb). |
GenerateDocumentationFile=false | Écarte la documentation XML, utile au développement mais sans objet dans un binaire remis à un technicien. |
Le répertoire de sortie n'est pas vidé par dotnet publish : le supprimer avant de republier évite d'y laisser traîner les fichiers d'une compilation antérieure.
(deployment/build-all.ps1 applique la même commande et place le résultat dans staging/license-tool/.)
⚠️ Sécurité — distribution interne uniquement. Un binaire qui embarque la clé permet d'extraire cette clé. La sécurité repose donc entièrement sur le fait que ce
.exene circule qu'entre techniciens Desautel. Il ne doit JAMAIS être joint à une release GitHub, ni à un canal public, ni remis à un client. Seul l'installateur de l'application (qui ne contient que la clé publique) est publiable.
Registre des émissions
L'outil consigne chaque licence émise dans %AppData%\Superviseur-Licences\licenses-registry.json : client, site, poste, empreinte, dates et jeton émis. Sélectionner une ligne réaffiche sa licence, ce qui permet de la renvoyer à un client qui l'a perdue sans en réémettre une nouvelle. Le registre n'a aucun rôle de sécurité : l'autorité reste la signature portée par chaque licence.
Rotation des clés
Pour générer une nouvelle paire RSA 2048 :
powershell
$rsa = [System.Security.Cryptography.RSA]::Create(2048)
'PRIV=' + [Convert]::ToBase64String($rsa.ExportPkcs8PrivateKey())
'PUB=' + [Convert]::ToBase64String($rsa.ExportSubjectPublicKeyInfo())Ensuite :
- Remplacer
PublicKeyBase64dansdotnet/shared/Licensing/LicenseVerifier.cspar la nouvelle clé publique. - Stocker la nouvelle clé privée hors dépôt (variable d'environnement ou
license-private.key). - Régénérer toutes les licences déployées : un poste mis à jour avec la nouvelle clé publique ne reconnaît plus les licences signées par l'ancienne.
Tests
dotnet/tests/tools n'embarque aucune clé de production : LicenseSigningHelper génère une paire RSA 2048 éphémère au chargement et l'injecte dans LicenseVerifier.Validate, ce qui valide la chaîne complète — contenu, signature, empreinte, expiration — sans introduire de clé privée dans le dépôt. Validate_AgainstTheProductionKey_RejectsTestLicenses vérifie qu'une licence signée par cette clé de test est refusée par la clé publique de production.
dotnet/tests/client n'émet aucune licence : il n'éprouve que les refus — texte illisible, signature nue sans préfixe, texte vide — et la stabilité de l'empreinte du poste.
Quatre des tests de dotnet/tests/tools passent par Xunit.SkippableFact et se sautent hors Windows, l'empreinte du poste étant lue par WMI.