Chiffrement du code lié à la licence
Rendre une fonctionnalité protégée cryptographiquement dépendante d’une licence valide en stockant le mot de passe du chiffrement du code dans la licence elle-même.
Un contrôle de licence booléen, tel que if (!IsLicensed) return;, est facile à supprimer en
modifiant le binaire. Cet exemple présente un schéma plus solide : la méthode sensible est chiffrée
avec le chiffrement du code de Babel Obfuscator, et le mot de
passe dont la machine virtuelle Babel (BVM) a besoin pour l’exécuter ne figure pas du tout dans le
binaire. Il se trouve, lui-même chiffré, dans une licence Babel signée et liée à la machine.
Supprimer le contrôle de licence ne suffit pas : sans licence valide, il n’y a pas de mot de passe,
et sans mot de passe la méthode chiffrée ne peut être ni reconstituée ni exécutée.
Exemple de code
git clone https://github.com/babelfornet/license-bound-encryption-console-example.gitLe projet est une application console .NET multiplateforme en deux parties : SecureApp,
l’application sous licence dont la méthode sensible est chiffrée, et LicenseGenerator, un outil
côté éditeur qui émet des licences signées. La version du package Babel est centralisée dans
Directory.Build.props, afin que l’exemple puisse cibler la version qui correspond à votre flux.
La méthode protégée
La propriété intellectuelle à protéger est une seule méthode. L’attribut [Obfuscation] demande
à Babel de chiffrer son corps et de le stocker dans l’assembly (internal=true) sous le nom de
source core, avec un mot de passe défini à la génération :
[Obfuscation(Feature = "msil encryption:internal=true;source=core;password=C0re-Pr0tect!on-K3y", Exclude = false)]
public static long Compute(int a, int b)
{
long acc = 0;
for (int i = 1; i <= b; i++)
acc += (long)a * i;
return acc + 42;
}Le chiffrement du code n’est activé que dans le build Release, et limité à la classe sensible au
moyen d’une propriété MSBuild dans SecureApp.csproj :
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<MsilEncryption Condition="'$(MsilEncryption)' == ''">SecureApp\.Secret::.*</MsilEncryption>
<BabelWarningsAsErrors>W00000</BabelWarningsAsErrors>
</PropertyGroup>Le chiffrement du code ne s’exécute que dans la configuration Release. La ligne BabelWarningsAsErrors
transforme en erreur l’avertissement W00000 du mode d’évaluation, de sorte qu’un assembly non
protégé ne peut jamais être livré par accident.
Où se trouve le mot de passe
À la génération, la méthode est chiffrée avec le mot de passe C0re-Pr0tect!on-K3y. À l’exécution,
la BVM demande ce mot de passe à l’application au moyen d’un hook get password avant de pouvoir
exécuter la méthode. Au lieu de renvoyer une valeur codée en dur, le hook valide la licence et y
lit le mot de passe :
[Obfuscation(Feature = "msil encryption get password")]
internal static string GetSourcePassword(string source)
{
var license = Validate();
// The code password is carried by the license as an encrypted field.
var field = license.Fields.FirstOrDefault(f => f.Name == source)
?? throw new InvalidOperationException($"License does not grant source '{source}'.");
// Decrypt the field with the vendor secret to recover the real code password.
return field.Value.Decrypt(Secrets.FieldSecret);
}Validate() utilise la clé publique RSA incorporée pour vérifier la signature de la licence et
applique toutes les restrictions, y compris la liaison au matériel :
public static ILicense Validate()
{
var manager = new StringLicenseManager
{
SignatureProvider = RSASignature.FromKeys(PublicKey)
};
return manager.Validate(File.ReadAllText(LicenseFilePath), typeof(Program));
}Émettre la licence
Côté éditeur, LicenseGenerator signe une licence liée à l’assembly SecureApp et à la machine en
cours, qui contient le mot de passe du code dans un champ chiffré. Le mot de passe n’apparaît
donc jamais en clair, ni dans le binaire, ni même dans le fichier de licence :
string encryptedPassword = Secrets.CodePassword.Encrypt(Secrets.FieldSecret);
var license = new StringLicense()
.ForAssembly(assemblyFullName)
.WithUniqueId("SECURE-")
.WithHardwareKey(HardwareId.Create().ToMachineKey())
.WithField(Secrets.Source, encryptedPassword)
.LicensedTo("Demo Customer", "demo@example.com", "ACME Corp");
string serial = license.SignWith(signature).ToReadableString("ASCII");LicenseGenerator et la clé privée RSA (keys.pem) servent uniquement à la démonstration. En
production, la licence est émise par l’éditeur, ou par le
Babel Licensing Service, et la clé privée est conservée
hors ligne. Seule la clé publique est livrée avec l’application.
Exécuter l’exemple
# Build the app in Release — this is when Babel encrypts Secret.Compute.
dotnet build src/SecureApp/SecureApp.csproj -c Release
# Generate a signed, machine-locked license.
dotnet run --project src/LicenseGenerator
# Deploy the license next to the app and run it.
cp SecureApp.lic src/SecureApp/bin/Release/net8.0/
dotnet src/SecureApp/bin/Release/net8.0/SecureApp.dllAvec une licence valide, la méthode protégée s’exécute :
License OK : SECURE-8CCC8
Licensed to : Demo Customer
Bound to machine: ADSQH-89GGJ-J7CJY-3FQFK-DF48H
Protected computation Secret.Compute(7, 5) = 147
The encrypted method ran: the license unlocked the code.Si vous supprimez ou falsifiez SecureApp.lic, ou si vous exécutez l’application sur une autre
machine, le résultat ne se limite pas à un message d’erreur plus aimable, car le code chiffré ne
peut matériellement pas s’exécuter :
License INVALID : Invalid license signature
Protected computation blocked: Invalid license signature
Without a valid license the encrypted code cannot be decrypted or executed.Pourquoi cette approche est plus solide
Comme le mot de passe provient d’une licence signée, un attaquant qui retire le code de validation
de l’assembly n’y gagne rien : la BVM n’a toujours pas de mot de passe pour la source core, et
Secret.Compute reste donc un bloc opaque et chiffré. La signature empêche de contrefaire une
licence, et la liaison au matériel empêche de réutiliser une licence légitime sur une autre machine.
La même technique est à la base de l’exemple
Licences basées sur des fonctionnalités et des
Modèles de licence hébergés par l’éditeur, où le champ chiffré
est fourni par le Babel Licensing Service au lieu d’un générateur local.