Skip to content

12noonLLC/LicenseManagerX

Repository files navigation

License Manager X by 12noon LLC

build GitHub last commit

NuGet Version NuGet Downloads

Description

This project ensures that software licenses are securely generated and validated, providing a robust mechanism for software protection.

Secret Model

  • .private files contain secret material (private key + passphrase context) and must be protected.
  • .lic files are intended for distribution to licensees.
  • Product ID and public key are not secrets.

For this repository, the .private file inside LicenseManagerX_Example is intentionally included as an inspectable example only. Do not treat that as a general practice for real applications.

License Manager X is a graphical front-end application designed to create and manage licenses for software applications using .NET. It leverages the Standard.Licensing project to handle license generation and validation.

In addition to this Windows application, License Manager X can also be used from a command line to support scripting, etc.

BREAKING CHANGE

Version 3.0.0 completely changes the command-line switches to a command tree design. See below for details.

Your application will need to use either:

  • the LicenseManager_12noon.Client NuGet package, which has an improved API to validate licenses for your .NET application.
  • the original Standard.Licensing NuGet package on which it is based.

You can change which package you use at any time--you are not locked in to one or the other.

Download the License Manager X application from the Microsoft Store.

License Manager X

Features

Key Management

Property Usage
Passphrase Secret used to generate public/private keypair and to create a license
Public key Used by the licensed application to validate the license
ID License ID (You can use it any way you want or not at all)
Product ID Used by the licensed application to verify the executable and public key.
Lock to assembly This ensures the license is associated ONLY with THIS build of the licensed application.

The application maintains the private key in the .private file but does not display it.

Product

Property Usage
Name The product name
Version The product version
Date published The date the product was published

These values can be displayed by the licensed application.

The publish date can represent any date you want.

Product Features

You can add custom product features to your license by specifying them in the key=value format. These features allow you to define additional properties or functionality for your product.

  1. In the Product features field, enter your custom feature in the key=value format.
  2. Add as many features as needed, each on a new line.
  3. Save the license file to apply the changes.

For example:

Key=Value
Feature1=Enabled
Feature2=False
MaxWidgets=100

The licensed application can then read and use these features as needed.

License

Property Usage
Type Standard or trial license
Expiration Date The date on which the license expires (no time, no time zone info). DateOnly.MaxValue means no expiry.
Expiration Days The number of days until the license expires. Zero means no expiry.
Quantity Minimum value is one (1)

The licensed application can check the type to permit only certain features.

The license expires at 12:00 AM (midnight) local time on the specified date. Stored as a time-zone-agnostic date.

If expiration-days is set to zero, there is no expiry.

The quantity is not enforced.

Expiration Date Semantics

Important: Expiration dates are stored and compared as dates (using local time), not UTC:

  • Storage: The expiration date is stored as a DateOnly value in ISO 8601 format (YYYY-MM-DD in XML files).
  • Interpretation: An expiration date of April 12 means the license expires at 12:00 AM (midnight) local time on April 12.
  • Validation: When validating a license, the current local date is compared against the expiration date.
  • Example: If a license is set to expire on April 12 and the current local time is April 11 at 11:59 PM, the license is still valid. Once the local clock strikes 12:00 AM on April 12, the license is expired.

This approach ensures consistent expiration behavior across different time zones without requiring time zone conversion logic.

Serialization Format

  • New Format: Expiration dates are saved in ISO 8601 format: YYYY-MM-DD (e.g., "2027-12-31")
  • Backwards Compatibility: The system can read expiration dates in the following legacy formats in .private files:
    • Old DateTime string: 12/01/2027 00:00:00
    • RFC1123 format: Sun, 02 Jan 2028 00:00:00 GMT
    • Any other culture-invariant DateTime format

When loading an older .private file with legacy date formats, the expiration date is automatically converted to the new ISO 8601 format when the keypair is next saved.

License Attributes

