Skip to content
Nils Christian Ehmke edited this page Jan 9, 2019 · 23 revisions

Additional to this Wiki entry, please consult also the project's JavaDoc.

General Approach

The general approach of liJense is as following. Initially, you are creating a new key pair, consisting of a private and a public key. You use the liJense API to prepare a license file, containing the active features etc. The license file is signed by liJense with the private key. Your application or library, which you want to protected with the license, contains the public key. This application or library uses the liJense API to load a given license file and checks the validity with the public key.

Keys

liJense uses currently RSA keys with the size of 4096 bit. Neither the key algorithm nor the key size can currently be changed via the API. Please visit the JavaDocs for more detailed information on the methods of KeyUtil.

Generating New Keys

Generating a new pair of private and public keys is quite easy. You can use KeyUtil.generateNewKeyPair() to do this. The currently used random generator to create the key pair is SHA1PRNG.

KeyPair keyPair = KeyUtil.generateNewKeyPair( );
PrivateKey privateKey = keyPair.getPrivate( );
PublicKey publicKey = keyPair.getPublic( );

If you want to generate the keys yourself (for instance, if you want to use an own random generator), you can do so and still use them with liJense, as long as the resulting keys are 4096 Bit RSA keys.

Important! Never ever pass the private key on or publish it. You use the private key only to sign the licenses. Your application or library, on the other hand, contains only the public key in order to verify the license.

Saving and Loading Keys

The KeyUtil class provides various methods to store and retrieve keys. In order to save a private or public key to a file, you can use KeyUtil.saveKeyToFile(Key,File). The keys are saved in Base64 encoding.

KeyUtil.saveKeyToFile( privateKey, new File( "key.private" ) );
KeyUtil.saveKeyToFile( publicKey, new File( "key.public" ) );

If you want to store the keys in a database, you might want to consider to use the getEncoded() methods of the keys instead.

byte[] binaryPrivateKey = privateKey.getEncoded();

In order to load keys, liJense provides three methods for the private and the public key respectively. Keys can be loaded from a file, an input stream (useful if the public key is within your JAR file) or from an array containing binary data (useful if the key is loaded from a database blob). Keep in mind that the first two methods expect the data to be Base64 encoded. The last method expect the key in binary format as provided by the getEncoded method.

PrivateKey privateKey = KeyUtil.loadPrivateKeyFromFile( new File( "key.private" ) );
PublicKey publicKey = KeyUtil.loadPublicKeyFromArray( binaryPublicKey );

Calculating and Verifying Fingerprints

When you ship your application or library with the public key to verify a given license, you should also check the fingerprint of the public key. This makes sure that the public key cannot simply be exchanged from your JAR file. The methods KeyUtil.calculateFingerprint(PublicKey) and KeyUtil.calculateFingerprint(byte[]) allow you to calculate the fingerprint of the public key, which is currently the SHA-512 hash of the key.

byte[] fingerprint = KeyUtil.calculateFingerprint( publicKey );

To verify the fingerprint, you can use the method KeyUtil.isFingerprintValid(PublicKey, byte[]). However, you can also use the fingerprint when loading a license to verify the fingerprint implicitly. It is recommended to store the fingerprint directly in a constant expression in the Java code.

private static final byte[] FINGERPRINT = new byte[] { 
    -96, -95, 56, -80, 0, -5, 49, -82, 
    -34, 44, -112, -20, -110, -38, 21, 28, 
    72, 88, 96, 37, -24, 48, -122, 34, 
    -12, 46, -109, 40, -4, -46, 105, -49, 
    117, 59, 30, 124, 4, -67, -107, -90, 
    -62, 115, 110, -102, 127, -126, 119, 78, 
    -75, 46, 30, 101, -53, -49, 59, 71, 
    -97, 54, -58, -38, 31, 102, 58, -122 };

...

if ( KeyUtil.isFingerprintValid( publicKey, FINGERPRINT ) ) {
    ...
}

Licenses

liJense uses currently SHA-512 with RSA for the signature of the license. Currently this cannot be changed via the API. Please visit the JavaDocs for more detailed information on the methods of LicenseUtil.

Creating Licenses

In order to create a license with liJense, you use an instance of ModifiableLicense, which is simply a Properties object with some additional methods. You can add arbitrary key/value pairs here. Some common use cases are:

  • The edition (e.g., enterprise edition)
  • Active features and modules (e.g., LDAP)
  • Limitations (e.g., maximal number of HTTP connections)
  • Expiration date

Various setValue methods in ModifiableLicense allow you to set values for each primitive datatype. Additionally, there is the setExpirationDate(Date) method to set the expiration date of the license. If left empty, the license never expires.

ModifiableLicense license = new ModifiableLicense( );
license.setValue( "edition", "Enterprise Edition" );
license.setValue( "createdAt", new Date( ) );
license.setValue( "feature.ldap.active", true );
license.setValue( "feature.http.maxConnections", 100 );
license.setExpirationDate( expirationDate );

This ModifiableLicense can now be turned into a license by using either LicenseUtil.createLicenseFile(ModifiableLicense, PrivateKey) or LicenseUtil.saveLicenseFile(ModifiableLicense, PrivateKey, File). The first method creates a String containing the license data. The second method saves the license directly on the file system. Both methods use Base64 encoding so that the license file can easily be transfered using a mail.

LicenseUtil.saveLicenseFile( license, privateKey, new File( "license.dat" ) );

Loading Licenses

The LicenseUtil class provides three methods to load a license: From the file system (useful for external license files), from an input stream (useful for licenes in the classpath) and from a Base64 encoded string (useful for licenses loaded from the database). Each of these methods get the license, the public key to verify the license and an optional fingerprint for the public key. It is recommended to always provide this fingerprint in production environment.

UnmodifiableLicense license = LicenseUtil.loadLicenseFile( publicKey, new File( "license.dat" ), Optional.of( FINGERPRINT ) );

Each of the three methods return an instance of UnmodifiableLicense. It is the counterpart of the ModifiableLicense, as it contains all key/value pairs from the license, but cannot be modified via the API. The UnmodifiableLicense provides various methods to retrieve and parse the values, as well as a method to check whether a license is already expired.

System.out.println( "Edition: " + license.getValue( "edition" ) );
System.out.println( "Created at: " + license.getValueAsDate( "createdAt", null ) );
System.out.println( "Feature 'LDAP' active: " + license.isFeatureActive( "feature.ldap.active" ) );
System.out.println( "Max. HTTP connections: " + license.getValueAsInt( "feature.http.maxConnections", 0 ) );

if ( license.isExpired( ) ) {
    ...
}

Clone this wiki locally