Your biggest enterprise deal just came in: a government contract worth $500K. Then you discover their security policy prohibits internet connections for classified systems. If your software requires online activation, that deal is dead.
Offline activation isn't a nice-to-have for enterprise software—it's a requirement for government, military, healthcare, financial, and industrial control system deployments. This article shows you how to implement secure offline activation with QR codes in C#.
Why Offline Activation Matters
Markets that REQUIRE offline activation:
- Government/Military - Classified systems, air-gapped networks
- Healthcare - HIPAA-compliant isolated networks
- Financial Services - Trading systems in secure facilities
- Industrial Control - SCADA systems, manufacturing floors
- Research Labs - Sensitive R&D environments
- Legal/Forensics - Evidence handling systems
Industry data:
- 15-20% of enterprise customers have air-gapped requirements
- Government contracts average 3-5x higher value than commercial
- 89% of Fortune 500 companies have at least some air-gapped systems
If your licensing system can't work offline, you're excluding 15-20% of the most valuable market.
Offline Activation Methods
Quick License Manager supports multiple offline activation approaches:
1. QR Code Activation (Recommended)
- User scans QR code with smartphone
- Smartphone accesses activation portal
- Returns computer key via QR code
- No typing, minimal errors
2. Manual Activation
- User copies Computer ID
- Enters ID on self-service portal from another machine
- Portal generates Computer Key
- User copies key back to air-gapped system
3. Pre-Generated License Files
- Generate license file in advance
- Deliver via USB drive
- Import into application
- No internet needed at any point
Understanding the Offline Activation Flow
AIR-GAPPED SYSTEM ONLINE SYSTEM LICENSE SERVER
(No Internet) (Has Internet) (soraco.co)
│ │ │
│ 1. Generate Computer ID │ │
│───────────────────────────▶ │
│ │ │
│ 2. Show QR Code / Manual │ │
│ Computer ID │ │
│ │ │
│ │ 3. Submit Computer ID │
│ │────────────────────────▶│
│ │ │
│ │ 4. Generate Computer Key│
│ │◀────────────────────────│
│ │ │
│ 5. Return Computer Key │ │
│◀─────────────────────────── │
│ │ │
│ 6. Activate License │ │
│ (Store Encrypted) │ │
└───────────────────────────┘ │
Key insight: The Computer ID is generated offline and identifies the specific machine. The Computer Key is returned by the server and proves authorization.
Implementing Basic Offline Activation
Step 1: Generate Computer ID
using QLM.LicenseLib;
public class OfflineActivationManager
{
private LicenseValidator lv;
private string settingsFile;
public OfflineActivationManager(string settingsFilePath)
{
settingsFile = settingsFilePath;
lv = new LicenseValidator(settingsFile);
}
public string GetComputerID()
{
// Generate unique identifier for this computer
// Uses hardware identifiers configured in your QLM settings
string computerID = lv.QlmLicenseObject.GetComputerID();
// Example result: "ACME-PC-12AB34CD56EF"
return computerID;
}
public string GetActivationKey()
{
// The activation key provided by the customer
// Example: "AXYZ0-12345-ABCDE-67890-FGHIJ-KLMNO"
return lv.ActivationKey;
}
}
The Computer ID is based on hardware binding and can use:
- Computer Name
- MAC Address
- Motherboard Serial Number
- CPU ID
- Volume Serial Number
- Active Directory Domain
Step 2: Display for Offline Activation
public class OfflineActivationDialog : Form
{
private TextBox txtActivationKey;
private TextBox txtComputerID;
private Button btnShowQRCode;
private Button btnManualActivation;
private OfflineActivationManager offlineManager;
public OfflineActivationDialog()
{
InitializeComponent();
offlineManager = new OfflineActivationManager("settings.xml");
// Display Computer ID
txtComputerID.Text = offlineManager.GetComputerID();
txtComputerID.ReadOnly = true;
}
private void btnShowQRCode_Click(object sender, EventArgs e)
{
string activationKey = txtActivationKey.Text.Trim();
if (string.IsNullOrEmpty(activationKey))
{
MessageBox.Show("Please enter your Activation Key first.");
return;
}
// Generate QR code containing activation URL
ShowQRCodeDialog(activationKey);
}
private void btnManualActivation_Click(object sender, EventArgs e)
{
// Show instructions for manual activation
ShowManualActivationInstructions();
}
}
Step 3: Generate QR Code
using QRCoder; // NuGet: QRCoder
public class QRCodeGenerator
{
public Bitmap GenerateActivationQRCode(
string activationKey,
string computerID,
string licenseServerUrl)
{
// Build activation URL
string activationUrl = BuildActivationUrl(
licenseServerUrl,
activationKey,
computerID
);
// Generate QR code
var qrGenerator = new QRCodeGenerator();
var qrCodeData = qrGenerator.CreateQrCode(
activationUrl,
QRCodeGenerator.ECCLevel.M // Medium error correction
);
var qrCode = new QRCode(qrCodeData);
Bitmap qrCodeImage = qrCode.GetGraphic(
pixelsPerModule: 20,
darkColor: Color.Black,
lightColor: Color.White,
drawQuietZones: true
);
return qrCodeImage;
}
private string BuildActivationUrl(
string serverUrl,
string activationKey,
string computerID)
{
// URL points to QLM Self-Service portal
// Example: https://yoursite.com/qlmselfhelp/
var uri = new UriBuilder(serverUrl);
uri.Path += "QlmCustomerSite/qlmactivationqrcode.aspx";
// Add query parameters
var query = HttpUtility.ParseQueryString(uri.Query);
query["qlmak"] = activationKey;
query["qlmcid"] = computerID;
uri.Query = query.ToString();
return uri.ToString();
}
}
More details: Offline activation via QR code
Step 4: Display QR Code Dialog
public class QRCodeActivationDialog : Form
{
private PictureBox pictureBoxQR;
private Label lblInstructions;
private TextBox txtComputerKey;
private Button btnActivate;
public QRCodeActivationDialog(string activationKey, string computerID)
{
InitializeComponent();
lblInstructions.Text =
"STEP 1: Scan this QR code with your smartphone\n" +
"STEP 2: Your phone will open the activation page\n" +
"STEP 3: The page will generate a Computer Key\n" +
"STEP 4: Enter the Computer Key below";
// Generate and display QR code
var qrGenerator = new QRCodeGenerator();
Bitmap qrImage = qrGenerator.GenerateActivationQRCode(
activationKey,
computerID,
"https://yoursite.com/qlmselfhelp/"
);
pictureBoxQR.Image = qrImage;
pictureBoxQR.SizeMode = PictureBoxSizeMode.Zoom;
}
private void btnActivate_Click(object sender, EventArgs e)
{
string computerKey = txtComputerKey.Text.Trim();
if (string.IsNullOrEmpty(computerKey))
{
MessageBox.Show("Please enter the Computer Key from your phone.");
return;
}
// Activate with the computer key
bool success = ActivateOffline(computerKey);
if (success)
{
MessageBox.Show(
"License activated successfully!",
"Activation Complete",
MessageBoxButtons.OK,
MessageBoxIcon.Information
);
this.DialogResult = DialogResult.OK;
this.Close();
}
}
private bool ActivateOffline(string computerKey)
{
try
{
var lv = new LicenseValidator("settings.xml");
string response;
bool success = lv.QlmLicenseObject.ActivateLicenseOffline(
activationKey: lv.ActivationKey,
computerKey: computerKey,
response: out response
);
if (success)
{
// Store activated license
lv.QlmLicenseObject.StoreKeys(computerKey);
return true;
}
else
{
MessageBox.Show($"Activation failed: {response}");
return false;
}
}
catch (Exception ex)
{
MessageBox.Show($"Error: {ex.Message}");
return false;
}
}
}
Manual Offline Activation
For users without smartphones or in extremely secure facilities:
Client-Side: Display Instructions
public class ManualActivationDialog : Form
{
private TextBox txtActivationKey;
private TextBox txtComputerID;
private Label lblInstructions;
private TextBox txtComputerKey;
private Button btnActivate;
private Button btnCopyComputerID;
public ManualActivationDialog()
{
InitializeComponent();
var offlineManager = new OfflineActivationManager("settings.xml");
txtComputerID.Text = offlineManager.GetComputerID();
txtComputerID.ReadOnly = true;
lblInstructions.Text =
"MANUAL ACTIVATION INSTRUCTIONS:\n\n" +
"1. Copy the Computer ID below\n" +
"2. On a computer with internet, visit:\n" +
" https://yoursite.com/qlmselfhelp/\n" +
"3. Click 'Activate a License'\n" +
"4. Enter your Activation Key and Computer ID\n" +
"5. The website will display a Computer Key\n" +
"6. Enter the Computer Key below and click Activate";
}
private void btnCopyComputerID_Click(object sender, EventArgs e)
{
Clipboard.SetText(txtComputerID.Text);
MessageBox.Show(
"Computer ID copied to clipboard!\n\n" +
"Paste it on the activation website.",
"Copied",
MessageBoxButtons.OK,
MessageBoxIcon.Information
);
}
private void btnActivate_Click(object sender, EventArgs e)
{
string computerKey = txtComputerKey.Text.Trim();
if (string.IsNullOrEmpty(computerKey))
{
MessageBox.Show("Please enter the Computer Key from the website.");
return;
}
// Same activation logic as QR code method
bool success = ActivateOffline(computerKey);
if (success)
{
MessageBox.Show("License activated successfully!");
this.DialogResult = DialogResult.OK;
this.Close();
}
}
}
Server-Side: Self-Service Portal
QLM provides a self-service portal that you can customize and host on your website:
<!-- Embed in your website -->
<iframe src="https://yoursite.com/qlmselfhelp/QlmCustomerSite/qlmactivation.aspx"
width="600"
height="800"
frameborder="0">
</iframe>
The portal automatically:
- Validates the Activation Key
- Verifies the Computer ID
- Generates a secure Computer Key
- Displays the key to the user
- Records the activation in your database
Using the QLM License Wizard
The easiest approach: use QLM's built-in License Wizard:
Launch the Wizard
public class LicenseWizardLauncher
{
public void ShowActivationWizard()
{
// Path to QLM License Wizard
string wizardPath = Path.Combine(
Application.StartupPath,
"QlmLicenseWizard.exe"
);
// Launch with offline activation enabled
var startInfo = new ProcessStartInfo
{
FileName = wizardPath,
Arguments = BuildWizardArguments(),
UseShellExecute = false
};
Process.Start(startInfo);
}
private string BuildWizardArguments()
{
var args = new StringBuilder();
// Settings file
args.Append($"/settings \"{settingsFilePath}\" ");
// Enable offline activation
args.Append("/offline ");
// Show QR code option
args.Append("/qrcode ");
// Product information
args.Append("/productname \"My Application\" ");
args.Append("/productversion \"1.0\" ");
return args.ToString();
}
}
The QLM License Wizard automatically:
- Detects if system is offline
- Offers QR code or manual activation
- Generates Computer ID
- Displays QR code for smartphone scanning
- Accepts Computer Key input
- Completes activation
No UI code needed on your part!
Offline Deactivation
Just as important: letting users deactivate licenses offline:
Generate Deactivation Verification Code
public class OfflineDeactivationManager
{
private LicenseValidator lv;
public string GenerateDeactivationVerificationCode()
{
// Get the currently activated license
string activationKey = lv.ActivationKey;
string computerID = lv.QlmLicenseObject.GetComputerID();
// Generate verification code
string dvc = lv.QlmLicenseObject.GenerateDeactivationVerificationCode(
activationKey,
computerID
);
// Example: "DVC-12AB-34CD-56EF"
return dvc;
}
public void ShowOfflineDeactivationDialog()
{
string dvc = GenerateDeactivationVerificationCode();
MessageBox.Show(
$"OFFLINE DEACTIVATION INSTRUCTIONS:\n\n" +
$"1. Deactivation Verification Code: {dvc}\n" +
$"2. On a computer with internet, visit:\n" +
$" https://yoursite.com/qlmselfhelp/\n" +
$"3. Click 'Deactivate a License'\n" +
$"4. Enter your Activation Key and the DVC\n" +
$"5. The license will be released on the server\n\n" +
$"This computer's license will be removed when you click OK.",
"Offline Deactivation",
MessageBoxButtons.OKCancel,
MessageBoxIcon.Information
);
}
}
More on offline deactivation with verification codes.
Deactivation with QR Code
public Bitmap GenerateDeactivationQRCode(
string activationKey,
string computerID)
{
// Generate DVC
string dvc = lv.QlmLicenseObject.GenerateDeactivationVerificationCode(
activationKey,
computerID
);
// Build deactivation URL
string deactivationUrl = BuildDeactivationUrl(
"https://yoursite.com/qlmselfhelp/",
activationKey,
computerID,
dvc
);
// Generate QR code
var qrGenerator = new QRCodeGenerator();
var qrCodeData = qrGenerator.CreateQrCode(
deactivationUrl,
QRCodeGenerator.ECCLevel.M
);
var qrCode = new QRCode(qrCodeData);
return qrCode.GetGraphic(20);
}
private string BuildDeactivationUrl(
string serverUrl,
string activationKey,
string computerID,
string dvc)
{
var uri = new UriBuilder(serverUrl);
uri.Path += "QlmCustomerSite/qlmdeactivationqrcode.aspx";
var query = HttpUtility.ParseQueryString(uri.Query);
query["qlmak"] = activationKey;
query["qlmcid"] = computerID;
query["qlmdvc"] = dvc;
uri.Query = query.ToString();
return uri.ToString();
}
QR code deactivation workflow:
- User clicks "Deactivate License"
- App generates deactivation QR code
- User scans with smartphone
- Phone opens deactivation portal
- Portal releases license on server
- User confirms deactivation in app
Pre-Generated License Files
For maximum security or pre-deployment scenarios:
Server-Side: Generate License File
public class LicenseFileGenerator
{
public byte[] GenerateLicenseFile(
string activationKey,
string computerID,
string customerEmail)
{
var lv = new LicenseValidator("settings.xml");
// Activate the license server-side
string response;
bool success = lv.QlmLicenseObject.ActivateLicense(
webServiceUrl: lv.QlmLicenseObject.DefaultWebServiceUrl,
activationKey: activationKey,
computerID: computerID,
computerName: "PreGenerated",
qlmVersion: lv.QlmLicenseObject.QlmVersion,
userData: customerEmail,
response: out response
);
if (!success)
{
throw new Exception($"Activation failed: {response}");
}
// Extract license file content from response
// Response contains XML with license data
string licenseXml = ExtractLicenseFromResponse(response);
// Encrypt and package
byte[] encryptedLicense = EncryptLicenseFile(licenseXml);
return encryptedLicense;
}
private string ExtractLicenseFromResponse(string response)
{
// Parse the response XML
var doc = new XmlDocument();
doc.LoadXml(response);
// Extract license node
var licenseNode = doc.SelectSingleNode("//LicenseFile");
return licenseNode?.InnerXml ?? string.Empty;
}
}
Client-Side: Import License File
public class LicenseFileImporter
{
public bool ImportLicenseFile(string licenseFilePath)
{
try
{
// Read license file
byte[] encryptedLicense = File.ReadAllBytes(licenseFilePath);
// Decrypt
string licenseXml = DecryptLicenseFile(encryptedLicense);
// Store in QLM's license location
var lv = new LicenseValidator("settings.xml");
lv.QlmLicenseObject.StoreKeys(licenseXml);
// Validate
bool needsActivation = false;
string errorMsg = string.Empty;
bool isValid = lv.ValidateLicenseAtStartup(
ELicenseBinding.ComputerName,
ref needsActivation,
ref errorMsg
);
if (isValid)
{
MessageBox.Show(
"License file imported successfully!",
"Success",
MessageBoxButtons.OK,
MessageBoxIcon.Information
);
return true;
}
else
{
MessageBox.Show($"License validation failed: {errorMsg}");
return false;
}
}
catch (Exception ex)
{
MessageBox.Show($"Import failed: {ex.Message}");
return false;
}
}
}
Deployment workflow:
- Generate license files for all target computers
- Package files on USB drive
- Deliver to customer site
- Install software + import license files
- No internet needed at any point
Security Considerations
1. Validate Computer Key Integrity
public bool ValidateComputerKey(string computerKey)
{
// QLM Computer Keys contain:
// - Digital signature
// - Timestamp
// - Computer ID hash
// - Activation Key reference
// Validation is built into ActivateLicenseOffline
// But you can add extra checks:
if (string.IsNullOrEmpty(computerKey))
return false;
// Check format (QLM uses specific patterns)
if (!Regex.IsMatch(computerKey, @"^[A-Z0-9\-]+$"))
return false;
// Check minimum length
if (computerKey.Length < 50)
return false;
return true;
}
2. Prevent Computer ID Tampering
public class SecureComputerIDGenerator
{
public string GetSecureComputerID()
{
// Use multiple hardware identifiers
var lv = new LicenseValidator("settings.xml");
// Configure in QLM to use:
// - Motherboard Serial (hardest to spoof)
// - CPU ID (processor-specific)
// - Volume Serial (hard drive)
string computerID = lv.QlmLicenseObject.GetComputerID();
// Add application-specific salt
string salted = HashWithSalt(computerID);
return salted;
}
private string HashWithSalt(string input)
{
// Add your application-specific salt
string salt = "YourApp-V1.0-2024";
return SHA256Hash(input + salt);
}
}
3. Time-Limited Offline Periods
public class OfflineTimeManager
{
private const int MaxOfflineDays = 30;
public bool IsOfflinePeriodValid()
{
var lv = new LicenseValidator("settings.xml");
// Get last server contact
DateTime lastContact = lv.QlmLicenseObject.LastAccessedDate;
// Calculate days offline
int daysOffline = (DateTime.Now - lastContact).Days;
if (daysOffline > MaxOfflineDays)
{
ShowReactivationRequired();
return false;
}
return true;
}
private void ShowReactivationRequired()
{
MessageBox.Show(
$"This license has been offline for more than {MaxOfflineDays} days.\n\n" +
"Please connect to the internet and reactivate your license,\n" +
"or perform an offline reactivation.",
"Reactivation Required",
MessageBoxButtons.OK,
MessageBoxIcon.Warning
);
}
}
Best Practices
1. Provide Multiple Activation Methods
public class ActivationMethodSelector : Form
{
private Button btnOnlineActivation;
private Button btnQRCodeActivation;
private Button btnManualActivation;
private Button btnLicenseFile;
private void DetectConnectivity()
{
bool hasInternet = CheckInternetConnection();
if (hasInternet)
{
// Online is fastest - make it primary
btnOnlineActivation.Enabled = true;
btnOnlineActivation.BackColor = Color.FromArgb(0, 122, 204);
// Others available as fallback
btnQRCodeActivation.Enabled = true;
btnManualActivation.Enabled = true;
}
else
{
// Offline mode - disable online
btnOnlineActivation.Enabled = false;
// Emphasize offline methods
btnQRCodeActivation.BackColor = Color.FromArgb(0, 122, 204);
btnManualActivation.Enabled = true;
}
// License file always available
btnLicenseFile.Enabled = true;
}
}
2. Clear User Instructions
public class ActivationInstructionsDialog : Form
{
public void ShowInstructions(ActivationMethod method)
{
string instructions = method switch
{
ActivationMethod.QRCode =>
"QR CODE ACTIVATION:\n\n" +
"1. Your smartphone must have internet access\n" +
"2. Scan the QR code that will be displayed\n" +
"3. Your phone's browser will open automatically\n" +
"4. A Computer Key will be displayed\n" +
"5. Enter that key in the next screen\n\n" +
"This process takes about 2 minutes.",
ActivationMethod.Manual =>
"MANUAL ACTIVATION:\n\n" +
"1. You'll need access to a computer with internet\n" +
"2. Copy the Computer ID we'll show you\n" +
"3. Visit our activation website\n" +
"4. Enter your Activation Key and Computer ID\n" +
"5. Copy the generated Computer Key\n" +
"6. Return here and enter that Computer Key\n\n" +
"This process takes about 5 minutes.",
ActivationMethod.LicenseFile =>
"LICENSE FILE IMPORT:\n\n" +
"1. You should have received a .lic file\n" +
"2. Copy it to a USB drive if needed\n" +
"3. We'll prompt you to select the file\n" +
"4. Activation will be immediate\n\n" +
"This process takes about 1 minute.",
_ => "Unknown activation method"
};
MessageBox.Show(instructions, "Activation Instructions");
}
}
3. Test Offline Scenarios
[TestClass]
public class OfflineActivationTests
{
[TestMethod]
public void TestQRCodeGeneration()
{
var qrGenerator = new QRCodeGenerator();
var bitmap = qrGenerator.GenerateActivationQRCode(
"TEST-KEY-12345",
"COMPUTER-ID-67890",
"https://test.soraco.co/qlmselfhelp/"
);
Assert.IsNotNull(bitmap);
Assert.IsTrue(bitmap.Width > 0);
}
[TestMethod]
public void TestOfflineActivation()
{
var lv = new LicenseValidator("test-settings.xml");
// Simulate offline activation
string computerKey = "GENERATED-COMPUTER-KEY";
string response;
bool success = lv.QlmLicenseObject.ActivateLicenseOffline(
"TEST-KEY",
computerKey,
out response
);
Assert.IsTrue(success);
}
[TestMethod]
public void TestLicenseFileImport()
{
var importer = new LicenseFileImporter();
bool success = importer.ImportLicenseFile("test-license.lic");
Assert.IsTrue(success);
}
}
Using Quick License Manager
While you can implement offline activation from scratch, Quick License Manager provides:
✅ QR code activation built-in
✅ Self-service portal ready to deploy
✅ QLM License Wizard with offline support
✅ Offline deactivation with verification codes
✅ Pre-generated license files
✅ Hardware binding with multiple identifiers
✅ Cross-platform support (Windows, Linux, Mac, mobile)
Download QLM and test offline activation in your environment.
Conclusion
Offline activation isn't optional for enterprise software—it's a requirement for accessing the most valuable markets. The three approaches each serve different needs:
QR Code Activation:
- Fastest and most user-friendly
- Requires smartphone with internet
- 2-minute activation process
Manual Activation:
- Works in maximum security environments
- Requires access to any internet-connected device
- 5-minute activation process
Pre-Generated License Files:
- Perfect for pre-deployment
- Zero internet requirements
- Instant activation
Supporting offline activation opens doors to government contracts, healthcare deployments, financial institutions, and industrial control systems—markets that command 3-5x higher prices than standard commercial software.
With Quick License Manager, offline activation is built-in with QR codes, self-service portals, and comprehensive documentation. Don't let online-only licensing cost you your most valuable deals.
Resources
- QLM Offline Activation Overview
- QR Code Activation
- License Wizard with Offline Support
- Getting Computer Key Offline
- Offline Deactivation
- QLM Features
- Download QLM
Have you implemented offline activation? What challenges did you face? Share in the comments! 💬
Top comments (0)