Click to go to home page

C/C++ ISV Application Development Guide


Preface

Overview Of License Enabling Activities

Defining Your License Management Policies

Deciding On API Choice

Coding Against The Core API

Example Of Single-User License Key Check

Example Of Server License Key Check

Example Of License Key Check With Customization For OEM Key

Coding Against The Extended API

Enabling Deferred-mode Licensing With The Extended API

Coding For Security

Checking Access Controlled Keys

Using The Secure API

Coding For Automatic License State Management

Using Digital Signatures For Anti-Spoofing

Building Your Application

Packaging Your Product

Next Steps


Preface

This document guides you through the specifics of the process of license-enabling your C/C++ product with EasyLicenser.  It adds detail to the overview of the EasyLicenser C/C++ Run Time Library API that is presented in the EasyLicenser Concepts guide.

Overview Of License Enabling Activities

The activities encompass:

    1. Defining your license management policies with respect to grace periods, grace quotas and custom key management.
    2. Deciding which API to use, according to your needs.
    3. Coding against the API.
    4. Packaging your product to include the run time library and the license key.

Defining Your License Management Policies

A key decision you need to make at the onset is whether you wish to enforce locking your license keys to specific hardware and / or operating system. Although locking your key provides a high degree of protection, it is also a sales inhibitor, as it subjects your end-customer to onerous procedures both at the time you take their order and whenever customers want to relocate their product installation. The approach is also less portable across platforms. The next decision you need to make is what you want to lock your key to: proprietary hardware, or the logical operating system runtime environment. If the former, you will need to implement or obtain hardware and operating system specific code and plug it into the EasyLicenser runtime framework. If the latter, you can utilize EasyLicenser's built-in enforcement facilities.

If you are going to node-lock your license keys, another decision you need to make is the tradeoff between convenience and security: whether security requirements override convenience, or whether a short time window of vulnerability is acceptable for the purpose of eliminating the process of explicitly acquiring node lock information from the customer.

When your product is licensed for time limited or metered use, you can decide on what you want to have happen when a license key check indicates an expiring or expired key.  You can define thresholds for generating warnings on expiring keys and grace periods or quotas to suppress generating errors when a quota or expiration date is exceeded. For time limited licenses, you will also need to decide whether the licenses are to be individually issued and tracked, or whether a single relative-time-limited license is to be included with the product distribution for convenience. For metered licenses, you will also need to decide whether to let the EasyLicenser runtime automatically manage metered state.

When your product is licensed according to paid-for options and features, you will accordingly define product options in your license key generation environment, and process the option values in your application.

EasyLicenser has built-in license management models, types and parameters.  If you have a specialized need for your own variation to the built-in policies, you can accomplish this by defining your own custom handler.

The specific mechanics of accomplishing these tasks are described below.

Deciding On API Choice

The EasyLicenser runtime library provides two levels of API's:

Both API's are threadsafe.

Core API

Four API calls are provided in ezLicenseInfo.h for checking a license key:

All core API calls are available with three signatures: a standard API call that is backward compatible with all previous versions of EasyLicenser, an access-control API, new in EasyLicenser 2.0, that accepts two additional parameters: a product name and an application password public key that is backward compatible with EasyLicenser 2.0, and a strict access-control API that accepts a "strict" parameter, which is implicitly false for the older signature for backward compatibility.

The access control API is required to be used with access-controlled keys and will also work with keys that are not based on products having application passwords defined for them. The sole purpose of the standard API call signature is backward compatibility. For all new application development, the access control API signature is recommended. The access control API with strict password check should be used for all new application development, for security.

If your application does not require customized or highly secure license management, you can use either of the first two API’s according to whether your product is a user or server product.  Otherwise, you will find it convenient to use one of the functionally complete APIs.

Extended API

Three key API calls are available with the ezLicenseExtAPI.h header file for managing the lifecycle of a license key at an application installation:

The API is an access-control API, and application password checking is implicitly strict.

All application categories can use the extended API. That is, it is not a requirement that the application require deferred time limited licensing, deferred node locking or automatic metering.

Coding Against The Core API

