Click to go to home page

Java 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

Packaging Your Product

Next Steps


Preface

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

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. A related decision 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 that meet most requirements directly. In the event that these do not directly meet your specific requirements and 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 available with the EzLicenseInfo class 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. 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 EzLicenseExtendedAPI class 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 Java Run Time Library API Reference Java documentation.  Some simple examples will be illustrated below that show how the API’s are used in typical application scenarios.  Source and binary code for additional examples are available in the demo and samples directories. In particular, the jdemo.java 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 jdemosecure.java application is similar to jdemo.java, but illustrates the use of the secure API. Use of the secure API for automatic state management is illustrated with the jdemostate.java 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 installer asks the user for the key, which is then stored in the operating system registry.  Subsequently, each time the user runs your application, 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:

 

// imports

import com.vs.ezlicrun.*;

// First, prompt for the login name
String userName =

(String) JOptionPane.showInputDialog(

null,

"Enter User Name:", "Login",

JOptionPane.PLAIN_MESSAGE);

userName = userName.trim();

:

// Check the license key that is already obtained

// from the registry into a "config" class.

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

EzLicenseInfo licenseInfo = new EzLicenseInfo();

try {

int warnings =

licenseInfo.checkSingleUserLicenseKeyBasic(

config.getLicenseKey(),0, 0, 0, 0, 0,

"FileManagerProduct", "rTxWsuxjr4/s3J=");

// last two params are product name and app password public key

} catch (EzLicExceptionBase eek) {

JOptionPane.showMessageDialog(

(JComponent)null,

"You are not licensed to use this product. " +

"Please contact technical support.",

"Licensing Error",

JOptionPane.ERROR_MESSAGE);

System.exit(1);

}

Example Of Server License Key Check

Suppose you have a Unix-based application server product Blogix 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:

 

// imports

import com.vs.ezlicrun.*;

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

// and the active sessionCount
String hostName = AppProperty.getProperty("HostName");
String licenseKey = AppProperty.getProperty("LicenseKey");
long sessionCount = App.getActiveSessionCount(); // the current value
:

// Check the license key

EzLicenseInfo licenseInfo = new EzLicenseInfo();

try {

int warnings =

licenseInfo.checkMultiUserLicenseKeyBasic(

licenseKey, 0, 0, 0, 0, 0,

hostName, sessionCount,

"Blogix", "ZrTxWs0xjr4/s3==");

// last two params are product name and app password public key

} catch (EzLicExceptionBase eek) {

// log warning and / or throw exception

// according to your preference

}

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:

        oemkey=tFyU6h7G….

