ライセンスに紐付けたコード暗号化
コード暗号化のパスワードをライセンス自体に格納することで、保護された機能を、暗号の仕組みによって有効なライセンスに依存させます。
if (!IsLicensed) return; のような真偽値によるライセンスチェックは、パッチで簡単に取り除けます。このサンプルは、より強力なパターンを示します。機密性の高いメソッドを Babel Obfuscator のコード暗号化で暗号化し、Babel Virtual Machine(BVM)がその実行に必要とするパスワードを、バイナリには一切含めません。パスワードは、それ自体が暗号化された状態で、署名され、ハードウェアに紐付けた Babel ライセンスの中にあります。ライセンスチェックを取り除くだけでは不十分です。有効なライセンスがなければパスワードはなく、パスワードがなければ、暗号化されたメソッドを復元することも実行することもできません。
コード例
git clone https://github.com/babelfornet/license-bound-encryption-console-example.gitこのプロジェクトは、2 つの部分からなるクロスプラットフォームの .NET コンソールアプリケーションです。SecureApp は、機密性の高いメソッドがコード暗号化されている、ライセンス対象のアプリケーションです。LicenseGenerator は、署名付きライセンスを発行するベンダー側のツールです。Babel パッケージのバージョンは Directory.Build.props で一元管理されているため、サンプルはお使いのフィードに合ったバージョンを対象にできます。
保護されたメソッド
保護する知的財産は、1 つのメソッドです。[Obfuscation] 属性は、ビルド時のパスワードを使ってメソッド本体を暗号化し、core というソース名でアセンブリ内に格納する(internal=true)よう Babel に指示します。
[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;
}コード暗号化は Release ビルドでのみ有効になり、SecureApp.csproj の MSBuild プロパティによって、対象は機密性の高いクラスに限定されます。
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<MsilEncryption Condition="'$(MsilEncryption)' == ''">SecureApp\.Secret::.*</MsilEncryption>
<BabelWarningsAsErrors>W00000</BabelWarningsAsErrors>
</PropertyGroup>コード暗号化は Release 構成でのみ実行されます。BabelWarningsAsErrors の行は、評価モードの警告 W00000 をエラーに変えるため、保護されていないアセンブリが誤って出荷されることはありません。
パスワードの保管場所
ビルド時に、メソッドはパスワード C0re-Pr0tect!on-K3y で暗号化されます。実行時に、BVM はメソッドを実行する前に、「get password」フックを通じてアプリケーションにそのパスワードを要求します。このフックは、ハードコードされた値を返す代わりに、ライセンスを検証し、そこからパスワードを読み取ります。
[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() は、埋め込まれた RSA 公開キーを使ってライセンスの署名を検証し、ハードウェアへの紐付けを含むすべての制限を適用します。
public static ILicense Validate()
{
var manager = new StringLicenseManager
{
SignatureProvider = RSASignature.FromKeys(PublicKey)
};
return manager.Validate(File.ReadAllText(LicenseFilePath), typeof(Program));
}ライセンスの発行
ベンダー側の LicenseGenerator は、SecureApp アセンブリと現在のコンピューターに紐付けたライセンスに署名し、コードのパスワードを暗号化されたフィールドとして格納します。そのため、パスワードが平文で現れることはありません。バイナリにも、ライセンスファイルにさえも含まれません。
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 と RSA 秘密キー(keys.pem)は、デモ専用です。本番環境では、ライセンスはベンダー、または Babel Licensing Service が発行し、秘密キーはオフラインで保管します。アプリケーションと一緒に配布するのは公開キーだけです。
実行
# 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有効なライセンスがあれば、保護されたメソッドが実行されます。
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.SecureApp.lic を削除または改ざんしたり、別のコンピューターで実行したりすると、わかりやすいエラーメッセージが表示されるだけではありません。暗号化されたコードは、物理的に実行できなくなります。
License INVALID : Invalid license signature
Protected computation blocked: Invalid license signature
Without a valid license the encrypted code cannot be decrypted or executed.この方法がより強力な理由
パスワードは署名付きライセンスから得られるため、攻撃者がアセンブリから検証コードを取り除いても、何も得られません。BVM には依然として core ソースのパスワードがないため、Secret.Compute は、中身のわからない暗号化されたデータの塊のままです。署名はライセンスの偽造を防ぎ、ハードウェアへの紐付けは、正規のライセンスを別のコンピューターで再利用することを防ぎます。同じ手法は、機能ベースのライセンスのサンプルと、ベンダーがホストするライセンステンプレートの基盤にもなっています。後者では、暗号化されたフィールドを、ローカルのジェネレーターではなく Babel Licensing Service が提供します。