I realize that this answer is about 10 years late to the party.
A good software license key/serial number generator consists of more than just a string of random characters or a value from some curve generator. Using a limited alphanumeric alphabet, data can be embedded into a short string (e.g. XXXX-XXXX-XXXX-XXXX) that includes all kinds of useful information such as:
The license key data is then encrypted and then encoded using the limited alphanumeric alphabet. For online validation, the license server holds the secrets for decrypting the information. For offline validation, the decryption secret(s) are included with the software itself along with the decryption/validation code. Obviously, offline validation means the software isn't secure against someone making a keygen.
Probably the hardest part about creating a license key is figuring out how to cram as much data as possible into as few bytes as possible. Remember that users will be entering in their license keys by hand, so every bit counts and users don't want to type extremely long, complex strings in. 16 to 25 character license keys are the most common and balance how much data can be placed into a key vs. user tolerance for entering the key to unlock the software. Slicing up bytes into chunks of bits allows for more information to be included but does increase code complexity of both the generator and validator.
Encryption is a complex topic. In general, standard encryption algorithms like AES have block sizes that don't align with the goal of keeping license key lengths short. Therefore, most developers making their own license keys end up writing their own encryption algorithms (an activity which is frequently discouraged) or don't encrypt keys at all, which guarantees that someone will write a keygen. Suffice it to say that good encryption is hard to do right and a decent understanding of how Feistel networks and existing ciphers work are prerequisites.
Verifying a key is a matter of decoding and decrypting the string, verifying the hash/checksum, checking the product ID and major and minor version numbers in the data, verifying that the license hasn't expired, and doing whatever other checks need to be performed.
Writing a keygen is a matter of knowing what a license key consists of and then producing the same output that the original key generator produces. If the algorithm for license key verification is included in and used by the software, then it is just a matter of creating software that does the reverse of the verification process.
To see what the entire process looks like, here is a blog post I recently wrote that goes over choosing the license key length, the data layout, the encryption algorithm, and the final encoding scheme:
https://cubicspot.blogspot.com/2020/03/adventuring-deeply-into-software-serial.html
A practical, real-world implementation of the key generator and key verifier from the blog post can be seen here:
https://github.com/cubiclesoft/php-misc/blob/master/support/serial_number.php
Documentation for the above class:
https://github.com/cubiclesoft/php-misc/blob/master/docs/serial_number.md
A production-ready open source license server that generates and manages license keys using the above serial number code can be found here:
https://github.com/cubiclesoft/php-license-server
The above license server supports both online and offline validation modes. A software product might start its existence with online only validation. When the software product is ready to retire and no longer supported, it can easily move to offline validation where all existing keys continue to work once the user upgrades to the very last version of the software that switches over to offline validation.
A live demo of how the above license server can be integrated into a website to sell software licenses plus an installable demo application can be found here (both the website and demo app are open source too):
https://license-server-demo.cubiclesoft.com/
Full disclosure: I'm the author of both the license server and the demo site software.