[Specify your license information here] A Windows desktop tool for generating licenses compatible with the Standard.Licensing library. The application lets you configure all available license options, sign them with your private key and save the result for distribution.
- Create Standard, Trial license types
- Set product name, version, and expiration date
- Store customer information (name and email)
- Add any additional attributes using JSON
- Sign licenses using your own RSA private key
- Save generated licenses to a file
- Generate RSA key pairs for license signing
- Copy license text to clipboard
- Password protection for private keys
- Support for both PEM and XML key formats
- Built-in help screen describing how to use the tool
The UI is designed with sensible defaults and labeled inputs so generating a license only requires filling in the desired fields and selecting your private key.
- Start the application.
- Enter your product details and customer information.
- Select the desired license type (Standard or Trial):
- Standard: Full license with custom customer information
- Trial: Pre-configured with trial defaults and 30-day expiration
- Set an expiration date (required).
- Optionally add extra attributes in JSON format (e.g.
{ "Seats": "5" }). - Browse to your private key file. PEM keys are recommended, but existing XML keys are also supported.
- If your key is password-protected, enter the password.
- Click Generate License to view the resulting license text.
- Use File → Save License to store the license in a
.licfile, or copy to clipboard using the Copy button.
- Click Generate Key Pair in the main window.
- Select your desired key size (2048 or 4096 bits recommended).
- Optionally enter a password to protect your private key.
- Click Generate Key Pair button.
- Save both private and public keys using the respective buttons.
- Optionally, use Insert Private Key to automatically use the generated key in the main window.
- F1: Open help window
- Esc: Exit application (with confirmation)
The generated license can then be validated in your application using the matching public key with the Standard.Licensing library.
- Open the solution file
StandardLicensingGenerator.slnin Visual Studio 2022 or newer. - Select the desired build configuration (Debug/Release).
- Build the solution using Build > Build Solution or press Ctrl+Shift+B.
- The compiled application will be available in the
bin/{Configuration}/net9.0-windowsdirectory.
- Open the solution file
StandardLicensingGenerator.slnin Rider. - Select the desired build configuration from the dropdown in the toolbar.
- Build the solution by clicking the build icon or pressing Ctrl+F9.
- The compiled application will be available in the
bin/{Configuration}/net9.0-windowsdirectory.
- Open a terminal or command prompt in the project directory.
- Run the following command to build the application:
dotnet build -c Release - To run the application directly:
dotnet run -c Release
To create a self-contained single-file executable for distribution:
dotnet publish -c Release -r win-x64 --self-contained true /p:PublishSingleFile=true
The resulting executable will be available in the bin/Release/net9.0-windows/win-x64/publish directory.
This guide describes how to incorporate license validation, import, updating, and management features using the output from StandardLicensingGenerator and the Standard.Licensing library.
To ensure a user holds a valid license for your application:
- Load the license file (e.g.
license.lic) at application startup or before accessing protected features. - Validate the license signature using your embedded or distributed public key.
- Check license properties: expiration dates, allowed features, seat counts, or custom attributes.
Example code using Standard.Licensing library:
// Include the Standard.Licensing NuGet package in your project
using Standard.Licensing;
using System;
using System.IO;
// Load the license file
string licenseText = File.ReadAllText("path/to/license.lic");
var license = License.Load(licenseText);
// Load your public key (from file or embedded resource)
string publicKey = File.ReadAllText("path/to/public_key.pem");
// Verify the license signature
if (!license.VerifySignature(publicKey))
{
// Invalid license
throw new UnauthorizedAccessException("License verification failed.");
}
// Check license expiration
if (license.Expiration < DateTime.Now)
{
// License has expired
throw new Exception("License has expired.");
}
// Check license type
if (license.Type == LicenseType.Trial)
{
// Handle trial license differently if needed
Console.WriteLine("Running in trial mode");
}
// Access customer information
string customerName = license.Customer.Name;
string customerEmail = license.Customer.Email;
// Access custom attributes
if (license.AdditionalAttributes.ContainsKey("Seats"))
{
string seats = license.AdditionalAttributes["Seats"];
Console.WriteLine($"Licensed for {seats} seats");
}Allow users to import their license file into your software:
- Provide a UI for selecting or drag-and-dropping a license file.
- Store the selected license, for example in the user's application data folder.
- Trigger validation immediately after import.
Example WPF implementation:
private void ImportLicense_Click(object sender, RoutedEventArgs e)
{
var dialog = new OpenFileDialog
{
Filter = "License Files (*.lic)|*.lic|All Files (*.*)|*.*",
Title = "Import License File"
};
if (dialog.ShowDialog() == true)
{
try
{
// Read the license file
string licenseText = File.ReadAllText(dialog.FileName);
var license = License.Load(licenseText);
// Validate the license (using public key from resources or settings)
string publicKey = GetPublicKeyFromResources();
if (!license.VerifySignature(publicKey))
{
MessageBox.Show("The license signature is invalid.", "Invalid License",
MessageBoxButton.OK, MessageBoxImage.Error);
return;
}
// Check if the license is expired
if (license.Expiration < DateTime.Now)
{
MessageBox.Show("This license has expired.", "Expired License",
MessageBoxButton.OK, MessageBoxImage.Warning);
// Optionally still allow using an expired license
}
// Save the license to application data folder
string appDataPath = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
"YourCompany", "YourApp");
Directory.CreateDirectory(appDataPath); // Ensure directory exists
File.WriteAllText(Path.Combine(appDataPath, "license.lic"), licenseText);
// Show license details to user
MessageBox.Show($"License imported successfully\n\n" +
$"Licensed to: {license.Customer.Name}\n" +
$"Type: {license.Type}\n" +
$"Expires: {license.Expiration:d}",
"License Imported", MessageBoxButton.OK, MessageBoxImage.Information);
// Apply the license to your application (e.g., unlock features)
ApplyLicense(license);
}
catch (Exception ex)
{
MessageBox.Show($"Failed to import license: {ex.Message}", "Error",
MessageBoxButton.OK, MessageBoxImage.Error);
}
}
}
private string GetPublicKeyFromResources()
{
// Load the public key from embedded resources
using var stream = GetType().Assembly.GetManifestResourceStream("YourNamespace.public_key.pem");
if (stream == null) throw new FileNotFoundException("Public key resource not found");
using var reader = new StreamReader(stream);
return reader.ReadToEnd();
}To support changes such as upgrading, extending, or modifying user licenses:
- Allow users to replace their license by importing a new file.
- Overwrite the previously stored license.
- Offer a menu or command for "Update License."
- Display relevant license status (valid/expired/type) in your application's settings or about dialog.
Example implementation for license status display:
private void UpdateLicenseStatus()
{
try
{
// Get path to stored license file
string appDataPath = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
"YourCompany", "YourApp");
string licensePath = Path.Combine(appDataPath, "license.lic");
if (!File.Exists(licensePath))
{
// No license found
LicenseStatusTextBlock.Text = "No License";
LicenseTypeTextBlock.Text = "Free Version";
LicenseExpirationTextBlock.Text = "N/A";
return;
}
// Load and validate the license
string licenseText = File.ReadAllText(licensePath);
var license = License.Load(licenseText);
string publicKey = GetPublicKeyFromResources();
bool isValid = license.VerifySignature(publicKey);
bool isExpired = license.Expiration < DateTime.Now;
// Update UI elements
LicenseStatusTextBlock.Text = isValid ? (isExpired ? "Expired" : "Valid") : "Invalid";
LicenseTypeTextBlock.Text = license.Type.ToString();
LicenseExpirationTextBlock.Text = license.Expiration.ToString("d");
LicenseCustomerTextBlock.Text = license.Customer.Name;
// Optionally show more details for custom attributes
if (license.AdditionalAttributes.Count > 0)
{
LicenseAttributesPanel.Visibility = Visibility.Visible;
LicenseAttributesTextBlock.Text = string.Join("\n",
license.AdditionalAttributes.Select(a => $"{a.Key}: {a.Value}"));
}
else
{
LicenseAttributesPanel.Visibility = Visibility.Collapsed;
}
}
catch (Exception ex)
{
// Handle errors reading or parsing the license
LicenseStatusTextBlock.Text = "Error";
LicenseTypeTextBlock.Text = "Unknown";
LicenseExpirationTextBlock.Text = "Unknown";
// Log the error
Console.WriteLine($"Error updating license status: {ex.Message}");
}
}- Valid License: Grant access to features as described by the license attributes.
- Expired or Invalid License: Restrict access or show an appropriate message.
- No License: Prompt the user to enter/import a valid license.
- Always perform license validation on application start and before providing critical functionality.
- Protect your public key: embed it in your application resources, not as plain text or in a writable location.
- Never ship your private key with your distribution.
- Consider implementing obfuscation to protect your license validation code.
- For increased security, implement a time-limited license cache to reduce the frequency of file access.
- Implement tamper detection to identify if license files have been modified.
- Consider adding a hardware identifier to tie licenses to specific machines.
Example of embedding a public key as a resource:
- Add your public key file to your project.
- Set its build action to "Embedded Resource".
- Access it in code:
private string GetEmbeddedPublicKey()
{
var assembly = Assembly.GetExecutingAssembly();
var resourceName = "YourNamespace.public_key.pem";
using var stream = assembly.GetManifestResourceStream(resourceName);
if (stream == null) throw new FileNotFoundException("Public key resource not found");
using var reader = new StreamReader(stream);
return reader.ReadToEnd();
}The StandardLicensingGenerator allows you to add custom attributes to your licenses through the JSON attributes field. These attributes can be used to control various aspects of your application:
- Feature Flags: Enable/disable specific features based on license tier
- Usage Limits: Set maximum allowed resources (users, projects, etc.)
- Custom Properties: Store any license-specific configuration
Example JSON attributes for different scenarios:
- Enterprise Edition with module access
{
"Edition": "Enterprise",
"MaxUsers": "Unlimited",
"Modules": ["Reporting", "Administration", "Integration"]
}- Professional Edition with limits
{
"Edition": "Professional",
"MaxUsers": "10",
"Modules": ["Reporting", "Administration"],
"StorageLimit": "50GB"
}- Basic Edition
{
"Edition": "Basic",
"MaxUsers": "3",
"Modules": ["Reporting"],
"StorageLimit": "5GB"
}Accessing these attributes in code:
// Check edition
string edition = license.AdditionalAttributes["Edition"];
// Check if a module is available
string[] modules = license.AdditionalAttributes["Modules"]
.TrimStart('[').TrimEnd(']')
.Split(',', StringSplitOptions.RemoveEmptyEntries)
.Select(m => m.Trim('"', ' '))
.ToArray();
if (modules.Contains("Reporting"))
{
// Enable reporting functionality
}
// Check numeric limits
if (int.TryParse(license.AdditionalAttributes["MaxUsers"], out int maxUsers))
{
// Apply user limit
}To implement license revocation and renewal in your application:
- Let users load a replacement license file with new terms or updated validity.
- When a new license is imported, verify it's for the same customer/product.
- Overwrite the previous license file with the new one.
For applications with internet access, you can implement server-side license validation:
public async Task<bool> ValidateLicenseOnline(License license)
{
try
{
// Create HTTP client (consider using a cached/singleton instance)
using var client = new HttpClient();
// Prepare request with license ID
var request = new HttpRequestMessage(HttpMethod.Get,
$"https://your-license-server.com/api/validate/{license.Id}");
// Add authentication if needed
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "your-api-key");
// Send request
var response = await client.SendAsync(request);
// Check if license is valid
if (response.IsSuccessStatusCode)
{
var result = await response.Content.ReadFromJsonAsync<LicenseValidationResult>();
return result?.IsValid == true && !result.IsRevoked;
}
return false;
}
catch (Exception ex)
{
// Handle network errors - typically fail open if server is unreachable
// Log the error
Console.WriteLine($"Online validation error: {ex.Message}");
// Return true to allow offline usage, or false for stricter validation
return true; // Allow offline usage
}
}
// Model for server response
public class LicenseValidationResult
{
public bool IsValid { get; set; }
public bool IsRevoked { get; set; }
public DateTime? ExpirationDate { get; set; }
public string? Message { get; set; }
}For applications without consistent internet access:
public class RevocationList
{
private HashSet<Guid> _revokedLicenseIds = new();
private readonly string _revocationListPath;
private DateTime _lastUpdateCheck = DateTime.MinValue;
public RevocationList(string revocationListPath)
{
_revocationListPath = revocationListPath;
LoadRevocationList();
}
private void LoadRevocationList()
{
if (File.Exists(_revocationListPath))
{
try
{
string[] lines = File.ReadAllLines(_revocationListPath);
_revokedLicenseIds = new HashSet<Guid>(
lines.Select(line => Guid.Parse(line.Trim()))
);
}
catch (Exception ex)
{
// Log error reading revocation list
Console.WriteLine($"Error loading revocation list: {ex.Message}");
}
}
}
public bool IsRevoked(Guid licenseId)
{
// Check if we should try to update the list (e.g., once per day)
if (DateTime.Now - _lastUpdateCheck > TimeSpan.FromDays(1))
{
_lastUpdateCheck = DateTime.Now;
UpdateRevocationListAsync().ConfigureAwait(false);
}
return _revokedLicenseIds.Contains(licenseId);
}
private async Task UpdateRevocationListAsync()
{
try
{
using var client = new HttpClient();
var response = await client.GetAsync("https://your-license-server.com/api/revoked-licenses");
if (response.IsSuccessStatusCode)
{
var revocationData = await response.Content.ReadAsStringAsync();
File.WriteAllText(_revocationListPath, revocationData);
LoadRevocationList(); // Reload after update
}
}
catch (Exception ex)
{
// Log error but continue with existing list
Console.WriteLine($"Error updating revocation list: {ex.Message}");
}
}
}The StandardLicensingGenerator targets .NET 9.0 for Windows and can be distributed in several ways:
Create a standalone executable that doesn't require .NET installation:
dotnet publish -c Release -r win-x64 --self-contained true /p:PublishSingleFile=true /p:IncludeNativeLibrariesForSelfExtract=trueFor users who already have .NET installed (smaller download size):
dotnet publish -c Release -r win-x64 --self-contained false /p:PublishSingleFile=trueFor automatic updates using Visual Studio:
- Right-click the project in Solution Explorer
- Select Publish
- Choose ClickOnce as the publish target
- Configure your settings and publish location
- Windows 10/11 or Windows Server 2016 or later
- If using framework-dependent deployment: .NET 9.0 Runtime
- No special hardware requirements
A help window is available from the Help menu inside the application. It provides a short overview of the workflow and explains where to place your private/public keys.
Press F1 at any time to access the help window.

