C++ Programming: Implementation of the Licensing System For a Software Product

Table Of Contents

Types of algorithms
Hardware Serial Key
The scheme of the licensing system
The folders structure
Bibliography list


This article is devoted to the development of the key licensing system for the applications. In the theoretical part of the article, we will examine the cryptography methods, which can be used while implementing the licensing system. Also we will discuss all pros and cons of these methods and select the possible ones for using in the application. In the practical part of the article, we will provide the implementation of the simplest licensing system, which guaranties the protection from cracking even if a hacker knows the source code of an algorithm.

Types of algorithms

The cryptographic algorithm, also called cipher, is a mathematical function used for encryption and decryption. Usually, these are two interconnected functions: one is used for encryption, another is for decryption.

If the reliability of the algorithm is based on keeping the algorithm itself in secret, then this algorithm is limited. Limited algorithms do not correspond to the nowadays standards and represent only the historical value.

The modern cryptography solves these problems with the help of the key. The key can be of any value selected from a wide range of values. The set of possible keys is called the key space.

There are two main types of algorithms based on the key usage: symmetric-key algorithm and algorithm with the public key.

Symmetric-key algorithms, sometimes called the conditional algorithms, are the algorithms where the encryption key can be calculated by the decryption key and vice versa. The encryption and decryption keys are the same in most symmetric-key algorithms. These algorithms, also called one-key or secret-key algorithms, require that the sender and the recipient reconcile the used key before the beginning of the secure message exchange. The safety of the symmetric-key algorithm is defined by the key. The key disclosure means that anyone can encrypt and decrypt messages. The key must be kept in secret as long as the transmitted messages should be secret.

Algorithms with the public key, also called asymmetric-key algorithms, are developed in the way that the key used for the encryption differs from that for decryption. Moreover, the decryption key can’t be calculated by the encryption key (at least, during the reasonable period of time). These algorithms are called “algorithms with the public key” because the encryption key can be open: anyone can use the encryption key for the encryption of the message but only one concrete person can decrypt the message with the corresponding decryption key. In these systems, the encryption key is often called the public key and the decryption key is called the private key. The private key is sometimes called the secret key.

It is obvious that the only type of the algorithm which suites us is the algorithm with the public key because we have to store the key in the program for the authentication of the entered serial key. When choosing these algorithms, we have the guarantee that the intruder, having the public key and the source code of the algorithm, won’t be able to make the key generator and create serial keys for another program copies.

There are many computer algorithms. The following three algorithms are most frequently used:

DPS (Data Encryption Standard) is the most popular computer encryption algorithm. It is the American and international standard. It is the symmetric-key algorithm where one and the same key is used for encryption and decryption.

RSA (which stands for RivestShamir and Adleman who first publicly described it) is the most popular algorithm with the public key. It is used both for the encryption and for digital signature.

DSP (Digital Signature Algorithm, is used as the part of the Digital Signature Standard) is another algorithm with the public key. It is used only for the digital signature and can’t be used for the encryption.

DES does not suit us because it is the symmetric-key algorithm.

Two algorithms are left: RSA and DSA. It is easy to choose between them if we look at the structure of the work of these algorithms.

RSA uses the public key for the creation of the cipher text from the source text. We don’t need it as it is supposed that we create and send keys and they will be decrypted and compared with the source value on the client side. As it was mentioned above, we can use only the public key on the client side in order not to compromise the licensing system.

Now some words about the work of DSA. Using the source text, DSA calculates the hash code and then “decrypts” it using the private key and receives the required serial key. It “encrypts” the received value on the client side and receives the hash code. Then it calculates the hash code from the source text in the usual way and compares two values. If these values coincide, then the serial key is valid.

The Crypto++B. Library contains the implementation of many algorithms. I used this library in the example. For more information, see http://www.cryptopp.com/.

bool RsaVerifyVector(const std::string & publicKeyStrHex, const std::string& source, const std::vector<char>& sign)
        CryptoPP::HexDecoder decoder;
        decoder.Put( (byte*)publicKeyStrHex.c_str(), publicKeyStrHex.size() );

        CryptoPP::RSA::PublicKey publicKey;
        publicKey.Load( decoder );

        // Verifier object
        CryptoPP::RSASS<CryptoPP::PSS, CryptoPP::SHA1>::Verifier verifier( publicKey );

        std::vector<char> rawSignature;
        std::string signStr(utils::GetBeginOf(sign), sign.size());
        utils::FromHexString(utils::string2wstring(signStr), &rawSignature);
        // Verify
        const char * pData = utils::GetBeginOf(source);
        return verifier.VerifyMessage( (const byte*) pData,
            source.size(), (const byte*) utils::GetBeginOf(rawSignature), rawSignature.size() );
void RsaSignVector(const std::string & privateKeyStrHex, const std::vector<char> & vec, std::string & sign)
        // Pseudo Random Number Generator
        CryptoPP::AutoSeededRandomPool rng;
        // Generate Parameters
        CryptoPP::InvertibleRSAFunction params;
        params.GenerateRandomWithKeySize( rng, 1536 );

        CryptoPP::HexDecoder decoder;
        decoder.Put( (byte*)privateKeyStrHex.c_str(), privateKeyStrHex.size() );

        CryptoPP::RSA::PrivateKey privateKey; // Private
        privateKey.Load( decoder );

        CryptoPP::RSASS<CryptoPP::PSS, CryptoPP::SHA1>::Signer signer( privateKey );

        size_t length = signer.MaxSignatureLength();
        CryptoPP::SecByteBlock signature( length );

        // Sign message
        signer.SignMessage( rng, (const byte*) utils::GetBeginOf(vec),
            vec.size(), signature );

        sign  = utils::wstring2string(utils::ToHexString<byte>(signature, signature.size()));
void RsaGenerateStringKeys(std::string & publicKeyStr, std::string & privateKeyStr)
        // Pseudo Random Number Generator
        CryptoPP::AutoSeededRandomPool rng;

        // Generate Parameters
        CryptoPP::InvertibleRSAFunction params;
        params.GenerateRandomWithKeySize( rng, 1536 );

        CryptoPP::RSA::PrivateKey privateKey( params );
        CryptoPP::RSA::PublicKey publicKey( params );

        CryptoPP::HexEncoder encoder;

        // save public Key
        encoder.Attach( new CryptoPP::StringSink( publicKeyStr ) );
        publicKey.Save( encoder );

        // save private Key
        encoder.Attach( new CryptoPP::StringSink( privateKeyStr ) );
        privateKey.Save( encoder );

Related Articles

More by Author

Must Read