NWTS Java Code

Project site for net.nicholaswilliams.java projects
Last modified March 7, 2012 15:53 UTC

Getting Started

There are three components to using the License Manager: key generation, license creation and license utilization. These three things are all you have to do to use the License Manager.

First, a note about the libraries. License Manager is broken up into several Jar files, and each has a specific purpose:
  • net.nicholaswilliams.java.licensing-core - This is the Jar that you embed in and distribute with your software. You must not ship any other License Manager libraries with your software. Just this one.
  • net.nicholaswilliams.java.licensing-licensor - This is the Jar file that you embed in your license creation application. You will also need the core Jar file in your license creation application.
  • net.nicholaswilliams.java.licensing-sources - This is the sources Jar file that you can attach in your IDE to enable debugging against the License Manager source code.
  • net.nicholaswilliams.java.licensing-javadoc - This is the API documentation Jar file that you can attach in your IDE to enable code hint documentation.
  • net.nicholaswilliams.java.licensing-samples - This Jar file contains various samples for using License Manager.
Finally, we highly recommend you read the API documentation for any License Manager class you use. The examples below are useful, but there are many options and ways to customize the License Manager, and you only get the details by reading the detailed documentation.

Key Generation

Since the License Manager uses public/private key security to protect licenses, you must first generate a public/private key pair. There are several ways to do this. The first way is to use the Command Line Interface (see the documentation). The second method is to use the RSAKeyPairGenerator to generate a key pair and save it encrypted to public and private key files:
import net.nicholaswilliams.java.licensing.encryption.*;
import net.nicholaswilliams.java.licensing.exception.*;
import java.io.*;
import java.security.KeyPair;

public class KeyGenerationExample {
    public static void main(String[] arguments) {
        RSAKeyPairGenerator generator = new RSAKeyPairGenerator();
        
        KeyPair keyPair;
        try {
            keyPair = generator.generateKeyPair();
        } catch(RSA2048NotSupportedException e) { return; }
        
        try {
            generator.saveKeyPairToFiles(keyPair, "private.key", "public.key", "key password".toCharArray());
        } catch(IOException | AlgorithmNotSupportedException | InappropriateKeyException | InappropriateKeySpecificationException e)
            { return; }
    }
}
The final method is to use the RSAKeyPairGenerator to generate a key pair and create implementations of the PrivateKeyDataProvider, PublicKeyDataProvider and PasswordProvider interfaces with the key and password data built in:
import net.nicholaswilliams.java.licensing.encryption.*;
import net.nicholaswilliams.java.licensing.exception.*;
import java.security.KeyPair;
import static net.nicholaswilliams.java.licensing.encryption.RSAKeyPairGeneratorInterface.GeneratedClassDescriptor;

public class KeyGenerationExample {
    public static void main(String[] arguments) {
        RSAKeyPairGenerator generator = new RSAKeyPairGenerator();
        
        KeyPair keyPair;
        try {
            keyPair = generator.generateKeyPair();
        } catch(RSA2048NotSupportedException e) { return; }
        
        GeneratedClassDescriptor rkd = new GeneratedClassDescriptor().
            setPackageName("my.packagename").setClassName("PrivateKeyProvider");
        
        GeneratedClassDescriptor ukd = new GeneratedClassDescriptor().
            setPackageName("my.packagename").setClassName("PublicKeyProvider");
        
        GeneratedClassDescriptor pd = new GeneratedClassDescriptor().
            setPackageName("my.packagename").setClassName("PasswordProvider");
        
        try {
            generator.saveKeyPairToProviders(keyPair, rkd, ukd, "key password".toCharArray());
            generator.savePasswordToProvider("key password".toCharArray(), pd)
        } catch(AlgorithmNotSupportedException | InappropriateKeyException | InappropriateKeySpecificationException e)
            { return; }
        
        System.out.println(rkd.getJavaFileContents() + "\n\n" + ukd.getJavaFileContents() + "\n\n" +
                               pd.getJavaFileContents());
    }
}
If you want to encrypt the public and private keys with different passwords (recommended), you can do that, too:
import net.nicholaswilliams.java.licensing.encryption.*;
import net.nicholaswilliams.java.licensing.exception.*;
import java.io.*;
import java.security.KeyPair;

public class KeyGenerationExample {
    public static void main(String[] arguments) {
        RSAKeyPairGenerator generator = new RSAKeyPairGenerator();
        
        KeyPair keyPair;
        try {
            keyPair = generator.generateKeyPair();
        } catch(RSA2048NotSupportedException e) { return; }
        
        try {
            generator.saveKeyPairToFiles(keyPair, "private.key", "public.key", "private password".toCharArray(), "public password".toCharArray());
        } catch(IOException | AlgorithmNotSupportedException | InappropriateKeyException | InappropriateKeySpecificationException e)
            { return; }
    }
}
import net.nicholaswilliams.java.licensing.encryption.*;
import net.nicholaswilliams.java.licensing.exception.*;
import java.security.KeyPair;
import static net.nicholaswilliams.java.licensing.encryption.RSAKeyPairGeneratorInterface.GeneratedClassDescriptor;