License attributes can also be added using the key=value format. These attributes allow you to define additional properties for the license.

  1. In the License attributes field, enter your custom attribute in the key=value format.
  2. Add as many attributes as needed, each on a new line.
  3. Save the license file to apply the changes.

For example:

Key=Value
Region=US
SupportLevel=Premium

The licensed application can access these attributes to enforce specific behaviors or display relevant information.

Licensee

This information can be displayed by the licensed application.

Property Usage
Name Name of the licensee
Email Email of the licensee
Company Company of the licensee (optional)

Usage

Create a New License

Note that the public key and product ID are passed by the licensed application to validate the license, so you only want to create a new keypair or change the product ID if you want to change them in the licensed application, rebuild it, and create new licenses for anyone who will use the new build.

  1. Create a keypair by entering a value for Passphrase and pressing Create Keypair button.
  2. Enter a Product ID.
  3. Optionally, lock the license to a specific build of the licensed application. Technically, this could be any file, but one that is unique to a specific build (e.g., an EXE or DLL) is most useful.
  4. Fill in the product information, license information, and licensee information.
  5. Press the Save Keypair... button. This will prompt you for where to save the .private file.
  6. Press the Save License... button. This will prompt you for where to save the .lic file.

The .private file contains all of the information used to create the license, including the secrets. Do keep the .private file somewhere safe. Do NOT add the .private file to source control. You will need it to create more licenses for your licensed application (unless you want to update the application to use a new public key).

The only intentional exception in this repository is the example .private file under LicenseManagerX_Example, which exists only so users can inspect a sample end-to-end setup.

Create a License Based on an Existing License

  1. Press the Load Keypair or License or Both... button to select a .private or .lic file (or both of them). Alternatively, you can drag/drop a .private and/or .lic file.
  2. After loading both files, License Manager X will validate the license file.

If the license is invalid (e.g., it expired or the assembly has changed), you can create a new (valid) license.

  1. Now you can update the product, license, or licensee information as needed.
  2. Press the Save Keypair... button to save the keypair file. This will prompt you for where to save the .private file.
  3. Press the Save License... button to create a new license. This will prompt you for where to save the .lic file.

Command Line Interface

The License Manager X application includes a built-in command-line interface. The same executable can run in both GUI mode (when launched without arguments) and CLI mode (when arguments are provided).

You can create a .private file using the GUI or the command-line interface. You can then use the command-line interface to generate new license files in a build script, etc.

Usage

lmx is the Windows app execution alias for License Manager X. You can manage this in Windows Settings.

lmx version
lmx keypair <create|update|show> ...
lmx license <create|update|show> ...

Command Tree

  • version - Show version information.
  • keypair create <keypair> ... - Create a new .private file.
  • keypair update <keypair> ... - Update allowed properties in an existing .private file.
  • keypair show <keypair> - Display properties from a .private file.
  • license create <keypair> --license <license> ... - Create a new .lic file from a .private file.
  • license update <keypair> --license <license> ... - Update an existing .lic file using a .private file.
  • license show <keypair> --license <license> - Display properties from a .lic file.

Shared Options

These options are supported where applicable on keypair create, keypair update, license create, and license update:

Option Description
--product-version, -pv <version> - Product version
--product-publish-date, -pd <date> - Product publish date in YYYY-MM-DD format
--product-features, -pf <key=value ...> - Product features as space-separated key=value pairs
`--type, -t <Standard Trial>`
--quantity, -q <number> - License quantity (positive integer)
--expiration-days, -dy <days> - Expiration in days (0 means no expiry)
--expiration-date, -dt <date> - Expiration date in YYYY-MM-DD format
--license-attributes, -la <key=value ...> - License attributes as space-separated key=value pairs
--lock <path> - Lock the license to a specific existing file, such as an EXE or DLL

Note: --expiration-days and --expiration-date are mutually exclusive.

Key=Value Format

For --product-features and --license-attributes, use space-separated key=value pairs:

  • Format: "key1=value1 key2=value2 key3=value3"
  • Keys cannot be empty
  • Values can be empty: "Key=" or Key
  • Spaces in values are not supported (use quotes around individual pairs if needed)

