ARSoft.WinFormsLicensing 1.5.2

dotnet add package ARSoft.WinFormsLicensing --version 1.5.2
                    
NuGet\Install-Package ARSoft.WinFormsLicensing -Version 1.5.2
                    
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="ARSoft.WinFormsLicensing" Version="1.5.2" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="ARSoft.WinFormsLicensing" Version="1.5.2" />
                    
Directory.Packages.props
<PackageReference Include="ARSoft.WinFormsLicensing" />
                    
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 ARSoft.WinFormsLicensing --version 1.5.2
                    
#r "nuget: ARSoft.WinFormsLicensing, 1.5.2"
                    
#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 ARSoft.WinFormsLicensing@1.5.2
                    
#: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=ARSoft.WinFormsLicensing&version=1.5.2
                    
Install as a Cake Addin
#tool nuget:?package=ARSoft.WinFormsLicensing&version=1.5.2
                    
Install as a Cake Tool

ARSoft WinForms Licensing

A robust time-based licensing library for Windows Forms applications that provides secure license management with encryption, dual storage, and comprehensive validation.

πŸš€ Features

  • Time-based Licensing: Create licenses with configurable expiration periods
  • Dual Storage with Self-Healing: Stores licenses in both local files and Windows Registry for redundancy with automatic repair
  • Encryption: AES encryption with PBKDF2 key derivation for secure license storage
  • Tamper Protection: SHA256 checksums prevent license modification
  • Flexible Distribution: Support for direct license data registration or .lic file import
  • Optional Logging: Built-in silent logger with optional custom logging support
  • Dual Usage Patterns: Separate factory methods for creation (with logging) and validation (silent)
  • Extended Default License: 366-day default validity period
  • License Detection: Built-in methods to check license presence and load valid licenses
  • Automatic Recovery: Self-healing validation automatically repairs corrupted or missing license copies

πŸ“¦ Installation

NuGet Package Manager

Install-Package ARSoft.WinFormsLicensing

.NET CLI

dotnet add package ARSoft.WinFormsLicensing

Package Manager Console

PM> Install-Package ARSoft.WinFormsLicensing

Note: As of v1.5.0, providing a logger is now optional. The library includes a built-in silent logger by default.

πŸ”§ How It Works

Core Concepts

  1. Dual Usage Patterns:
    • ForCreation(): Full-featured manager with custom logging for license generation tools and vendor operations
    • ForValidation(): Silent manager for client applications (no logging overhead)
  2. License Creation: Generate time-bound licenses for specific clients (default 366 days)
  3. Dual Storage with Self-Healing: Automatically saves to both AppData file and Windows Registry with automatic repair if one becomes corrupted
  4. Encryption: All license data is encrypted using AES with derived keys
  5. Validation: Multi-layer validation including checksum verification, expiration checks, and dual-source verification
  6. Flexible Distribution: Support for both direct license registration and .lic file import
  7. License Detection: Check for license presence before validation
  8. Unified Encryption: Both creation and validation modes use the same encryption parameters for consistency

Security Features

  • PBKDF2 Key Derivation: Uses 100,000 iterations with SHA256
  • AES Encryption: Industry-standard encryption for license data
  • SHA256 Checksums: Prevents tampering with license data
  • Client Hashing: Secure client identification without storing sensitive info
  • Dual-Source Validation: Validates both file and registry copies with automatic repair

Self-Healing License Validation

The library implements a robust self-healing mechanism during license validation:

  1. Dual-Source Check: Validates licenses from both file storage and Windows Registry
  2. Automatic Repair: If one source is missing or corrupted, it's automatically restored from the valid source
  3. Most Recent Selection: When both sources are valid, the most recently issued license is used
  4. Transparent Recovery: Self-healing happens automatically without user intervention

This ensures maximum license integrity and protects against:

  • Accidental file deletion
  • Registry corruption
  • Storage media failures
  • Tampering attempts

πŸ› οΈ Basic Implementation

1. Setup and Initialization

The library provides two factory methods for different use cases:

For License Creation (Vendor Tools with Logging)
using ARSoft.WinFormsLicensing;
using Microsoft.Extensions.Logging;
using System.Text;

// Option 1: Without logger (uses built-in silent logger - recommended for production)
var licenseManager = LicenseManager.ForCreation(
    logger: null, // Pass null to use built-in silent logger
    keyPassword: "YourSecurePassword123!", // Use a strong password
    keySalt: Encoding.UTF8.GetBytes("YourUniqueSalt12345"), // At least 8 bytes
    appName: "MyApplication",
    settingsFile: "license.json",
    registryKeyPath: "SOFTWARE\\MyCompany\\MyApp\\License"
);