The specific details of the API are available in the form of in-context comments in the ezLicenseInfo.h C/C++ header file located at the cpp/include directory.  Some simple examples will be illustrated below that show how the API calls are used in typical application scenarios.  Source and binary code for additional examples are available in the cpp/demo directory. In particular, the C_Demo.c application is a simple Hello World application that performs a simple check on a license key provided as an input parameter based on a specified user / host name. For details on the demo program and how to run it, see the description of the directory structure in the Setup, Management And Deployment Guide. The cdemosecure.cpp application is similar to C_Demo.c, but illustrates the use of the secure API. Use of the secure API for automatic state management is illustrated with the cdemostate.c application.

Example Of Single-User License Key Check

Suppose you have a desktop application such as a file management utility program that you sell to end consumers.

In your environment, you create a product definition for your product, which you call FileManagerProduct and with which you associate a secret password MySecret. The License Manager displays a corresponding application password public key rTxWsuxjr4/s3J=. Your application developer embeds the public key into your application and uses the product name and public key at the time of a license key check, as illustrated below.

Whenever a new customer visits your web site and downloads your application and pays by credit card for a perpetual license to use it, your web site defines a serial number or user id, which is also used as the EasyLicenser user name input to a key your web site programmatically generates and emails to your customer.  The key that you generate is a User license model, of User license type.  It is not time limited and does not have a quota limit defined on it.

At product installation time, your product asks the user for the key, which is then stored in the operating system registry.  Subsequently, each time the user runs your product, it prompts your user for the user id authentication information.  Your code can take that user name input, retrieve the license key from the registry, and perform the license key check as follows:

 

// includes

#include <stdlib.h>

#include <time.h>

#include <string.h>

#include <stdio.h>

 

#include <ezLicenseInfo.h>

:

// Get the user name with the appropriate UI widget:

char *username = uiWidget->getText();

 

// Check the license key that is already obtained from the registry into a "config" class.

// (note: "config" is not part of EasyLicenser)

// First, allocate a license info context

void * ezLicInstance = allocLicenseInfo();

int errorCode =

checkSingleUserLicenseKeyBasic(

ezLicInstance,

config->getLicenseKey(),

0, // no grace period for expired keys

0, // not metered license: no quota used to date

0, // metered-license quota threshold N/A

0, // no grace period for expired keys

0, // not metered license: no quota grace

username, // user name matching what’s in the key

"FileManagerProduct", // product name matching what’s in the key

"rTxWsuxjr4/s3J=" // application password public key

);

// errorCode is 0 if success, > 0 if success-with-warning,

// < 0 if error.  Analyze it for specifics.

if (errorCode >= 0) {

// Extract some interesting license params if needed

char *customCookie = getCustomCookie(ezLicInstance);

// analyze custom cookie in application-specific way

analyzeCustomCookie(customCookie);

// Check for warning conditions eg. about-to-expire key

checkWarnings(errorCode);

} else

// check for error codes defined in ezlicStatusCodes.h

checkErrors(errorCode);

// Free license key context

freeLicenseInfo(ezLicInstance);

Example Of Server License Key Check

Suppose you have an application server product Clogix having an application password public key ZrTxWs0xjr4/s3== that you wish to license by the number of concurrent sessions on the application server.  You wish to lock the user into a specific host machine, therefore you adopt an enforced host naming policy that requires your customer to provide you with the DNS host name for their server machine at the time of accepting an order and generating a key.

The key that you generate is of a Server license model, and Concurrent User license type.  At the time of generating the key, you specify an upper limit on the number of concurrent users according to what your customer paid for.  It is not time limited, and you do not define a quota limit on it. The host name that you specify is based on what your customer tells you. Your customer obtains this information through a "uname -a" shell command on the server machine targeted for deployment of your application.

Your server application is designed to load at startup time an application property text file that includes, among other items, the license key and the host name, similar to the following:

:
LicenseKey=tFy67+h=…
HostName=licensedHostName1

:

At run time, either periodically or in response to specific events such as session initiation, your product code can interrogate its environment to obtain the number of active sessions and the application properties and check the license:

 

// includes

#include <stdlib.h>

#include <time.h>

#include <string.h>

#include <stdio.h>

 

#include <ezLicenseInfo.h>

:

// First, get the host name and license key,

// and the active sessionCount

char *hostName = AppProperty.getProperty("HostName");

char *licenseKey = AppProperty.getProperty("LicenseKey");

long sessionCount =

App.getActiveSessionCount(); // the current value

 

// Check the license key

// First, allocate a license info context

void * ezLicInstance = allocLicenseInfo();

int errorCode =

checkMultiUserLicenseKeyBasic(

ezLicInstance,

licenseKey,

0, // no grace period for expired keys

0, // not metered license: no quota used to date

0, // metered-license quota threshold N/A

0, // no grace period for expired keys

0, // not metered license: no quota grace

hostName, // host name matching what’s in the key

sessionCount, // current usage value

"Clogix", // product name matching key

"rTxWsuxjr4/s3J=" // application password public key

);

// errorCode is 0 if success, > 0 if success-with-warning,

// < 0 if error.  Analyze it for specifics.

if (errorCode >= 0) {

// Extract some interesting license params if needed

char *customCookie = getCustomCookie(ezLicInstance);

// analyze custom cookie in application-specific way

analyzeCustomCookie(customCookie);

// Check for warning conditions eg. about-to-expire key

checkWarnings(errorCode);

} else

// check for error codes defined in ezlicStatusCodes.h

checkErrors(errorCode);

// Free license key context

freeLicenseInfo(ezLicInstance);

Example Of License Key Check With Customization For OEM Key

Suppose you are an OEM reseller and your desktop product embeds infrastructure GUI technology obtained from an OEM.  Your OEM would like to ensure that their embedded product can only be used in the context of your product.  It should not be possible for your customer to decompose your product to extract the OEM’s product and make unlicensed use of the latter.  At the same time, you do not wish to impose an additional license management burden on your customer – you wish to distribute a single key to your customer.

Your OEM can provide you with their key, which may or may not be based on EasyLicenser.  At the time of key composition, you can go to the Custom tab and provide the OEM key value in the Custom Key text area as follows:

        tFyU6h7G….

Your application code that checks for the license key will include a custom key handler that recursively performs a license check against the custom key (in this example, we are assuming the key is access controlled as well):

 

// includes

#include <stdlib.h>

#include <time.h>

#include <string.h>

#include <stdio.h>

 

#include <ezLicenseInfo.h>

 

// custom key handler for OEM key processing: fwd reference

// Signature conforms to that specified in <ezLicCustomKeyHandler.h>

int OemKeyHandler(void *keyCtx, const char *customKey);

:

// Get the user name with the appropriate UI widget:

char *username = uiWidget->getText();

 

// Check the license key that is already obtained from the registry into a "config" class.

// (note: "config" is not part of EasyLicenser)

// First, allocate a license info context

void * ezLicInstance = allocLicenseInfo();

int errorCode =

checkLicenseKey(

ezLicInstance,

config->getLicenseKey(),

OemKeyHandler, // custom key handler for processing OEM key

"WidgetMaker", // OEM username passed in as application ctx

0, // no grace period for expired keys

0, // not metered license: no quota used to date

0, // metered-license quota threshold N/A

0, // no grace period for expired keys

0, // not metered license: no quota grace

username, // user name matching what’s in the key

"FileManagerProduct", // product name matching what’s in the key

"rTxWsuxjr4/s3J=" // application password public key

);

// errorCode is 0 if success, > 0 if success-with-warning,

// < 0 if error.  Analyze it for specifics.

if (errorCode >= 0) {

// Success

// Check for warning conditions eg. about-to-expire key

checkWarnings(errorCode);

} else

// check for error codes defined in ezlicStatusCodes.h

checkErrors(errorCode);

// Free license key context

freeLicenseInfo(ezLicInstance);

:

// Custom key handler for OEM key processing

int OemKeyHandler(void *appCtx, const char *customKey) {

// First, allocate a license info context

void * ezLicInstance = allocLicenseInfo();

int errorCode =

checkLicenseKey(

ezLicInstance,

customKey, // custom key is the OEM key

NULL, // no custom key handler for OEM key itself

NULL, // no app ctx

0, // no grace period for expired keys

0, // not metered license: no quota used to date

0, // metered-license quota threshold N/A

0, // no grace period for expired keys

0, // not metered license: no quota grace

(char *)appCtx, // user name matching what’s in the key

"Widget", // product name matching what’s in the OEM key

"TwRxWquZjr43x=" // application password public key

);

// Free license key context

freeLicenseInfo(ezLicInstance);

return errorCode;

}

