Enigma.LicenseManager 1.2.0

dotnet add package Enigma.LicenseManager --version 1.2.0
                    
NuGet\Install-Package Enigma.LicenseManager -Version 1.2.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Enigma.LicenseManager" Version="1.2.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Enigma.LicenseManager" Version="1.2.0" />
                    
Directory.Packages.props
<PackageReference Include="Enigma.LicenseManager" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Enigma.LicenseManager --version 1.2.0
                    
#r "nuget: Enigma.LicenseManager, 1.2.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Enigma.LicenseManager@1.2.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Enigma.LicenseManager&version=1.2.0
                    
Install as a Cake Addin
#tool nuget:?package=Enigma.LicenseManager&version=1.2.0
                    
Install as a Cake Tool

Enigma.LicenseManager

NuGet License: MIT

Enigma.LicenseManager is a comprehensive .NET library designed for secure license management in applications. It provides robust cryptographic protection using both traditional RSA and modern ML-DSA (FIPS 204) digital signature algorithms, ensuring your software licensing is both secure and future-proof.

What's new in 1.2.0 — a production-readiness release: Enigma.Cryptography upgraded to 5.0.0, Central Package Management adopted, a non-Avalonia dependency refresh, the test suite migrated to xUnit v3, and build configuration consolidated. No public API or behavioural change — see the release notes.

Features

  • Dual Cryptographic Support: Choose between RSA and ML-DSA (post-quantum) signatures
  • Flexible License Management: Create, validate, and manage licenses with customizable properties
  • Device Binding: Lock licenses to specific machines using hardware-derived device identifiers
  • Cross-Platform Compatibility: Supports .NET Standard 2.0 and .NET 8.0+
  • JSON Serialization: Easy license storage and distribution in JSON format
  • Product Version Matching: Support for wildcard patterns in product IDs (e.g., MyApp 1.*)
  • Expiration Handling: Built-in support for time-based license expiration
  • Thread-Safe Service: LicenseService is fully thread-safe for concurrent access

Installation

dotnet add package Enigma.LicenseManager

License Creation

Create an RSA-Signed License

Generate a license with RSA digital signature for traditional cryptographic security:

await using var privateKeyFile = new FileStream("<YourKeyFile.pem>", FileMode.Open, FileAccess.Read);
var privateKey = PemUtils.LoadPrivateKey(privateKeyFile, "<KeyPassword>");

var license = new LicenseBuilder()
    .SetProductId("MyApp 1.*")
    .SetExpirationDate(DateTime.UtcNow.AddDays(365))
    .SetOwner("Acme Corp")
    .SignWithRsa(privateKey)
    .Build();

Create an ML-DSA-Signed License

Generate a license with ML-DSA (post-quantum) signature for enhanced future security:

await using var privateKeyFile = new FileStream("<YourKeyFile.pem>", FileMode.Open, FileAccess.Read);
var privateKey = PemUtils.LoadPrivateKey(privateKeyFile, "<KeyPassword>");

var license = new LicenseBuilder()
    .SetProductId("MyApp")
    .SignWithMlDsa(privateKey)
    .Build();

Create a Device-Bound License

Bind a license to a specific machine so it cannot be used elsewhere:

// On the target machine, generate the device ID
var deviceId = LicenseUtils.GenerateDeviceId();

// When creating the license, bind it to that device
var license = new LicenseBuilder()
    .SetProductId("MyApp 1.*")
    .SetDeviceId(deviceId)
    .SetExpirationDate(DateTime.UtcNow.AddDays(365))
    .SignWithRsa(privateKey)
    .Build();

Id defaults to a ULID and CreationDate defaults to DateTime.UtcNow when not explicitly set.

License Persistence

Save License to JSON

Export your generated license to a JSON file for distribution:

await using var fs = new FileStream("<DestinationPath>", FileMode.Create, FileAccess.Write);
await license.SaveAsync(fs);

Load License from JSON

Import a license from a JSON file for validation:

await using var fs = new FileStream("<LicenseFilePath>", FileMode.Open, FileAccess.Read);
var license = await License.LoadAsync(fs);

Both SaveAsync and LoadAsync accept an optional CancellationToken parameter.

License Validation

Verify a Single License

Validate a license against its public key. IsValid returns a (bool, string?) tuple with an error message on failure:

await using var publicKeyFile = new FileStream("<YourKeyFile.pem>", FileMode.Open, FileAccess.Read);
var publicKey = PemUtils.LoadKey(publicKeyFile);

var service = new LicenseService();
var (isValid, errorMessage) = service.IsValid(license, publicKey, "MyApp 1.0");

if (!isValid)
    Console.WriteLine($"License rejected: {errorMessage}");

For device-bound licenses, pass the device ID:

var deviceId = LicenseUtils.GenerateDeviceId();
var (isValid, errorMessage) = service.IsValid(license, publicKey, "MyApp 1.0", deviceId);

Using LicenseService

LicenseService maintains an in-memory, thread-safe collection of licenses. Register licenses once, then check validity by product ID throughout your application:

var service = new LicenseService();

// Register licenses with their public keys
service.AddLicense(license, publicKey);

// Check if any registered license is valid for a product
if (service.HasValidLicense("MyApp 1.0"))
{
    // Feature is licensed
}