// Option 2: With custom logger (for debugging/monitoring)
var loggerFactory = LoggerFactory.Create(builder => builder.AddConsole());
var customLogger = loggerFactory.CreateLogger<LicenseManager>();

var licenseManagerWithLogging = LicenseManager.ForCreation(
    logger: customLogger,
    keyPassword: "YourSecurePassword123!",
    keySalt: Encoding.UTF8.GetBytes("YourUniqueSalt12345"),
    appName: "MyApplication",
    settingsFile: "license.json",
    registryKeyPath: "SOFTWARE\\MyCompany\\MyApp\\License"
);

// Option 3: Toggle logging on/off at runtime
licenseManager.LoggerEnabled = true;  // Enable logging
licenseManager.LoggerEnabled = false; // Disable logging (default)
For License Validation (Client Applications - Silent Mode)
using ARSoft.WinFormsLicensing;
using System.Text;

// Initialize LicenseManager for validation (no logging, same encryption parameters)
var licenseManager = LicenseManager.ForValidation(
    password: "YourSecurePassword123!", // Same password as ForCreation
    salt: Encoding.UTF8.GetBytes("YourUniqueSalt12345"), // Same salt as ForCreation
    appName: "MyApplication",
    settingsFile: "license.json",
    registryKeyPath: "SOFTWARE\\MyCompany\\MyApp\\License"
);

Important: The password and salt used in ForValidation() must match those used in ForCreation() and LicenseGenerator.GenerateLicenseFile(). These are your application's encryption credentials.

2. License Distribution Workflows

The library supports two main distribution patterns:

Workflow A: Direct License Registration
  1. Vendor: Creates license using CreateLicense() method
  2. Vendor: Provides license data to customer (via secure channel)
  3. Customer: Enters license data in your registration window
  4. Customer App: Uses RenewLicense() or similar to register the license
Workflow B: License File Distribution
  1. Vendor: Generates .lic file using LicenseGenerator.GenerateLicenseFile()
  2. Vendor: Sends .lic file to customer (via secure channel)
  3. Customer: Receives .lic file
  4. Customer App: Uses ImportLicenseFile() to import and register the license

3. Creating Licenses (Vendor/Generator Tools)

Direct License Creation
// Use ForCreation() for vendor tools
var licenseManager = LicenseManager.ForCreation(
    logger: null,
    keyPassword: "YourSecurePassword123!",
    keySalt: Encoding.UTF8.GetBytes("YourUniqueSalt12345"),
    appName: "MyApplication",
    settingsFile: "license.json",
    registryKeyPath: "SOFTWARE\\MyCompany\\MyApp\\License"
);

// Create a 366-day license for a client (default validity)
bool success = licenseManager.CreateLicense(
    clientName: "John Doe Company", 
    licensedDate: DateTime.Now,
    validityDays: 366 // Optional: defaults to 366 days
);

if (success)
{
    Console.WriteLine("License created and registered successfully!");
}
Generating License Files (.lic)
using ARSoft.WinFormsLicensing.Service.Helpers;

// Generate encrypted .lic file for distribution
// IMPORTANT: Use the SAME password and salt as your application
bool fileGenerated = LicenseGenerator.GenerateLicenseFile(
    clientName: "John Doe Company",
    password: "YourSecurePassword123!",
    salt: Encoding.UTF8.GetBytes("YourUniqueSalt12345"),
    outputFile: @"C:\licenses\client-license.lic",
    validityDays: 366,
    startDate: DateTime.Now
);

if (fileGenerated)
{
    Console.WriteLine("License file generated successfully!");
    // Send client-license.lic to customer via secure channel
}

4. License Registration (Customer Applications)

// Initialize with ForValidation() for silent operation
var licenseManager = LicenseManager.ForValidation(
    password: "YourSecurePassword123!",
    salt: Encoding.UTF8.GetBytes("YourUniqueSalt12345"),
    appName: "MyApplication",
    settingsFile: "license.json",
    registryKeyPath: "SOFTWARE\\MyCompany\\MyApp\\License"
);

// Import License from File
bool imported = licenseManager.ImportLicenseFile(@"C:\path\to\received-license.lic");

if (imported)
{
    MessageBox.Show("License imported and registered successfully!", "Success");
}
else
{
    MessageBox.Show("Failed to import license file.", "Error");
}

// Or Register License from Data
var licenseData = new LicenseData
{
    Client = hashedClientName, // This should come from vendor
    Licensed = licensedDate,
    Expires = expirationDate,
    Version = "1.0",
    Checksum = providedChecksum
};