The above approach can be extended to any number of embedded OEM keys, possibly in combination with other customizations.  To accomplish this, simply provide a list of name-value pairs instead of a single key value and process the entire list.

Coding Against The Extended API

The specific details of the API are available in the form of in-context comments in the ezLicenseExtAPI.h C/C++ header file located at cpp/include directory.  The API is used in essentially the same manner regardless of the license policy that is encoded in the keys, with the difference that an additional auxiliary key license key parameter, corresponding to an enabler key provided by Agilis, is supplied in order to enable deferred-time-limited or deferred-node-locked licensing functionality. If deferred-licensing functionality is not required (for example, corresponding to the scenario where a trial license is upgraded to a production license), a null value is provided for the auxiliary key at the time of activation.

To activate a license for a desktop application installation that is currently not in an activated state, your application's activation logic will invoke the activateExtAPILicense API call (using the same example as the single-user license above), as follows:

 

// includes

#include <stdlib.h>

#include <time.h>

#include <string.h>

#include <stdio.h>

 

#include <ezLicenseInfo.h>

#include <ezLicenseExtAPI.h>

:

// Activate the license key provided by you to your end customer,

// optionally enabled for deferred-mode licensing with an auxiliary key provided by Agilis.

void *licenseInfo = NULL;

int status =

activateExtAPILicense(

config->getLicenseKey(), auxKey, NULL, NULL, userName,

"FileManagerProduct", "rTxWsuxjr4/s3J=",

NULL, NULL, TRUE, auxKey != NULL,

&licenseInfo);

// deferred node locking only if auxiliary key is specified.

if (status < 0) { // error

:

:

} else {

// extract and save key cookie in persistent store:

status = getLicenseExtAPIKeyCookieStr(licenseInfo, strCookie, sizeof(strCookie));

}

// Free license key context

freeLicenseInfo(licenseInfo);


To check the license in the steady state, your application's license checking logic will read the last-saved key cookie from persistent store and then invoke the checkExtAPILicense API call, following which it will save the new key cookie in persistent store, as follows:

 

// includes

#include <stdlib.h>

#include <time.h>

#include <string.h>

#include <stdio.h>

 

#include <ezLicenseInfo.h>

#include <ezLicenseExtAPI.h>

:

// Check the last-obtained license activation record

// that you read from persistent store eg. a registry entry.

// Bump up usage count by one, by specifying -1 for the quota-increment parameter.

void *licenseInfo = NULL;

int status =

checkExtAPILicense(

strCookie, NULL, NULL, 0, -1, 0, 0, userName, 0,

"FileManagerProduct", "rTxWsuxjr4/s3J=",

NULL, NULL, TRUE,

&licenseInfo);

if (status < 0) { // error

:

:

} else {

// extract and save key cookie in persistent store:

status = getLicenseExtAPIKeyCookieStr(licenseInfo, strCookie, sizeof(strCookie));

}

// Free license key context

freeLicenseInfo(licenseInfo);


To deactivate the license (for example at the time of uninstallation), your application's deactivation logic will read the last-saved key cookie from persistent store and then invoke the deactivateLicense API call, as follows:

 

// includes

#include <stdlib.h>

#include <time.h>

#include <string.h>

#include <stdio.h>

 

#include <ezLicenseInfo.h>

#include <ezLicenseExtAPI.h>

:

// Deactivate the last-obtained license activation record

// that you read from persistent store eg. a registry entry.

int status =

deactivateExtAPILicense(

strCookie,

"FileManagerProduct", "rTxWsuxjr4/s3J=",

NULL, NULL, TRUE);

if (status < 0) { // error

:

:

}

// delete key cookie from persistent store


The cdemoextapi.c application, available in the cpp/demo directory, is an application that performs normal or deferred-mode license checks according to whether and what auxiliary license key is provided.

Enabling Deferred-mode Licensing With The Extended API

Deferred-mode functionality is turned on by specifying the appropriate pair of keys: the actual key generated by you, and the enabling auxiliary key generated by Agilis and provided to you, as follows:

Coding For Security

The objective behind coding for security is to thwart the following types of attacks:

Checking Access Controlled Keys

