Skip to content

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 ​

ComposantRôleChez qui
dotnet/shared/LicensingFormat, signature vérifiable, emplacement du fichier, demande .licreqPartagé
Client de supervision, gestionnaire de serviceProduire la demande, appliquer la licence reçueChez le client
dotnet/license-generatorÉmettre et signer les licences, tenir le registreDesautel 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 ​

  1. 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 .licreq porte l'empreinte du poste, son nom réseau et la version installée.

  2. Chez Desautel — Superviseur-Licences.exe ouvre 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.

  3. 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 :

  1. Ressource embarquée dans le binaire — incluse à la compilation sidotnet/license-generator/license-private.key était présent sur la machine de build (cf. LicenseGenerator.csproj). C'est ce qui produit un .exe autonome « tout dedans ». Le dépôt git, lui, ne contient jamais la clé.

  2. 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.csproj
  3. Fichier 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-tool

La sortie est un fichier unique de ~60 Mo, Superviseur-Licences.exe, sans aucun fichier annexe :

OptionRôle
IncludeNativeLibrariesForSelfExtractAbsorbe 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.
EnableCompressionInSingleFileRamè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 .exe ne 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 :

  1. Remplacer PublicKeyBase64 dans dotnet/shared/Licensing/LicenseVerifier.cs par la nouvelle clé publique.
  2. Stocker la nouvelle clé privée hors dépôt (variable d'environnement ou license-private.key).
  3. 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.

Documentation Technique