// For device-bound licenses
var deviceId = LicenseUtils.GenerateDeviceId();
if (service.HasValidLicense("MyApp 1.0", deviceId))
{
    // Feature is licensed for this device
}

// Remove a license when no longer needed
service.RemoveLicense(license);

Tooling

Desktop app

Enigma License Manager is an Avalonia desktop application (src/Enigma.LicenseManager.Desktop/) for working with keys and licenses through a GUI, without writing code. It provides three pages:

  • Generate Keys — generate an RSA (2048/3072/4096/8192) or ML-DSA (level 87) key pair and save the public / private keys to PEM, optionally encrypting the private key with a password.
  • Generate Licenses — build and sign a license (product ID, owner, optional device binding and expiration) with an RSA or ML-DSA private key, and save it as JSON. Reusable profiles save/load the form fields to a .json file (the signing key password is never stored).
  • Validate Licenses — validate a license file against a public key, optionally constraining the product ID and device ID.

A runtime light / dark theme toggle is persisted per user. Run it with:

dotnet run --project src/Enigma.LicenseManager.Desktop

The GUI's key/license operations are backed by the shared Enigma.LicenseManager.Tools library (src/Enigma.LicenseManager.Tools/), which wraps the core library's signing/verification with key generation, PEM I/O, and RSA-vs-ML-DSA dispatch behind DI-registered services (AddLicenseTools()).

Command-line tool

enigma-license (src/Enigma.LicenseManager.Cli/) runs the same key and license operations as the desktop app from a terminal or CI pipeline. Run it with:

dotnet run --project src/Enigma.LicenseManager.Cli -- <command>

The examples below use enigma-license as shorthand for dotnet run --project src/Enigma.LicenseManager.Cli --.

Full round-trip — generate a key pair, sign a license, validate it:

mkdir -p keys

# 1. Generate an RSA key pair (private key encrypted with a password)
enigma-license keygen --algorithm rsa --rsa-size 3072 \
  --public keys/public.pem --private keys/private.pem --password s3cret

# 2. Sign a license with the private key
enigma-license license generate --product-id "MyApp 1.*" --owner "Acme Corp" \
  --expires 2027-01-31 --algorithm rsa --key keys/private.pem --password s3cret \
  --out app.license.json

# 3. Validate it with the public key
enigma-license license validate --license app.license.json \
  --public-key keys/public.pem --product-id "MyApp 1.2.3"
# License is VALID.

keygen writes to the paths you give it and does not create parent directories, so create the output directory first (mkdir -p keys) or use paths in the current directory.

Generate keys
mkdir -p keys

# RSA key pair (--rsa-size is one of 2048/3072/4096/8192, default 3072; RSA only)
enigma-license keygen --algorithm rsa --rsa-size 4096 \
  --public keys/public.pem --private keys/private.pem

# ML-DSA key pair (post-quantum, fixed to level 87)
enigma-license keygen --algorithm ml-dsa \
  --public keys/public.pem --private keys/private.pem

# Encrypt the private key with a password
enigma-license keygen --algorithm rsa \
  --public keys/public.pem --private keys/private.pem --password s3cret
Generate a license
enigma-license license generate \
  --product-id "MyApp 1.*" \
  --owner "Acme Corp" \
  --device-id "abc123" \
  --expires 2027-01-31 \
  --algorithm rsa --key keys/private.pem --password s3cret \
  --out app.license.json

--owner, --device-id and --expires are optional; omitting --expires means the license never expires. --algorithm must match the private key. Drop --password for an unencrypted key.

Validate a license
# Valid — product and device match → exit 0
enigma-license license validate \
  --license app.license.json --public-key keys/public.pem \
  --product-id "MyApp 1.2.3" --device-id "abc123"
# License is VALID.

# Invalid — product id mismatch → exit 1
enigma-license license validate \
  --license app.license.json --public-key keys/public.pem \
  --product-id "OtherApp 1.0"
# License is INVALID: Product id mismatch. (License productId: MyApp 1.*, requested productId: OtherApp 1.0)

--product-id and --device-id are optional; omit --product-id to validate against the license's own product id. A wrong public key or a device-id mismatch is also reported as invalid (exit 1).

Exit codes
Code Meaning
0 Success / license valid
1 License invalid
2 Operation error (bad password, missing file — message on stderr)

Exit codes make the tool scriptable in CI: enigma-license license validate ... && ./deploy.sh.

Every command supports --help; enigma-license --version prints the tool version.

Project Structure

Path Description
src/Enigma.LicenseManager/ Core library
src/Enigma.LicenseManager.Tools/ Shared key / license operations (used by the desktop app)
src/Enigma.LicenseManager.Desktop/ Avalonia desktop application
src/Enigma.LicenseManager.Cli/ Command-line tool (enigma-license)
src/UnitTests/ Unit tests
Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.2.0 70 7/12/2026
1.1.0 273 3/16/2026
1.0.0 364 10/8/2025

v1.2.0 — production-readiness release. Highlights: Enigma.Cryptography upgraded to 5.0.0, Central Package Management adopted, a non-Avalonia dependency refresh, the test suite migrated to xUnit v3, and build settings consolidated into Directory.Build.props + .editorconfig. No public API or behavioural change — a recompile-and-verify upgrade; licenses and keys created with earlier 1.x releases remain compatible. See RELEASENOTES.md for the full details.