This is illustrated in the previous examples. No programming is required beyond passing the product name and application password public keys as parameters to an access-control API. The product name exactly corresponds to the name of the product that you specified in the License Manager GUI. The application password public key is the value displayed in the Product Details screen in the License Manager GUI when you define an application password for the product, and the product definition is selected at the time of key generation. If the key is not access-controlled, you can pass NULL values for the parameters. If the key is access-controlled, the parameter values are mandatory. For maximum security, specify a value of TRUE for the "strict password check" parameter.

Using The Secure API

The secure API accepts an encrypted key cookie parameter that is initialized at product activation time using a create key cookie seed API call, and saved by the application in persistent store. Every time a license key check is performed, for example upon application startup, the saved key cookie os provided to the secure license key check API call, which validates it and produces and returns an updated key cookie if the license key check is successful. The application replaces the previous key cookie with the new key cookie. If the key cookie is tampered with, or the system clock is turned back to before the timestamp contained in the last-saved key cookie, the license key check will fail regardless of the validity of the license key.

The secure API optionally accepts an application-defined custom exception handler.

The secure API's key cookie also securely manages application license state that changes at run time with successive license key checks. This state information is protected from tampering by the same mechanisms that protect the key cookie. The specifics of using key cookies for secure state management are described below under Coding For Automatic State Management.

For a detailed illustration of how the secure API is used in conjunction with key cookies, please refer to the cdemosecure.cpp source code. Note however that for automatic state management, the preferred approach is to use the higher-level extended API instead.

Coding For Automatic License State Management

The secure API and key cookie mechanism is also used in an enhanced mode to automatically manage license state that changes with successive use at the end customer site. Quota consumption is implicitly tracked in the key cookie as a function of a quota increment that is specified to the license key check call. To differentiate the quota increment from the pre-EasyLicenser 2.0 usage where the quota value always represented a cumulative quota value maintained by the application, a quota increment is represented by a negative value.

In addition to automatically managing quota consumption, the key cookie can also track arbitrary application license state that is supplied at the time of key cookie creation or a secure license key check.

The license state contents of the key cookie can be retrieved through a set of getter methods provided for the purpose.

For a detailed illustration of how the secure API is used in conjunction with key cookies in enhanced mode to automatically manage license state, please refer to the cdemostate.c source code. The above objectives can be met with less coding if you use the higher-level extended API instead of the secure API. The cdemoextapi.c source code illustrates how to use the extended API.

Using Digital Signatures For Anti-Spoofing

The mechanism for protection from client library spoofing is a dynamic digital signature: at your development environment, you generate a digital signature for the EasyLicenser (and / or your own) library that is unique to your application and platform, and is based on a password specified by you. For the EasyLicenser runtime library, you do not have to use the digital signature provided to you by Agilis. You then package or embed with your application the public-key-encrypted versions of your password and signature, and then use these to securely double- check the signature of the library at run time at your end customer's site, as follows:

ISV development environment:

Outside of your actual application, invoke the makeLibDigest function defined in cpp/include/Signature.h and contained in the ilscrypt10 static library, providing as input parameters the EasyLicenser runtime library file name, a password unique to your application and known only to your developers, and the EasyLicenser C/C++ library product name EzlmCppLibrary, as follows:

#include <Signature.h>
:
// Invocation returns a vector of 3 strings:
// signature, public-key-encrypted signature, public-key-encrypted password
char sigbuf[SIGBUFSIZE]; // buffer to receive signature
char encsigbuf[SIGBUFSIZE]; // buffer to receive public-key-encrypted signature
char encpwdbuf[SIGBUFSIZE]; // buffer to receive public-key-encrypted password
if (makeLibDigest(
        "ezLicenserlib20", // shared library name w/o extension
        "myezlmsecret", // your unique secret password
        "EzlmCppLibrary", // product name
        TRUE, // indicate this is running in ISV environment
        sigbuf, sizeof(sigbuf), // output signature buffer
        encsigbuf, sizeof(encsigbuf), // output encrypted signature buffer
        encpwdbuf, sizeof(encpwdbuf)) != 0) { // output encrypted password buffer
    printf("%s\n", sigbuf); // error msg is in signature buffer
    exit(1);
}

