A License Generation and Verification System - Part I
Mar 17, 241) Intro
Recently, I’ve been working on a licensing mechanism that should work offline (client’s on-prem environment without internet access). This is an age-old problem, I remember variety of cracking methods & keygens in Windows OS and games. But hopefully, using the concepts I have learned in my cyber security masters degree, I came up with a solution. I want to share a simplified version of it.
In this post, I am roughly specifying the terms, requirements and ideas on the problem. In the future posts, I will share details on how & why we have implemented these in a particular way.
2) Problem
- We have a product that shall be installed in on-prem environments and there can be no internet access after the installation
- We want to control which features are enabled in customers according to the license keys that is provided by us
- We need to do this as secure as possible (focusing specifically: confidentiality, integrity and non-repudiation)
- Many month later, we may provide another license key to the customer and the verification mechanism should work just fine without an internet connection
- Customers should not be able to generate their own license, inject it to our product or bypass the license feature checking mechanism anyhow
3) Defining some terms
- License feature
- the functionality we want to enable
- fields such as: feature name, description, start date, expiry date, and other details (such as feature quota)
- License document
- a JSON formatted document including required information like start date, expiry date and enabled features
- if a feature is not included in license document, then it is not enabled
- License key
- encrypted version of the license document
- encryption is necessary to ensure that document is generated by us, not anyone else
- as a side effect, encryption helps obscuring document details
- License encryption/decryption key
- a symmetric key that will be used during both encryption & decryption of the license document
- License certificates
- PEM formatted X.509 certificates for storing license decryption key
- License verification mechanism
- checking validity of license document and certificates
- whether certificate chaining is correct and license can be decrypted
- whether current date is within start-expiry date range of certificates,
- checking whether current date is within start-expiry date range of license document,
- checking whether a specific feature is in the license document
- (optional) checking whether quota is sufficient for the specific feature
- checking validity of license document and certificates
- License feature check
- checking whether a license feature is enabled & available in the license document
4) Requirements
A license document should be confidential (not in plaintext)
We need an encryption mechanism to ensure confidentiality
A license document should include necessary info
- Client’s basic info (like company name)
- Features enabled
- Start and expiry dates
- JSON format is good enough
A license document cannot be tampered (if so, it should be detected)
- The system should accept only the documents/keys that are generated by us
- Again we can use cryptography for checking license’s validity
- Digital signatures are an option
- Also since a plain-text license document is a simple JSON object, JWT comes to mind
- A JWT can be verified easily with digital signatures
License verification mechanism cannot be bypassed
- In other words, this is not a good idea: storing boolean fields in a Postgresql table, and using that to mark features as on or off
- This is not a good idea either: decrypting the license key, storing the plain-text license document in database and using that plain-text version for license feature checks
- Since the product is in customer’s own environment, they can access database tables and manipulate documents there
- Ideally, everything should be verified every time we want to check whether a feature is allowed or not
- Every time we do a license feature check, we should verify every part:
- license key (to be decrypted and parsed as license document),
- license certificates,
- and whether a specific license feature is in the license document
- In other words, we should only store encrypted data (license key) at rest. We should do decryption & other verifications in run-time
- Here we are assuming that customers are not able to intercept and manipulate our product’s Linux processes in run-time
- Every time we do a license feature check, we should verify every part:
If customers share license keys between each other, the system should detect it
If customer A shares the encrypted license key to customer B, system should reject it in customer B’s environment. To implement this, each customer should have their own license encryption/decryption key
In the next blog posts, I will dive into the reasoning that led us to a particular solution.