Cifratura del codice legata alla licenza
La cifratura del codice legata alla licenza è una variante del codice protetto da password in cui la password che sblocca un metodo cifrato non è una costante fissa inserita nella build, ma un valore letto dalla licenza del cliente in fase di esecuzione.
Il metodo protetto viene comunque distribuito all’interno dell’assembly offuscato, ma il suo codice resta cifrato finché una licenza valida non fornisce la password corretta. Una funzionalità sensibile o premium viene quindi eseguita solo per gli utenti la cui licenza la autorizza, mentre il resto dell’applicazione continua a funzionare normalmente per tutti gli altri.
È lo schema che Babel Obfuscator usa internamente per riservare le proprie funzionalità con licenza. Si aggiunge alla normale cifratura del codice che già conosci: non c’è nulla di nuovo da installare, solo una regola e un piccolo hook da scrivere.
Come funziona
- Contrassegna il codice da proteggere. Una regola XML (o l’attributo
Obfuscation) seleziona i metodi o i tipi da cifrare e assegna loro un nome di origine (source) e una password, mantenendo il codice cifrato all’interno dell’assembly (Internal = true). - Fornisci un hook della password. Un metodo contrassegnato con
[Obfuscation(Feature = "msil encryption get password")]riceve il nome dell’origine e restituisce la password di quell’origine leggendo un campo della licenza caricata. - Esegui. Alla prima chiamata del metodo protetto, la Babel Virtual Machine (BVM) richiama l’hook per ottenere la password dell’origine. Se la licenza contiene il campo previsto, il metodo viene decifrato ed eseguito; se il campo manca o il valore è errato, la decifratura fallisce e il metodo non può essere eseguito: la funzionalità resta di fatto bloccata.
Solo i metodi selezionati da una regola con un <Source> sono legati alla licenza. I metodi cifrati con il semplice --msilencryption (senza origine) vengono sbloccati dall’assembly stesso e continuano a funzionare senza licenza, quindi l’applicazione senza licenza continua a funzionare: restano escluse solo le funzionalità subordinate alla licenza.
Cifrare il codice
Aggiungi una regola che cifra il codice sensibile e lo associa a un’origine con nome e a una password:
<Rule name="encrypt premium" feature="msil encryption" exclude="false">
<Target>Methods</Target>
<Pattern>ACME.Premium::*</Pattern>
<Properties>
<Source>premium</Source>
<Password>Xa9!k2$LpQ7r</Password>
<Internal>true</Internal>
</Properties>
<Description>Encrypt the premium feature, unlocked by the license.</Description>
</Rule>Puoi esprimere la stessa cosa inline con l’attributo Obfuscation:
[Obfuscation(Feature = "msil encryption:internal=true;source=premium;password=Xa9!k2$LpQ7r", Exclude = false)]
internal class Premium
{
public string Run() => "premium result";
}La <Password> che imposti in fase di build deve corrispondere al valore che l’hook della password restituisce per quell’origine in fase di esecuzione. In una soluzione legata alla licenza l’hook legge quel valore dalla licenza, quindi memorizza lo stesso valore nel campo della licenza che emetti per i tuoi clienti.
Leggere la password dalla licenza
Dichiara un unico hook che associa un nome di origine alla password contenuta nella licenza. L’esempio seguente usa Babel Licensing per cercare una funzionalità con licenza e restituirne il valore:
[Obfuscation(Feature = "msil encryption get password")]
internal static string GetSourcePassword(string source)
{
var license = LicenseManager.Instance.License;
var feature = license.Features.FirstOrDefault(f => f.Name == source);
if (feature == null)
throw new ApplicationException($"feature '{source}' is not licensed");
return Encoding.UTF8.GetString(feature.Data);
}In fase di esecuzione la BVM chiama GetSourcePassword("premium"); l’hook restituisce la password memorizzata nella licenza del cliente, e il metodo premium viene decifrato ed eseguito.
L’hook deve essere raggiungibile dal codice che Babel inietta, quindi dichiaralo public o internal. Se lo lasci private o protected, durante l’offuscamento Babel ne estende automaticamente la visibilità a internal (non viene mai reso pubblico), così il codice iniettato può chiamarlo.
Per un’implementazione completa ed eseguibile, che comprende la classe LicenseManager e il modo di emettere licenze per edizione, vedi l’esempio Licenze basate sulle funzionalità.
Funziona con ogni modalità di consegna della licenza
Poiché l’hook si limita a leggere dall’oggetto licenza caricato, la cifratura del codice legata alla licenza non dipende da come quella licenza arriva all’applicazione. Funziona senza modifiche con tutte le modalità di consegna di Babel Licensing:
- File di licenza: un file di licenza firmato, distribuito con l’applicazione o scaricato sul client.
- Licenza flottante: postazioni concesse temporaneamente da un Babel Licensing Service.
- Attivazione della licenza: una licenza attivata e vincolata al computer.
Qualunque modalità tu scelga, una volta che il tuo codice di gestione delle licenze ha convalidato e caricato la licenza, l’hook della password può leggere il campo che gli serve e la funzionalità protetta si sblocca.
Preferisci tenere il codice cifrato del tutto fuori dall’assembly principale? Combina questo schema con i File di codice esterni per distribuire il metodo cifrato insieme alla licenza.