You can also use the platform-specific makelibdigest command line utility instead of coding against the API. In either case, you will embed the public key encrypted password into your application, and you will embed or otherwise arrange your application to be parameterized to accept either the signature or its public key encrypted form. For security, it is recommend that you parameterize your application to accept the public key encrypted form of the signature so you don't have to keep it secret and so you only need change a data file whenever the EasyLicenser runtime library is updated. The coding tradeoff is that your application will need to make an extra call at run time to perform an additional public key encryption call.

Runtime application environment:

Code your application to invoke makeLibDigest similar to the above, except that with this API call, provide the public-key-encrypted password and indicate that this is a run time invocation:

#include <Signature.h>
#include <PkCrypt.h>
:
// Invocation returns a vector of 3 strings:
// signature, public-key-encrypted signature, public-key-encrypted password
char sigbuf[SIGBUFSIZE]; // buffer to receive signature
char encsigbuf[SIGBUFSIZE]; // buffer to receive public-key-encrypted signature
char encpwdbuf[SIGBUFSIZE]; // buffer to receive public-key-encrypted password
if (makeLibDigest(
        "ezLicenserlib20", // shared library name w/o extension
        "UYqqRp+iT5asTpa7", // pk-encrypted password corresponding to "myezlmsecret"
        "EzlmCppLibrary", // product name - keep consistent
        FALSE, // indicate this is not running in ISV environment
        sigbuf, sizeof(sigbuf), // output signature buffer
        encsigbuf, sizeof(encsigbuf), // output encrypted signature buffer
        encpwdbuf, sizeof(encpwdbuf)) != 0) { // output encrypted password buffer
    printf("%s\n", sigbuf); // error msg is in signature buffer
    exit(1);
}


If you stored the original actual signature in its public key encrypted form as recommended, first reconstruct the encrypted signature and check it in order to detect spoofing of the cryptographic library itself. Use the EasyLicenser library product's public key for encryption:

char prodPubKey[SIGBUFSIZE];
if (makePublicKey("EzlmCppLibrary", prodPubKey, sizeof(prodPubKey))) {
    // error making public key
    exit(1); // internal error
}

char encrSig[SIGBUFSIZE]; // buffer to receive encrypted signature
if (encryptWithPublicKey(sigbuf,
        prodPubKey, // product public key
        encrSig, sizeof(encrSig)) { // encryption output buffer
    exit(1); // internal encryption error
}
if (strcmp(encrSig,encrsigbuf)) {
    printf("*** Spoofed signature / crypto library ***\n");
    exit(1);
}


Next, compare the resulting signature with the saved signature:

if (strcmp(encrSig, savedOriginalSignature) {
    printf("*** Spoofed EasyLicenser library ***\n");
    exit(1);
}


You should perform the check on the library prior to using any of its functions, for example upon program startup.

There is necessarily a negative consequence of verifying signatures: a new release of the library is necessarily accompanied by an updated signature that requires a new release of your application unless you parameterize your application to accept a signature parameter, for example in a configuration file.

Building Your Application

To compile and link your application modules that perform license management, include the appropriate header files in cpp/include, and link with the libraries in the platform-specific subdirectory of cpp/lib.

Always include the ezLicenseInfo.h header file. Also include ezLicenseExtAPI.h if you are using the extended API. If you are performing explicit encryption operations, also include PkCrypt.h. Include Signature.h if invoking the makeLibDigest API call.

Link your object modules with the ilscrypt10 static library and the ezLicenserlibVV dynamic library (or its import library, if your platform is Windows), both located at the platform-specific subdirectory of cpp/lib.

The platform-specific shared library will need to accompany your product distribution and be included in your product's library path environment variable at your end customer site.

Packaging Your Product

Your product packaging should include the ezLicenserlibVV.so / ezLicenserlibVV.sl / ezLicenserlibVV.dll shared library file located at the platform-specific subdirectory of cpp/lib. Your product installer should deposit this file either in your product installation binary / library files directory or a common library directory. The installer should also ensure that the directory is in the PATH, LD_LIBRARY_PATH or equivalent shared library path environment variable.

Next Steps

Refer to the C/C++ header files located at the cpp/include directory for specific information on the EasyLicenser C/C++ API calls that you will use while writing your product’s license-enabling code.


Copyright © 2002+ Agilis Software LLC, Santa Clara, CA, USA. All rights reserved.