public class KeyGenerationExample {
    public static void main(String[] arguments) {
        RSAKeyPairGenerator generator = new RSAKeyPairGenerator();
        
        KeyPair keyPair;
        try {
            keyPair = generator.generateKeyPair();
        } catch(RSA2048NotSupportedException e) { return; }
        
        GeneratedClassDescriptor rkd = new GeneratedClassDescriptor().
            setPackageName("my.packagename").setClassName("PrivateKeyProvider");
        
        GeneratedClassDescriptor ukd = new GeneratedClassDescriptor().
            setPackageName("my.packagename").setClassName("PublicKeyProvider");
        
        GeneratedClassDescriptor pd1 = new GeneratedClassDescriptor().
            setPackageName("my.packagename").setClassName("PrivatePasswordProvider");
        
        GeneratedClassDescriptor pd2 = new GeneratedClassDescriptor().
            setPackageName("my.packagename").setClassName("PublicPasswordProvider");
        
        try {
            generator.saveKeyPairToProviders(keyPair, rkd, ukd, "private password".toCharArray(), "public password".toCharArray());
            generator.savePasswordToProvider("private password".toCharArray(), pd1)
            generator.savePasswordToProvider("public password".toCharArray(), pd2)
        } catch(AlgorithmNotSupportedException | InappropriateKeyException | InappropriateKeySpecificationException e)
            { return; }
        
        System.out.println(rkd.getJavaFileContents() + "\n\n" + ukd.getJavaFileContents() + "\n\n" + 
                               pd1.getJavaFileContents() + "\n\n" + pd2.getJavaFileContents());
    }
}

License Creation

Creating a license is very simple in License Manager. There is a simple singleton LicenseCreator class that manages the creation of all licenses. You must set a couple properties before getting the instance the first time after the JVM is started. From then on, all other calls to getInstance() can be called alone.

Note that you can either use one password to encrypt your public and private keys and license data, or you can use one password to encrypt your public key and another to encrypt your licenses and private key, or you can use one password to encrypt your private key and another to encrypt your licenses and public key, or you can use one password to encrypt your public and private keys and another password to encrypt your licenses, or you can use a different password for each one. Changing which passwords you use for which depends on which interfaces you implement. We highly recommend you use three different passwords, but more importantly we recommend you pick one scheme and stick to it permanently to prevent having to re-issue licenses.

Application startup:
import net.nicholaswilliams.java.licensing.licensor.*;

public class ApplicationStartupManager
{
    ...
    public void startup()
    {
        ...
        LicenseCreatorProperties.setPrivateKeyDataProvider(new MyPrivateKeyProvider());
        LicenseCreatorProperties.setPrivateKeyPasswordProvider(new MyPrivateKeyPasswordProvider());
        LicenseCreator.getInstance();
    }
    ...
}
License creation:
import net.nicholaswilliams.java.licensing.License;
import net.nicholaswilliams.java.licensing.licensor.*;

public class LicenseCreationService
{
    ...
    public void createLicense()
    {
        ...
        License license = new License.Builder().
                withProductKey("5565-1039-AF89-GGX7-TN31-14AL").
                withHolder("Customer Name").
                withGoodBeforeDate(expirationDate).
                withFeature("FEATURE1").
                withFeature("FEATURE2", feature2ExpDate).
                build();
        
        byte[] licenseData = LicenseCreator.getInstance().signAndSerializeLicense(license, "license password".toCharArray());
        ...    
    }
    ...
}
You can encode the serialized license for plain-text transmission:
String trns = org.apache.commons.codec.binary.Base64.encodeBase64(licenseData);
And decode the license on the other end:
byte[] licenseData = org.apache.commons.codec.binary.Base64.decodeBase64(trns);

License Utilization

Verifying, validating and using licenses is nearly as simple as creating them; just a few more properties to set to create the LicenseManager singleton instance, and a few more interfaces to implement.

Application startup:
import net.nicholaswilliams.java.licensing.*;

public class ClientSoftwareStartupManager
{
    ...
    public void startup()
    {
        ...
        LicenseManagerProperties.setPublicKeyDataProvider(new MyPublicKeyProvider());
        LicenseManagerProperties.setPublicKeyPasswordProvider(new MyPublicKeyPasswordProvider());
        LicenseManagerProperties.setLicenseProvider(new MyLicenseProvider());
        
        // Optional; set only if you are using a different password to encrypt licenses than your public key
        LicenseManagerProperties.setLicensePasswordProvider(new MyLicensePasswordProvider());
        
        // Optional; set only if you wish to validate licenses
        LicenseManagerProperties.setLicenseValidator(new MyLicenseValidator());
        
        // Optional; defaults to 0, which translates to a 10-second (minimum) cache time
        LicenseManagerProperties.setCacheTimeInMinutes(5);
        
        LicenseManager.getInstance();    
    }
    ...
}
License utilization:
import net.nicholaswilliams.java.licensing.*;
import net.nicholaswilliams.java.licensing.exception.*;

public class LicenseUsageService
{
    ...
    public void useLicense()
    {
        ...
        LicenseManager manager = LicenseManager.getInstance();
        
        License license = manager.getLicense("client1");
        try {
            manager.validateLicense(license);
        } catch(ExpiredLicenseException | InvalidLicenseException e) { return; }
        
        int seats = license.getNumberOfLicenses();
        
        boolean bool;
        try {
            bool = manager.hasLicenseForAllFeatures("client2", "feature1", "feature2");
        } catch(ExpiredLicenseException | InvalidLicenseException e) { bool = false; }
        ...    
    }
    ...
}