Generating and verifying license keys is a common requirement for a lot commercial softwarethese days. From desktop applications such as those built on frameworks like Electronor Qt, to dual-licensed open source packages and libraries like Sidekiq,to a variety of other on-premise software applications and dependencies.
When it comes to software licensing, the key generation and verification algorithms vendorschoose can make or break a licensing system. After an algorithm has been compromised, a vendorcan no longer trust any previously generated license keys, including those belonging to legitend-users.
License Key Generation Algorithm C
Both of these solutions can come at a huge cost, both in terms of end-user trust, support costs,as well as engineering resources. Suffice it to say, it's a bad situation. And ideally, what wewant to do is avoid the situation entirely, by choosing a modern, secure license keyalgorithm from the get-go.
Software cracks usually only work for a single version of a particular application, sincethe application code itself is modified to bypass any license checks (meaning a softwareupdate often requires an updated crack for the new application code.) Distributing acracked version of an application falls on the bad actor.
The other major attack vector is known as a software "keygen", which is much more ominous. Asits name may imply, a keygen is a form of software, often a separate program or webpage, thatgenerates valid license keys, i.e. a key-generator, or "keygen."
Most software vendors have some type of license keygen, which they keep secret. For example, aftera user submits a successful purchase order, part of the order process calls a key generator, whichgenerates a valid, legitimate license key for the new customer.
Depending on your key generation algorithm, a keygen like this may only be able to generate validkey for a single version of an application. But in the worst case, a bad actor can create a keygenthat generates valid license keys that work across all versions of an application, requiringa complete upheaval of the product's licensing system.
Now, we've alluded to this legacy algorithm, which is actually still in use to this day by anumber of software vendors. It's called Partial Key Verification, and although it may seemlike a good-enough system, it is security through obscurity.
These days, writing a partial key verification (PKV) algorithm is actually more work than simplydoing it the right way. But for the sake of understanding, let's write our own partial keyverification system. And then we're going to break it.
Partial Key Verificationis a software license key algorithm that partitions a product key into multiple "subkeys."With each new version of your product, your license key verification algorithm will check a differentsubset of a license's subkeys.
I'd recommend reading the above blog post by Brandon from 2007, with his partial serial numberverification system being written in Delphi. But if you're not into Delphi, we'll be portingthe partial key verification algorithm to Node.
Our PKV keygen should be a tightly kept trade secret, because with it comes the power to craftlicense keys at-will. But we'll soon realize, much to our demise, keeping a PKV keygen secretis actually not possible.
Now, a keygen for production-use may have more subkeys, or the subkeys may be arrangedor intermingled differently, but the algorithm is still going to be more or less thesame. As will the algorithm's vulnerabilities.
The gist of the verification algorithm is that we firstly check key formatting, then we'llverify the checksum is valid. Next, we'll verify the format of the seed value, which if werecall is the first 8 characters of the serial.
If you notice, getSubkeyFromSeed(seed, 24, 3, 200) is deriving an expected 0th subkey from theseed value. We then compare the expected 0th subkey to our license key's actual 0th subkey. If thesubkeys don't match, the license key is not valid.
Let's assume the role of the business once again. We need to fix this. Luckily, our chosenkey algorithm lets us go from verifying the 0th subkey, to verifying the 1st subkey. Allwe have to do is adjust the subkey parameters:
To be frank, Partial Key Verification is a lot of work, especially for a key algorithm thatwe will eventually leak in its entirety. PKV is flawed by its very nature. Sure, the moresubkeys there are, the longer it will take to leak the entire algorithm. Maybe that's awhile.Or maybe the bad actor isn't sophisticated enough to keep a record of subkey parameters.
Some applications will have a central point in the bytecode where this check happens, but othersharden their system by inlining the license key checks, making the work of a bad actor wanting tocrack the software much, much harder. But licensing is all essentially the same: it's a seriesof conditionals.
When choosing a modern license key algorithm, we have a quite a few solid options. For example, ourAPI supports a variety of cryptographic schemes for license keys, from elliptic-curve signatures,to RSA signatures and even encryption. Today, we'll be covering elliptic-curve and RSA-2048 signatures.
The license keys we generate may differ in length, depending on the cryptographic scheme we use,but the format is going to stay the same: some encoded data, a delimiter ".", and a cryptographicsignature of the data. (This is more or less the same format our API uses for cryptographic keys.)
We aren't going to be doing a deep-dive into elliptic-curve cryptography(ECC) today, but that link should curb the curious. Within the ECC category, there are a myriad ofdifferent algorithms. Today, we'll be exploring Ed25519, a modern implementation of a Schnorrsignature system using elliptic-curve groups.
After generating our keypair, we're going to want to keep those encoded keys in a safeplace. We'll use the private signing key for our keygen, and we'll use the publicverify key to verify authenticity of license keys within our application.
What's great about this license key format is that we can embed any dataset intoit that we need. Right now, we're embedding the customer's email, but we could includeother information as well, such as order ID, key expiration date, entitlements, andmore. (It could even be a JSON object, which is actually the default for our API.)
One downside is that the more data you embed, the larger the license keys will become.But in the real world, this isn't really an issue, since the majority of users willcopy-and-paste their license keys, as opposed to typing them in by hand.
And as expected, like our keypair, our license keys are also much larger. But they'resecure. And remember, most users copy-and-paste, so length doesn't really matter.(You could even wrap license keys in a license.dat file, which makes distributiona breeze. But that's just an implementation detail.)
Once again, it takes less than 10 lines of code to verify license keys withinyour application. Our RSA implementation can be improved by using a more modernnon-deterministic padding scheme, PKCS1-PSS (which our API also supports.)
But remember, a crack != a keygen, so your application's licensing always runsthe risk of being circumvented via code modification. But license keys cannotbe forged when you utilize a licensing system built on modern cryptography.
Generating and verifying the authenticity of cryptographically signed license keyslike we've covered will work great for a lot of licensing needs. The implementationis straight forward, it's secure, and these types of license keys work especiallygreat for offline-first perpetual licenses (or a timed license with an embedded,immutable expiry).
I'm working on a project that involves writing low-level C software for a hardware implementation. We are wanting to implement a new feature for our devices that our users can unlock when they purchase an associated license key.
Our hardware is not connected to the internet. Therefore, an algorithm must be implemented in such a way that these keys can be generated from both the server and from within the device. Seeds for the keys can be derived from the hardware serial number, which is available in both locations.
You could just concatenate the serial number of the device, the feature name/code and some secret salt and hash the result with SHA1 (or another secure hashing algorithm). The device compares the given hash to the hash generated for each feature, and if it finds a match it enables the feature.
ECDSA is a public key algorithm like RSA, but with a shorter key and signature size than RSA to provide the same level of effective security. ECDSA is based on a elliptic curve cryptography. Both the integer factorization problem used in RSA and elliptic curve used in ECDSA reduce to discrete logarithm problem, which is believed to be difficult to solve.
Alternatively, if you want to keep 32-characters with base64 encoding, which can hold at most 192-bit of data, you can use ECDSA with 96-bit key size. The effective strength of 96-bit ECDSA is 48-bit, which is generally not strong enough for proper encryption, but in your case it may still be easier for the attacker to reverse engineer the program to remove your license key checks rather than trying to generate a forged key.
I've decided to post my own solution, which is using now proposed on this forum ECDSA private / public key pair, and includes usage of two-way encrypting algorithm about which I have asked in here:Poor man serial number generation scheme, part 2
public class LicenseData protected internal string productCode; protected internal string name; protected internal string email; /// /// Returns the string data input for the CocoaFob algorithm. This implementation returns a comma separated string /// including the , and if set. /// @return /// public virtual string toLicenseStringData() StringBuilder result = new StringBuilder(); if (productCode != null) result.Append(productCode); result.Append(','); //name is mandatory property if (name == null) throw new System.Exception("name cannot be null"); result.Append(name); if (email != null) result.Append(','); result.Append(email); return result.ToString(); ... 2ff7e9595c
Comments