bool registered = licenseManager.RenewLicense(licenseData);

if (registered)
{
    MessageBox.Show("License registered successfully!", "Success");
}

5. License Validation (Client Applications)

// Check if license exists before validation
if (!licenseManager.IsLicensePresent())
{
    MessageBox.Show("No license found. Please register your software.", "License Missing", 
                    MessageBoxButtons.OK, MessageBoxIcon.Error);
    // Show registration form or exit
    return;
}

// Validate license on application startup
// The validation method automatically repairs corrupted or missing licenses
var (isValid, message, daysRemaining) = licenseManager.ValidateLicense();

if (!isValid)
{
    MessageBox.Show($"License Error: {message}", "License Validation", 
                    MessageBoxButtons.OK, MessageBoxIcon.Error);
    Application.Exit();
    return;
}

// Show expiration warning if needed
if (daysRemaining <= 30 && !string.IsNullOrEmpty(message))
{
    MessageBox.Show(message, "License Expiration Warning", 
                    MessageBoxButtons.OK, MessageBoxIcon.Warning);
}

Note: The ValidateLicense() method automatically performs self-healing. If the license file is corrupted but the registry copy is valid (or vice versa), the corrupted copy will be automatically restored. This happens transparently without any user intervention.

6. Working with License Data

// Load valid license data for inspection
var licenseData = licenseManager.LoadValidLicense();

if (licenseData != null)
{
    Console.WriteLine($"Licensed to: {licenseData.Client}"); // Note: This is a hashed value
    Console.WriteLine($"Expires on: {licenseData.Expires:yyyy-MM-dd}");
    Console.WriteLine($"Version: {licenseData.Version}");
}

πŸ’Ό Business Workflows

Workflow A: Direct License Registration

  1. Vendor Side:

    • Uses ForCreation() instance to create licenses
    • Generates license data using CreateLicense()
    • Provides license details to customer (securely)
  2. Customer Side:

    • Application has registration form/window
    • Customer enters provided license data
    • Application uses ForValidation() instance with RenewLicense() to register
    • Application validates license on startup using ValidateLicense()
    • Self-healing ensures license integrity automatically

Workflow B: License File Distribution

  1. Vendor Side:

    • Creates license generator tool using this library
    • Uses LicenseGenerator.GenerateLicenseFile() to create .lic files
    • Sends .lic files to customers via secure channels
  2. Customer Side:

    • Receives .lic file from vendor
    • Application uses ForValidation() instance with ImportLicenseFile()
    • Application validates license on startup using ValidateLicense()
    • Self-healing protects against storage corruption

Workflow C: Hybrid Approach

  1. Initial Registration: Customer registers using direct license data
  2. Renewals: Vendor sends .lic files for license renewals
  3. Updates: Customer imports new .lic files without reinstalling application
  4. Automatic Recovery: License automatically repairs if one storage location fails

πŸ”’ Security Best Practices

  1. Use Strong Passwords: Use complex passwords for key derivation
  2. Unique Salt: Use a unique salt for each application (minimum 8 bytes)
  3. Consistent Credentials: Use identical password and salt across all vendor tools and customer applications
  4. Secure Distribution: Distribute license files/data securely (encrypted email, secure download)
  5. Regular Validation: Validate license on application startup and periodically during runtime
  6. Error Handling: Implement proper error handling for license validation failures
  7. Registry Path Validation: Use valid registry paths starting with "SOFTWARE\" and containing only alphanumeric characters, backslashes, underscores, or hyphens
  8. Silent Operation in Production: Use ForValidation() in client applications to avoid logging overhead
  9. Trust Self-Healing: The dual-storage system with automatic repair provides robust protection against data corruption

πŸ“ Configuration Options

Factory Methods

LicenseManager.ForCreation()

For license generation tools and vendor operations with optional logging:

Parameter Type Required Description
logger ILogger<LicenseManager> No Logger instance for tracking events. Pass null to use built-in silent logger (recommended for production).
keyPassword string Yes Password for encryption key derivation. Must match across all instances.
keySalt byte[] Yes Salt for key derivation (min 8 bytes). Must match across all instances.
appName string Yes Application name for storage paths
settingsFile string Yes License file name
registryKeyPath string Yes Registry key path for license storage

Logging Control:

  • Pass null for logger parameter to use built-in silent logger (no output)
  • Pass a custom logger when you need debugging/monitoring capabilities
  • Set LoggerEnabled = true to enable logging with your custom logger
  • Set LoggerEnabled = false to disable logging (reverts to silent logger)