Your application code that checks for the license key will include a custom key handler that extracts the name-value pair and puts it into a hash table that is maintained by the EasyLicenser run time (in this example, we are assuming the key isn't access controlled):

 

// imports
import com.vs.ezlicrun.*;
EzLicenseInfo licenseInfo = new EzLicenseInfo();
try {

int warnings =

licenseInfo.checkLicenseKey(

licenseKey,

// Built-in custom key handler,

// defined in EasyLicenser Run Time Library

(new EzLicCustomKeyHandler()),

// No application context needed for this handler
null,
0, 0, 0, 0, 0,

username, 0);

} catch (EzLicExceptionBase eek) {

JOptionPane.showMessageDialog(

(JComponent)null,

"You are not licensed to use this product. " +
"Please contact technical support.",
"Licensing Error",
JOptionPane.ERROR_MESSAGE);

System.exit(1);

}

You instruct your OEM to make the following call the first time it is invoked by your code (which necessarily follows the above code fragment in time):

 

String oemkey = (new EzLicCustomKeyHandler()).

getCustomKeyValue("oemkey");

At this point, your OEM can perform the necessary license key check according to the technology that was used to generate the key.  Suppose the OEM used EasyLicenser to generate the OEM key.  Then the OEM’s code would be similar to the following:

 

EzLicenseInfo oemLicenseInfo = new EzLicenseInfo();
int oemwarnings =

oemLicenseInfo.checkSingleUserLicenseKeyBasic(

oemkey,0, 0, 0, 0, 0,

// User name is based on the product /

// company being OEM’ed to

"YourProductName");

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 name-value pair.  The only constraint is that the names should be unique.

Coding Against The Extended API

The specific details of the API are available in the Java Run Time Library API Reference Java documentation.  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 activateLicense API call (using the same example as the single-user license above), as follows:

 

// imports

import com.vs.ezlicrun.*;

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

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

try {

EzLicenseExtendedAPI

licenseInfo =

EasyLicenseExtendedAPI.activateLicense(

config.getLicenseKey(), auxKey, null, null, userName,

"FileManagerProduct", "rTxWsuxjr4/s3J=",

null, null, true, auxKey != null);

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

} catch (EzLicExceptionBase eek) {

:

:

}

// extract and save key cookie in persistent store:

String keycookie = licInfo.getKeyCookie();



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 checkLicense API call, following which it will save the new key cookie in persistent store, as follows:

 

// imports

import com.vs.ezlicrun.*;

// 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.

try {

EzLicenseExtendedAPI

licenseInfo =

EasyLicenseExtendedAPI.checkLicense(

licenseKeyCookie, null, null, 0,

-1, 0, 0, 0, userName, 0,

"FileManagerProduct", "rTxWsuxjr4/s3J=",

null, null, true);

} catch (EzLicExceptionBase eek) {

:

:

}

// extract and save key cookie in persistent store:

licenseKeyCookie = licInfo.getKeyCookie();



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:

 

// imports

import com.vs.ezlicrun.*;

// Deactivate using the last-obtained license activation record

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

try {

EasyLicenseExtendedAPI.deactivateLicense(

licenseKeyCookie,

-1, 0, 0, 0, userName, 0,

"FileManagerProduct", "rTxWsuxjr4/s3J=",

null, true);

} catch (EzLicExceptionBase eek) {

:

:

}



The jdemoextapi.java application, available in the 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:

License key deployment for applications having visible activation

When the application provides an installer or other activation mechanism for prompting the user to enter a license key (whether trial or perpetual), the activation sequence invokes the extended API to deactivate an existing installation and then perform an activation with the newly supplied key, for example a perpetual key representing a replacement for a trial key. The enabling key parameter is ignored in this second activation call if the replacement key is perpetual.

Hot license key deployment for applications lacking visible activation

When a user interface is not available for explicitly controlling activation, and the process of upgrading a license key consists of replacing an existing key with a new key (for a 24x7 server application, for example), the above deployment logic is modified to occur on the fly as follows: at the time a license check would ordinarily be made during normal execution of the application, the license check is preceded by a "new key" check by comparing the relative timestamps of the license key and the key cookie. For example, if the license key and the key cookie are saved in files, the check is simply:

if ((new File(licensekeyfilename)).lastModified() >

(new File(keycookiefilename)).lastModified()) {

// enter deactivation-then-reactivation sequence

}

// continue with normal license check as usual

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. If specified, the exception handler is thrown upon successful execution and can be caught in your application at a location arbitrarily distant from the location of the license key check, making it more difficult to alter the application logic to bypass the license key check. The exception carries with it the warning code and new key cookie.

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, exception based flow control and dynamic invocation, please refer to the jdemosecure.java 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 jdemostate.java 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 jdemoextapi.java source code illustrates how to use the extended API.

Using Digital Signatures For Anti-Spoofing

The mechanism for protection from spoofing of an EasyLicenser library (or your own libraries) 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 is based on a password specified by you. For the EasyLicenser 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 com.vs.ils.crypt.Signature.makeLibDigest method, providing as input parameters the EasyLicenser runtime library jar file name, a password unique to your application and known only to your developers, and the EasyLicenser Java library product name EzlmJavaLibrary, as follows:

// Invocation returns a vector of 3 strings:
// signature, public-key-encrypted signature, public-key-encrypted password
String[] signatureEtc =
    com.vs.ils.crypt.Signature.makeLibDigest(
            "ezlicrun20.jar", // version 2.0 EasyLicenser runtime eg.
            "myezlmsecret", // your chosen password
            "EzlmJavaLibrary", // EasyLicenser runtime library product name
            true); // indicate it's an ISV environment


Follow the invocation by extracting the returned data:

String
    signature = signatureEtc[0]; // The actual signature
String
    pubEncrSig = signatureEtc[1]; // signature encr w/ prod pub key
String
    pubEncrPass = signatureEtc[2]; // passwd encr w/ prod pub key


You can also use the 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 can change a data file whenever the EasyLicenser runtime library is updated. The 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 invoke the com.vs.ils.crypt.Signature.makeLibDigest method 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:

// Invocation returns a vector of 2 strings similar to before:
// signature, public-key-encrypted signature, public-key-encrypted password
String[] signatureEtc =
    com.vs.ils.crypt.Signature.makeLibDigest(
            "ezlicrun20.jar", // version 2.0 EasyLicenser runtime library eg.
            "OCwwLzk4JjA2JzAh", // pk-encrypted password
            "EzlmJavaLibrary", // EasyLicenser runtime library product name
            false); // indicate it's a runtime environment


Follow the invocation by extracting the returned data as before, except that the a password value is not returned:

String
    signature = signatureEtc[0]; // Alleged actual signature
String
    pubEncrSig = signatureEtc[1]; // Alleged signature encr w/ prod pub key
String


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:

// First generate the product's public key:
String prodPubKey = com.vs.ils.crypt.PkCrypt.makePublicKey("EzlmJavaLibrary");
// Next, generate the public-key encryption of the encrypted signature:
signature = PkCrypt.encryptWithPublicKey(signature,prodPubKey);
if (!signature.equals(pubEncrSig)) {
    System.out.println("*** Spoofed signature / crypto library ***");
    System.exit(1);
}


Next, compare the resulting signature with the saved signature:

if (!signature.equals(savedOriginalSignature)) {
    System.out.println("*** Spoofed EasyLicenser runtime library ***");
    System.exit(1);
}


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

If possible, provide an explicit path specification for the library name parameter to the makeLibDigest API call. The path is permitted to be relative, for example ../lib/ezlicrun20.jar. Specifying an explicit path eliminates the need to add the parent directory to your class path. An explicit path specification is also required on Windows, in order to circumvent a bug in the Java runtime when the directory path in a classpath entry contains white spaces.

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.

For an example of how digital signatures are checked in an application, refer to the jdemosecure.java secure demo applicaton.

Packaging Your Product

It is sufficient to copy lib/ezlicrunVV.jar and extlib/ilscrypt10.jar from the EasyLicenser installation root directory into your build environment and include it in your class path. If your application performs a digital signature check and does not provide an explicit path specification for the library name when invoking the makeLibDigest API call, your class path will also need to include the absolute directory path containing the run time library.

When you package your Java product, include the above jar files in your zip file or executable Jar in the usual fashion.

If your application utilizes deferred-mode functionality, your product packaging will include at a minimum the auxiliary key provided by Agilis. If the deferred-mode functionality is specifically deferred time-limited functionality, the packaging will also include the global relative-time-limited license key generated by you as described above.

Next Steps

Refer to the Java Run Time Library API Reference for specific information on the EasyLicenser Java Run Time Library API that you will use while writing your Java product’s license-enabling code.


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