Skip to Content
New release 12 available 🎉
LicensingLicense-Bound Code Encryption

License-Bound Code Encryption

Make a protected feature cryptographically dependent on a valid license by storing the code-encryption password inside the license itself.

A boolean license check — if (!IsLicensed) return; — is easy to patch out. This example shows a stronger pattern: the sensitive method is encrypted with Babel Obfuscator Code Encryption, and the password that the Babel Virtual Machine (BVM) needs to run it is not in the binary at all. It lives, itself encrypted, inside a signed and hardware-locked Babel license. Removing the license check is not enough — without a valid license there is no password, and without the password the encrypted method cannot be reconstructed or executed.

Code Example

git clone https://github.com/babelfornet/license-bound-encryption-console-example.git

The project is a cross-platform .NET console application with two parts: SecureApp, the licensed application whose sensitive method is code-encrypted, and LicenseGenerator, a vendor-side tool that mints signed licenses. The Babel package version is centralized in Directory.Build.props so the sample can target the version that matches your feed.

The Protected Method

The intellectual property to protect is a single method. The [Obfuscation] attribute tells Babel to encrypt its body and store it inside the assembly (internal=true) under the source name core, using a build-time password:

[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; }

Code Encryption is enabled in the Release build only, scoped to the sensitive class through an MSBuild property in SecureApp.csproj:

<PropertyGroup Condition="'$(Configuration)' == 'Release'"> <MsilEncryption Condition="'$(MsilEncryption)' == ''">SecureApp\.Secret::.*</MsilEncryption> <BabelWarningsAsErrors>W00000</BabelWarningsAsErrors> </PropertyGroup>

Code Encryption runs only in the Release configuration. The BabelWarningsAsErrors line turns the evaluation-mode warning W00000 into an error, so an unprotected assembly can never ship by accident.

Where the Password Lives

At build time the method is encrypted with the password C0re-Pr0tect!on-K3y. At runtime the BVM asks the application for that password through a get password hook before it can run the method. Instead of returning a hard-coded value, the hook validates the license and reads the password from it:

[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() uses the embedded RSA public key to verify the license signature and enforces every restriction, including the hardware lock:

public static ILicense Validate() { var manager = new StringLicenseManager { SignatureProvider = RSASignature.FromKeys(PublicKey) }; return manager.Validate(File.ReadAllText(LicenseFilePath), typeof(Program)); }

Minting the License

The vendor-side LicenseGenerator signs a license that binds to the SecureApp assembly and to the current machine, and carries the code password as an encrypted field. The password therefore never appears in clear — not in the binary, and not even in the license file:

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");

The LicenseGenerator and the RSA private key (keys.pem) are for demonstration only. In production the license is issued by the vendor — or by the Babel Licensing Service — and the private key is kept offline. Only the public key ships with the application.

Running It

# 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.dll

With a valid license the protected method runs:

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.

Remove or tamper with SecureApp.lic, or run it on a different machine, and the outcome is not just a friendlier error message — the encrypted code physically cannot run:

License INVALID : Invalid license signature Protected computation blocked: Invalid license signature Without a valid license the encrypted code cannot be decrypted or executed.

Why This Is Stronger

Because the password is derived from a signed license, an attacker who strips the validation code out of the assembly gains nothing: the BVM still has no password for the core source, so Secret.Compute remains an opaque, encrypted blob. The signature prevents forging a license, and the hardware lock prevents reusing a legitimate one on another machine. This same technique underpins the Feature Based Licenses example and the vendor-hosted License Templates, where the encrypted field is delivered by the Babel Licensing Service instead of a local generator.

Last updated on