LicenseManager.ForValidation()

For client applications with silent operation (no logging):

Parameter Type Required Description
password string Yes Password for encryption key derivation. Must match ForCreation() password.
salt byte[] Yes Salt for key derivation (min 8 bytes). Must match ForCreation() salt.
appName string Yes Application name for storage paths
settingsFile string Yes License file name
registryKeyPath string Yes Registry key path for license storage

Important: The password and salt must be identical to those used in ForCreation() and LicenseGenerator.GenerateLicenseFile().

Key Methods

Method Description Available In
CreateLicense() Creates and saves a new license Both (ForCreation recommended)
ValidateLicense() Validates existing license with automatic self-healing Both
IsLicensePresent() Checks if license exists in either storage location Both
LoadValidLicense() Loads license data if valid Both
RenewLicense() Registers license from data object Both
RenewLicenseFromString() Registers from encrypted string Both
RenewLicenseFromFile() Registers from license file Both
ImportLicenseFile() Imports and registers .lic file Both

Properties

Property Type Description
LoggerEnabled bool Gets or sets whether logging is enabled. Only affects ForCreation() instances with custom loggers.

Utility Classes

Class/Method Description
LicenseGenerator.GenerateLicenseFile() Creates encrypted .lic files. Must use same password/salt as your application.

Storage Locations

  • File Storage: %AppData%\[AppName]\[SettingsFile]
  • Registry Storage: HKEY_CURRENT_USER\[RegistryKeyPath]

Both locations are validated and automatically synchronized during license validation.

Validation Messages

The library returns Portuguese validation messages:

  • "LicenΓ§a nΓ£o encontrada ou corrompida." - License not found or corrupted
  • "LicenΓ§a expirada." - License expired
  • Expiration warnings are generated automatically for licenses with 31 days or fewer remaining

πŸ§ͺ Example Application Structure

MyApplication/
β”œβ”€β”€ VendorTools/                   // Vendor-side tools
β”‚   β”œβ”€β”€ LicenseGenerator/         // Tool for generating .lic files
β”‚   β”‚   β”œβ”€β”€ GeneratorForm.cs      // UI for license generation
β”‚   β”‚   └── Program.cs            // Uses LicenseGenerator helper
β”‚   └── LicenseCreator/           // Tool for direct license creation
β”‚       β”œβ”€β”€ CreatorForm.cs        // UI for license creation
β”‚       └── Program.cs            // Uses ForCreation()
β”œβ”€β”€ ClientApp/                    // Customer application
β”‚   β”œβ”€β”€ Program.cs               // Application entry with validation
β”‚   β”œβ”€β”€ MainForm.cs              // Main application form
β”‚   β”œβ”€β”€ LicenseRegistrationForm.cs // Customer registration UI
β”‚   └── LicenseService.cs        // Service layer (uses ForValidation)
β”œβ”€β”€ Models/
β”‚   └── AppConfig.cs             // Application configuration
└── Shared/
    └── LicenseConstants.cs      // Shared configuration constants (password/salt)

Example Usage Patterns

Vendor License Generator Tool
// Tool for generating .lic files (no logger needed in production)
// Note: This doesn't require LicenseManager, just the helper class

bool success = LicenseGenerator.GenerateLicenseFile(
    clientName: "Customer ABC Corp",
    password: "YourSecurePassword123!",
    salt: Encoding.UTF8.GetBytes("YourUniqueSalt12345"),
    outputFile: @"C:\licenses\customer-abc.lic",
    validityDays: 366
);
Customer Registration Window
// Customer app registration form (silent mode)
var licenseManager = LicenseManager.ForValidation(
    password: "YourSecurePassword123!",
    salt: Encoding.UTF8.GetBytes("YourUniqueSalt12345"),
    appName: "MyApplication",
    settingsFile: "license.json",
    registryKeyPath: "SOFTWARE\\MyCompany\\MyApp\\License"
);

// Import .lic file received from vendor
bool imported = licenseManager.ImportLicenseFile(licenseFilePath);

// Or register direct license data
bool registered = licenseManager.RenewLicense(licenseData);
Client Application Validation
// Main application startup validation (silent mode with auto-healing)
var licenseManager = LicenseManager.ForValidation(
    password: "YourSecurePassword123!",
    salt: Encoding.UTF8.GetBytes("YourUniqueSalt12345"),
    appName: "MyApplication",
    settingsFile: "license.json",
    registryKeyPath: "SOFTWARE\\MyCompany\\MyApp\\License"
);

if (!licenseManager.IsLicensePresent())
{
    ShowRegistrationDialog();
    return;
}