Keypair Commands

keypair create <keypair>

Creates a new .private file. The target keypair file must not already exist.

Required options:

Option Description
--passphrase <text> Passphrase used to protect the keypair
--product-id <text> Product ID
--product-name <text> Product name
--licensee-name <text> Licensee name
--licensee-email <text> Licensee email

Optional options:

  • --licensee-company <text> - Licensee company.
  • Any of the shared options listed above.

Example:

> lmx keypair create my.private --passphrase "correct horse battery staple" --product-id MyProductId --product-name "My Product" --licensee-name "Jane User" --licensee-email jane@example.com --licensee-company Contoso --product-version 1.2.3
keypair update <keypair>

Updates an existing .private file. The source keypair file must already exist.

Allowed options:

  • Any of the shared options listed above.

Protected .private properties are intentionally not accepted by keypair update. This command cannot change:

  • Passphrase
  • Public key
  • Private key
  • Product name
  • Licensee name
  • Licensee email
  • Licensee company

Example:

> lmx keypair update my.private --product-version 2.0.0 --product-publish-date 2025-06-15 --product-features Edition=Pro Seats=50
keypair show <keypair>

Displays properties from an existing .private file. The source keypair file must already exist.

License Commands

license create <keypair> --license <license>

Creates a new .lic file from a .private file. The source keypair file must already exist.

Required options:

  • --license <license> - Path to the new .lic file.

Optional options:

  • Any of the shared options listed above.

Example:

> lmx license create my.private --license my.lic --type Standard --quantity 100 --product-version 2.0.0
license update <keypair> --license <license>

Updates an existing .lic file using a .private file. The source keypair and license files must already exist.

Required options:

  • --license <license> - Path to the existing .lic file.

Optional options:

  • Any of the shared options listed above.
license show <keypair> --license <license>

Displays properties from an existing .lic file. The source keypair and license files must already exist.

Security Notes

The CLI cannot override these protected properties from the .private file:

  • Passphrase
  • Public or private keys
  • Product name
  • Customer name, email, or company

Reserved Names:

  • Product features cannot use: Product, Version, Publish Date
  • License attributes cannot use: Product Identity, Assembly Identity, Expiration Days

If the license file already exists, it will not be overwritten and an error will be displayed.

The Licensed Application

Install the LicenseManager_12noon.Client NuGet package in your application.

The licensed application must pass the Product ID and the Public Key to the license validation API.

const string PRODUCT_ID = "My Product ID";	// Copied from the License Manager X application
const string PUBLIC_KEY = "The Public Key";	// Copied from the License Manager X application
LicenseFile license = new();
bool isValid = license.IsLicenseValid(PRODUCT_ID, PUBLIC_KEY, out string messages);
if (!isValid)
{
	// INVALID
	MessageBox.Show("The license is invalid. " + messages);
	return;
}

// VALID
if (license.StandardOrTrial == LicenseType.Trial)
{
	// Example: LIMIT FEATURES FOR TRIAL
}

If the license is valid, you can use any of the properties (e.g., for display or to limit features).

Alternatively, you can use the Standard.Licensing NuGet package to validate the license in your application.

Note: Of course, the hash of Product ID and Public Key will not prevent a determined hacker from working around the license. However, it will prevent a simple text substitution of the public key.

You could also do something more involved, such as prompting the licensee the first time they run the application to enter some secret text (e.g., a password or GUID) and storing a hash of it and the public key in protected storage. Then the application could use the hash as the Product ID. Of course, the licensee would have to keep that text as secret as they should keep the license file.

Example Application

The LicenseManagerX_Example project is an example application to demonstrate how to use the NuGet client library to validate a license and access the license's information.

Note: The .lic (license) files are managed by the Standard.Licensing library and are not directly parsed or modified by License Manager X. Expiration dates in .lic files use UTC format as defined by the Standard.Licensing library.

About

Windows GUI front-end for Standard.Licensing project with optional enhanced API for applications

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors