Skip to Content
Nuova versione 12 disponibile 🎉
LicensingCifratura del codice legata alla licenza

Cifratura del codice legata alla licenza

Fai dipendere una funzionalità protetta, a livello crittografico, da una licenza valida conservando la password della cifratura del codice nella licenza stessa.

Un controllo booleano della licenza, if (!IsLicensed) return;, è facile da eliminare con una patch. Questo esempio mostra uno schema più robusto: il metodo sensibile è cifrato con la cifratura del codice di Babel Obfuscator, e la password che serve alla Babel Virtual Machine (BVM) per eseguirlo non è affatto nel file binario. Si trova, a sua volta cifrata, in una licenza Babel firmata e vincolata al computer. Rimuovere il controllo della licenza non basta: senza una licenza valida non c’è password, e senza la password il metodo cifrato non può essere ricostruito né eseguito.

Esempio di codice

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

Il progetto è un’applicazione console .NET multipiattaforma in due parti: SecureApp, l’applicazione con licenza il cui metodo sensibile è protetto dalla cifratura del codice, e LicenseGenerator, uno strumento del produttore che emette licenze firmate. La versione del pacchetto Babel è centralizzata in Directory.Build.props, così l’esempio può usare la versione presente nel tuo feed.

Il metodo protetto

La proprietà intellettuale da proteggere è un solo metodo. L’attributo [Obfuscation] indica a Babel di cifrarne il corpo e di conservarlo all’interno dell’assembly (internal=true) con il nome di origine core, usando una password definita in fase di build:

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

La cifratura del codice è abilitata solo nella build Release, limitata alla classe sensibile tramite una proprietà MSBuild in SecureApp.csproj:

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

La cifratura del codice viene eseguita solo nella configurazione Release. La riga BabelWarningsAsErrors trasforma in errore l’avviso W00000 della modalità demo, così un assembly non protetto non può mai essere distribuito per sbaglio.

Dove si trova la password

In fase di build il metodo viene cifrato con la password C0re-Pr0tect!on-K3y. In fase di esecuzione la BVM chiede quella password all’applicazione attraverso un hook get password prima di poter eseguire il metodo. Invece di restituire un valore scritto nel codice, l’hook convalida la licenza e legge la password da lì:

[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() usa la chiave pubblica RSA incorporata per verificare la firma della licenza e applica tutte le restrizioni, compreso il vincolo al computer:

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

Emettere la licenza

LicenseGenerator, lo strumento del produttore, firma una licenza vincolata all’assembly SecureApp e al computer corrente, che contiene la password del codice in un campo cifrato. La password quindi non compare mai in chiaro, né nel file binario né nel file di licenza:

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 e la chiave privata RSA (keys.pem) hanno solo scopo dimostrativo. In produzione la licenza è emessa dal produttore, oppure dal Babel Licensing Service, e la chiave privata è conservata offline. Con l’applicazione viene distribuita solo la chiave pubblica.

Eseguire l’esempio

# 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

Con una licenza valida il metodo protetto viene eseguito:

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.

Se rimuovi o manometti SecureApp.lic, oppure esegui l’applicazione su un altro computer, il risultato non è soltanto un messaggio di errore più cortese, perché il codice cifrato non può materialmente essere eseguito:

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

Perché è più robusto

Poiché la password proviene da una licenza firmata, un attaccante che elimina dall’assembly il codice di convalida non ottiene nulla: la BVM continua a non avere la password per l’origine core, quindi Secret.Compute resta un blob cifrato e opaco. La firma impedisce di falsificare una licenza, e il vincolo al computer impedisce di riutilizzarne una legittima su un altro computer. La stessa tecnica è alla base dell’esempio Licenze basate sulle funzionalità e dei Modelli di licenza ospitati dal produttore, in cui il campo cifrato è fornito dal Babel Licensing Service anziché da un generatore locale.

Last updated on