// ValidateLicense() automatically repairs corrupted licenses
var (isValid, message, daysRemaining) = licenseManager.ValidateLicense();
if (!isValid)
{
    ShowLicenseErrorDialog(message);
    return;
}

πŸ“Š License Data Structure

public class LicenseData
{
    public string Client { get; init; }      // Hashed client identifier (SHA256)
    public DateTime Licensed { get; init; }  // License issue date
    public DateTime Expires { get; init; }   // License expiration date
    public string Version { get; init; }     // License version
    public string Checksum { get; set; }     // SHA256 tamper protection checksum
}

Important Notes:

  • The Client field stores a SHA256 hash of the client name, not the actual name
  • The Checksum is automatically generated and validated to prevent tampering
  • All dates are handled as DateTime objects

πŸ› Error Handling

The library provides comprehensive error handling with detailed logging (when enabled):

  • Invalid license format
  • Corrupted license data (checksum validation with automatic repair)
  • Expired licenses
  • Missing license files (with automatic restoration from registry)
  • Registry access issues (with automatic restoration from file)
  • Encryption/decryption errors
  • Invalid registry paths
  • File system access errors
  • Base64 decoding errors
  • Invalid .lic file format

All errors are logged appropriately (when logging is enabled) and methods return boolean success indicators or tuples with validation results. The self-healing mechanism automatically attempts to recover from storage corruption.

πŸ“‹ Requirements

  • .NET 6.0 or higher
  • Windows OS (uses Windows Registry)
  • Microsoft.Extensions.Logging (for custom logging in ForCreation - optional)
  • Newtonsoft.Json (for JSON serialization)

🚨 Important Limitations

  • Encryption Consistency: Password and salt must be identical across all tools and applications
  • Registry Path Validation: Registry paths must start with "SOFTWARE\" and contain only valid characters
  • Salt Requirements: Encryption salt must be at least 8 bytes
  • Both Methods Full-Featured: Both ForCreation() and ForValidation() support all operations (create, renew, import, validate). The distinction is primarily about logging.
  • Self-Healing Requirements: Both storage locations must be accessible for self-healing to work properly

🀝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

πŸ“„ License

This project is licensed under the MIT License - see the LICENSE file for details.

πŸ“ž Support

For support, feature requests, or bug reports, please create an issue on GitHub.

πŸ”„ Version History

v1.5.2

  • Self-Healing License Validation: ValidateLicense() now validates both file and registry copies
  • Automatic Repair: Corrupted or missing licenses are automatically restored from the valid source
  • Most Recent Selection: When both copies are valid, the most recently issued license is used
  • Enhanced Reliability: Improved protection against storage corruption and data loss
  • Updated Validation Messages: Combined "not found" and "corrupted" into single message for cleaner UX

v1.5.1

  • Breaking Change: Removed parameterless ForValidation() method
  • ForValidation() now requires password and salt: Client applications must provide encryption credentials to decrypt licenses
  • Unified encryption: Both ForCreation() and ForValidation() now use the same encryption parameters for consistency
  • Simplified architecture: The distinction between creation and validation is now primarily about logging, not functionality

v1.5.0

  • Logger now optional: The logger parameter in ForCreation() can now be null, which uses a built-in silent logger
  • Runtime logging control: Added LoggerEnabled property to toggle logging on/off at runtime
  • Improved flexibility: Applications no longer need to set up logging infrastructure if they don't need it
  • Backward compatible: Existing code with custom loggers continues to work without changes

v1.4.0

  • Breaking Changes: LicenseUtils was made public
  • Added ImportLicenseFile() method for .lic file import
  • Added LicenseGenerator utility class for creating .lic files

v1.3.0

  • Breaking Changes: LicenseUtils was made public
  • Improved error handling and validation
  • Enhanced security with better registry path validation

v1.2.0

  • Breaking Changes: Introduced factory methods pattern
  • Added ForCreation() and ForValidation() factory methods
  • Added IsLicensePresent() method for license detection
  • Added LoadValidLicense() method for license data access
  • Improved separation of concerns between license creation and validation

v1.0.0

  • Initial release
  • Time-based licensing
  • Dual storage (file + registry)
  • AES encryption with PBKDF2
  • License renewal capabilities
  • Comprehensive logging

Product Compatible and additional computed target framework versions.
.NET net8.0-windows10.0.17763 is compatible.  net9.0-windows was computed.  net10.0-windows 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.5.2 219 10/6/2025
1.5.1 208 10/4/2025
1.4.2 353 9/5/2025 1.4.2 is deprecated because it is no longer maintained and has critical bugs.