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.gitThe 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.dllWith 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.