A modern, self-hosted license management system for VMProtect-protected software. Designed to automate license generation for software developers while outsourcing the license key generation and HWID collection tasks to sellers/resellers.
As a software developer using VMProtect for copy protection, you face a challenge: generating license keys requires your private RSA key and understanding of VMProtect's binary format. This creates bottlenecks:
- Manual process: You must personally generate each key when a customer purchases
- HWID collection: Customers need to submit their Hardware ID, which you must relay
- Seller coordination: If you have resellers, they depend on you for every license
This project solves these problems by providing a web-based platform where:
- You (admin) import your VMProtect product keys once
- Sellers can independently generate licenses using credits you allocate
- Customers provide their HWID directly to sellers
- Everything is tracked with full audit trail and approval workflows
- 🔐 VMProtect Compatible - Generate license keys using VMProtect's RSA encryption format (offline verification)
- 👥 Multi-tenant - Admin and seller roles with credit-based quotas
- 📦 Multi-product - Support multiple products with individual RSA key pairs
- 🔄 Renewals & Refunds - Built-in approval workflow for license management
- 📊 Full Audit Trail - Track all license generations, credit transactions, and requests
- ⏱️ Flexible Duration - 7-day (0.3 credits) to 6-month license options
- 🔑 HWID Management - Monthly free change + paid instant change option
- 📋 Sortable Tables - Click columns to sort by customer, date, credits
- 🌐 Bilingual - Full English and Chinese language support (日/月 labels)
- 📱 Mobile Optimized - Responsive design for all screen sizes
- 🚀 Easy Deploy - One-click deploy to Vercel
Click to view more screenshots
| Main Dashboard | User Management | Product Management |
|---|---|---|
![]() |
![]() |
![]() |
| Credit Management | License Import | Request Approvals |
|---|---|---|
![]() |
![]() |
![]() |
| Product Assignment | All Requests | License Overview |
|---|---|---|
![]() |
![]() |
![]() |
| Seller Dashboard | Batch Generation | Batch Generation Window |
|---|---|---|
![]() |
![]() |
![]() |
| Credit History | Credit History (License Ref) | Approvals |
|---|---|---|
![]() |
![]() |
![]() |
| License Renewal | HWID Request | Refund Request |
|---|---|---|
![]() |
![]() |
![]() |
- Node.js 18+
- Supabase account (free tier works)
git clone https://github.com/thetobysiu/vmprotect-license-manager.git
cd vmprotect-license-manager
npm install- Create a new project at supabase.com
- Go to SQL Editor → New Query
- Copy and run the entire contents of
supabase/schema.sql - Go to Settings → API and copy your credentials
Create .env.local:
NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
SUPABASE_SERVICE_ROLE_KEY=your-service-role-keynpm run devDefault login: admin / admin (change password immediately!)
Or manually:
npm i -g vercel
vercel --prodAny platform supporting Next.js works:
npm run build
npm start| Feature | Description |
|---|---|
| User Management | Create/delete seller accounts, manage assigned products |
| Credit Management | Add/deduct credits (supports decimals like 0.3), view transaction history |
| Product Management | Import VMProtect XML, assign products to sellers |
| License Overview | Sortable columns, filters, search by customer/HWID, export CSV, bulk delete |
| Import Licenses | Import existing licenses via XML or text |
| Request Approval | Approve/reject refund and HWID change requests with optional credit charge |
| HWID Settings | Configure HWID change cost percentage (default 30%) |
| Feature | Description |
|---|---|
| Generate Licenses | Bulk generate via CSV or single quick-add (7 days to 6 months) |
| My Licenses | Sortable table with customer, HWID, created, expiry, credits columns |
| Renewals | Extend license expiry (7 days = 0.3 credits, 1-6 months) |
| Refund Requests | Submit refund requests to admin |
| HWID Changes | One free change per month, or pay 30% credits for instant change |
| Duration | Credits | Notes |
|---|---|---|
| 7 days | 0.3 | Short-term trial |
| 1 month | 1.0 | Standard |
| 2 months | 2.0 | ... |
| 6 months | 6.0 | Maximum |
| Option | Cost | Approval |
|---|---|---|
| Monthly free | 0 | Auto-approved (once per month per license) |
| Paid instant | 30% of license credits | Auto-approved |
| Request approval | Admin decides | Admin can charge 0 or custom amount |
When a HWID change is requested, the system now stores:
- Original HWID - The previous hardware ID
- Original License Key - The old serial number (for blocklist use)
- New HWID - The updated hardware ID
- New License Key - Generated after approval
This allows you to maintain a blocklist of compromised or revoked keys while keeping full audit history.
VMProtect uses asymmetric RSA cryptography for license key generation and verification:
┌─────────────────────────────────────────────────────────────────────────┐
│ KEY GENERATION (Server-side) │
├─────────────────────────────────────────────────────────────────────────┤
│ License Data (Name, HWID, Expiry, ProductCode) │
│ ↓ │
│ PKCS#1 v1.5 Padding │
│ ↓ │
│ RSA Encrypt with PRIVATE KEY ←── Only you have this! │
│ ↓ │
│ Base64 Encoded License Key (serial number) │
└─────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────┐
│ KEY VERIFICATION (Client-side) │
├─────────────────────────────────────────────────────────────────────────┤
│ Base64 License Key │
│ ↓ │
│ RSA Decrypt with PUBLIC KEY ←── Embedded in your protected .exe │
│ ↓ │
│ Verify: HWID matches, Expiry valid, ProductCode correct, CRC valid │
│ ↓ │
│ ✅ License Valid (or ❌ Invalid) │
└─────────────────────────────────────────────────────────────────────────┘
| Term | Description |
|---|---|
| Private Key | Secret key used to sign/generate licenses. Never share this! Stored securely in your database. |
| Public Key | Embedded in your protected executable. Used to verify licenses. Safe to distribute. |
| Modulus (N) | Shared component of both keys. Part of the RSA key pair. |
| HWID | Hardware ID - unique identifier of customer's machine. Prevents license sharing. |
| Offline Verification | License is verified locally without internet. The public key in your .exe can validate any license signed by your private key. |
Unlike online license servers, VMProtect licenses are cryptographically self-contained:
- ✅ No server dependency - Your software works even if your server is down
- ✅ No latency - Instant verification, no network round-trips
- ✅ Privacy - No "phone home" required
- ✅ Tamper-proof - Mathematical guarantee via RSA (impossible to forge without private key)
The trade-off is that revocation requires issuing new software builds with updated keys or using the HWID change workflow.
- In VMProtect: Project → Options → License Manager → Export
- In this app: Products tab → Parse XML → Paste and Add
The exported XML contains Modulus, Private, ProductCode, and Bits which are used for RSA key generation.
Two methods supported:
- XML Format: Paste VMProtect license export XML
- Text Format: Paste serial keys (one per line)
Generated keys work with VMProtect's VMProtectSetSerialNumber() function and contain:
| Block | Description |
|---|---|
| Version | Serial format version (always 1) |
| Customer Name | Optional identifier (max ~60 chars for 1024-bit keys) |
| HWID | Base64-encoded hardware fingerprint |
| Expiry Date | License expiration (day/month/year) |
| Product Code | 8-byte product identifier |
| Checksum | SHA-1 based CRC for integrity |
The entire payload is RSA-encrypted with PKCS#1 v1.5 padding and Base64-encoded for the final serial number.
| Table | Purpose |
|---|---|
users |
Admin/seller accounts with credit balance (NUMERIC for decimals) |
products |
RSA keys, product codes |
seller_products |
Product-seller assignments |
licenses |
All generated licenses (duration_months, credits_used as NUMERIC) |
credit_transactions |
Credit history with license links |
approval_requests |
Refund/HWID change workflow (stores original HWID and license key) |
settings |
Global app settings |
| Layer | Protection |
|---|---|
| Database | Row Level Security (RLS) enabled, service_role only access, anon/public revoked |
| API | All routes require authentication, role-based authorization |
| Session | HTTP-only cookies, secure flag in production, 7-day expiry |
| RSA Keys | Private keys only accessible to admin via server-side API |
| Product Access | Sellers can only generate licenses for assigned products |
| Credit System | Atomic server-side balance updates prevent race conditions |
Before deploying to production, consider these enhancements:
| Priority | Recommendation | Status |
|---|---|---|
| 🔴 High | Upgrade from SHA-256 to bcrypt for password hashing | TODO |
| 🔴 High | Change default admin password immediately after setup | Manual |
| 🟡 Medium | Add rate limiting to login endpoint (prevent brute force) | TODO |
| 🟡 Medium | Sign session cookies with HMAC to prevent tampering | TODO |
| 🟢 Low | Add CSRF tokens for form submissions | Optional |
| 🟢 Low | Implement account lockout after failed attempts | Optional |
| Variable | Required | Description |
|---|---|---|
NEXT_PUBLIC_SUPABASE_URL |
Yes | Your Supabase project URL |
SUPABASE_SERVICE_ROLE_KEY |
Yes | Service role key ( |
⚠️ Never exposeSUPABASE_SERVICE_ROLE_KEYin client-side code. It bypasses RLS.
| Category | Technology |
|---|---|
| Framework | Next.js 14 (App Router) |
| Database | Supabase (PostgreSQL) |
| Styling | Tailwind CSS |
| Language | TypeScript |
| Icons | Lucide React |
| Dates | date-fns |
| CSV | PapaParse |
Click to expand API reference
| Endpoint | Method | Access | Description |
|---|---|---|---|
/api/auth/login |
POST | Public | Login |
/api/auth/logout |
POST | All | Logout |
/api/auth/session |
GET | All | Get current session |
/api/licenses |
GET | All | List licenses |
/api/licenses |
DELETE | Admin | Bulk delete |
/api/licenses/import |
POST | Admin | Import licenses |
/api/licenses/renew |
POST | All | Renew license |
/api/licenses/reparse |
POST | Admin | Reparse all keys |
/api/generate-licenses |
POST | All | Generate new licenses |
/api/products |
GET/POST/DELETE | Admin | Manage products |
/api/seller-products |
GET/POST/DELETE | Admin | Product assignments |
/api/users |
GET/POST/PATCH/DELETE | Admin | Manage users |
/api/transactions |
GET/POST/DELETE | Varies | Credit transactions |
/api/requests |
GET/POST/PATCH/DELETE | Varies | Approval requests |
/api/settings |
GET/PATCH | All/Admin | App settings |
A system for pre-generated redeemable codes that customers can activate with their HWID:
┌─────────────────────────────────────────────────────────────────────────┐
│ CURRENT WORKFLOW │
├─────────────────────────────────────────────────────────────────────────┤
│ Customer → Seller → "What's your HWID?" → Customer sends HWID │
│ → Seller generates license → Sends to customer │
└─────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────┐
│ WITH REDEEM CODES │
├─────────────────────────────────────────────────────────────────────────┤
│ Seller pre-generates codes → Customer buys code │
│ Customer visits redeem page → Enters code + HWID → Gets license key │
│ (Self-service, no seller interaction needed!) │
└─────────────────────────────────────────────────────────────────────────┘
-- New table: redeem_codes
CREATE TABLE redeem_codes (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
code TEXT UNIQUE NOT NULL, -- e.g., "XXXX-XXXX-XXXX-XXXX"
product_id UUID NOT NULL REFERENCES products(id),
seller_id UUID NOT NULL REFERENCES users(id),
duration_months INTEGER DEFAULT 1,
is_redeemed BOOLEAN DEFAULT FALSE,
redeemed_at TIMESTAMPTZ,
redeemed_license_id UUID REFERENCES licenses(id),
created_at TIMESTAMPTZ DEFAULT NOW(),
expires_at TIMESTAMPTZ -- Optional: code expiry
);
CREATE INDEX idx_redeem_codes_code ON redeem_codes(code);
CREATE INDEX idx_redeem_codes_seller ON redeem_codes(seller_id);| Endpoint | Method | Access | Description |
|---|---|---|---|
/api/redeem-codes |
GET | Seller/Admin | List redeem codes |
/api/redeem-codes |
POST | Seller/Admin | Generate new codes |
/api/redeem-codes/redeem |
POST | Public | Redeem code with HWID |
/api/redeem-codes |
DELETE | Seller/Admin | Delete unused codes |
-
Admin/Seller Dashboard
- New "Redeem Codes" tab
- Bulk generate codes (quantity, product, duration)
- View/export unredeemed codes
- Track redeemed codes with license links
-
Public Redeem Page (
/redeem)- Simple form: Code input + HWID input
- No login required
- Returns license key on success
- Instructions for getting HWID from your software
- Database: Add
redeem_codestable to schema - Types: Add
RedeemCodeinterface totypes.ts - API Routes:
POST /api/redeem-codes- Generate codes (deduct seller credits)GET /api/redeem-codes- List codes with filtersPOST /api/redeem-codes/redeem- Public redeem endpoint
- Frontend:
- Add RedeemCodesTab component to dashboard
- Create
/redeempublic page
- Integration: Link redeemed codes to generated licenses
Extend the system to support license blocking for abuse cases:
- Add
is_blockedandblock_reasonfields to licenses table - Admin can block any license, seller can request blocks
- Blocked licenses appear in software (requires software update to check)
- Option: Publish block list API for software to query
ALTER TABLE licenses ADD COLUMN is_blocked BOOLEAN DEFAULT FALSE;
ALTER TABLE licenses ADD COLUMN block_reason TEXT;
ALTER TABLE licenses ADD COLUMN blocked_at TIMESTAMPTZ;
ALTER TABLE licenses ADD COLUMN blocked_by UUID REFERENCES users(id);Since VMProtect uses offline verification, blocking requires one of:
- Online check: Software queries
/api/licenses/check?hwid=XXXon startup - Block list file: Periodically download signed block list
- Key rotation: Issue new product keys, invalidating old licenses
- Customer accounts with email verification
- View purchased licenses
- Request HWID changes directly
- Download latest software versions
Contributions are welcome! Please feel free to submit a Pull Request.
This project is licensed under the MIT License - see the LICENSE file for details.
















