Cmobilecom License Cloud and Client API

Version 1.1 Developer Guide

Track software (application or library) installations, and issue evaluation and commercial licenses from cloud. Main features include:

License Issuer Instance

Create a license issuer instance on Cmobilecom cloud (cmobilecom.com). Login into the instance and complete the following tasks:
  1. Create license issuer profile. A PKI key store will be created, which contains private key for signing licenses.
  2. Save your license issuer profile as XML and store it in a secure place. It can be imported back into the system if needed.
  3. Export its trust store that needs to be included in your client code. Trust store contains certificate for verifying license signature and secret key for authenticating clients. By default trust store password and key password are the same as the server key store password. It can be changed using java keytool.
  4. Create licensed softwares. A software to be licensed can have a version range. If a client updates the software to a new version in the range, the same license will still be valid for the new version.

Client API

Add cmobilecom maven repository in your build.gradle:
repositories {	
	jcenter()
	mavenCentral()
	
	
	maven {
		url "https://cmobilecom.com/maven/repository/release"
	}
	
}
See Java doc for API references.

License Decoder

If you send users license files directly and your software does not need to interact with license server, License decoder is what you needed, which verifies license signature and docode license file. Add the following decpendencies in your build (e.g. gradle):
	dependencies {
		implementation 'com.cmobilecom:cmobilecom-license-client-core:1.1'
	}
For example,
	InputStream licenseFileInputStream = getClass().getResourceAsStream("/myapp.license");
	InputStreamReader licenseReader = new InputStreamReader(licenseFileInputStream, "UTF-8");

	String issuerTrustStorePath = "/com/example/license/issuer.truststore"; // in classpath
	String issuerTrustStorePassword = "1234567890";
	String issuerTrustStoreKeyPassword = "helloworld";
	LicenseDecoder licenseDecoder = new LicenseDecoder(issuerTrustStorePath,
			issuerTrustStorePassword, null, null, issuerTrustStoreKeyPassword);

	// parse Cmobilecom JPA license
	SoftwareInfo softwareInfo = new SoftwareInfo("com.cmobilecom", "cmobilecom-jpa", "1.1.1");
	License license = licenseDecoder.decode(licenseReader, null, "cmobilecom.com", softwareInfo);
For android, secret key can not be read from keystore, and it needs to be specified in constructor. For example,
	byte[] secretKey = new byte[]{0x39, 0x1a, 0x7c, (byte)0xcf, 0x29, 0x3f, 0x55, 0x18,
		0x5a, (byte)0xab, 0x50, 0x6f, (byte)0xe6, (byte)0xc2, 0x12, (byte)0x92,
		0x3a, (byte)0xe2, (byte)0xf5, 0x7b, 0x33, (byte)0xcc, 0x78, 0x2e};
	LicenseDecoder licenseDecoder = new LicenseDecoder(issuerTrustStorePath,
			issuerTrustStorePassword, null, secretKey);
The secret key must match the secret key of the license issuer profile on server side.

License Accessor

License accessor is used to register a software installation, request an evaluation license from server, retrieve an existing license from server and release a license to server. Add the following decpendencies in your build (e.g. gradle):
	dependencies {
		implementation 'com.cmobilecom:cmobilecom-license-client-accessor:1.1'
	}
Instantiate a LicenseAccessor:

private LicenseAccessor licenseAccessor;

public LicenseAccessor getLicenseAccessor() {
	if (this.licenseAccessor != null)
		return this.licenseAccessor;
		
	// client software info
	SoftwareInfo softwareInfo = new SoftwareInfo("com.example", "Product 001", "1.0.1");
	
	long serverInstanceId = 123456789; // server instance id on cloud
	String licenseLocalCacheRootDir = "/path/license";
	String licenseLocalCacheFileName = "myapp.license";
	String issuerTrustStorePassword = "1234567890";
	
	this.licenseAccessor = new LicenseAccessor(serverInstanceId,
			"example.com", softwareInfo, licenseLocalCacheRootDir, 
			licenseLocalCacheFileName,
			"/com/example/license/issuer.truststore", 
			null, null, issuerTrustStorePassword);
	
	InstallRegister installRegister = licenseAccessor.getInstallRegister();
	installRegister.configure(true, "com/cmobilecom/client/test", "install.signature", 
			new byte[]{0x10, 0x1F, 0x23, 0x38, 0x2F, 0x63, 0x5E, 0x3A, 
					0x52, 0x29, 0x7B, 0x6C, 0x4C, 0x38, 0x1E, 0x28}, 
			new byte[]{0x11, 0x22, 0x53, 0x38, 0x2F, 0x63, 0x5E, 0x3A, 
					0x53, 0x29, 0x73, 0x28, 0x39, 0x5E, 0x2F, 0x23,
					0x39, 0x32, 0x1C, 0x6C});
	
	return this.licenseAccessor;
}
Issuer domain must be the same as the one specified in the license issuer profile on cloud. Configure InstallRegister to override default settings that are used to control how installation signature is encoded and where it is stored.

A license accessor supports multiple licensees using licensee discriminator. Retrieved license is cached locally and its validity time is configured in the licensed software on the server side. If the cached license is expired, it will be retrieved from server side.

Request an evaluation license

A new license will be created on server from a license template. If an existing license is found on server for the user and the software, it will be sent to client without creating a new license.

For example,


	// construct a license template that will be sent to server
	License licenseTemplate = new License();
	licenseTemplate.setLicensee("My Example Company");
	
	// group 1
	LicenseFeatureGroup featureGroup1 = new LicenseFeatureGroup();
	featureGroup1.setGroupId("0001");
	
	// expiration date will be set by server
	Date currentDate = new Date();
	LicenseFeature feature1 = new LicenseFeature("module_1", "feature_1", "1.0",
			Type.EVALUATION, currentDate, null);
	featureGroup1.addFeature(feature1);
	licenseTemplate.addFeatureGroup(featureGroup1);
	
	// group 2
	LicenseFeatureGroup featureGroup2 = new LicenseFeatureGroup();
	featureGroup2.setGroupId("0002");
	
	// expire date will be set by server
	LicenseFeature feature2 = new LicenseFeature("module_2", "feature_2", "1.0.1",
			Type.EVALUATION, currentDate, null);
	featureGroup2.addFeature(feature2);
	licenseTemplate.addFeatureGroup(featureGroup2);
	
	LicenseAccessor licenseAccessor = getLicenseAccessor();	
	CaptchaResponse captchaResponse = new CaptchaResponse("private-key", "remote-ip", "response");
	License license = licenseAccessor.getEvaluationLicense("system", licenseTemplate,
			"system", "123456", true, true, captchaResponse);
Captcha(reCaptcha) response is required to ensure that the call is requested by a human, and it will be validated on the server side. The private key is sent to the server for validation, but it will not be stored on server.

Evaluation expiration date is set by server according to the evaluation days of the licensed software. The license will be bound to the client instance.

Retrieve a license

Retrieve an existing license from server using credential. For example,
	LicenseCredential licCredential = new LicenseCredential(null, "My Company", 
			"20181005CHS4J4V31X2KA8P8", "1234567890");
	LicenseAccessor licenseAccessor = getLicenseAccessor();	
	License license = licenseAccessor.getLicense("system", licCredential, true, true, true);
When retrieving a license from server, and its available quantity on server will decrease by 1. LicenseAccessException will be thrown if there is no such license available.

Save session id for releasing a license to server or retrieving the same license without decreasing available license quantity on server.

Release a license

Release current license to server, and its available quantity on server will increase by 1. So other clients can retrieve the license. For example,
	LicenseCredential licCredential = new LicenseCredential("system", "My Company", 
			"20181005CHS4J4V31X2KA8P8", "1234567890");
	LicenseAccessor licenseAccessor = getLicenseAccessor();	
	licenseAccessor.releaseLicenseToServer(licCredential, license, true);
After a license is released to server, its local cache is removed.

License Listener

When a license is loaded from local cache or from server, put into cache in memory and removed from cache in memory, a listener (if registered) will be notified.

For example,

	LicenseAccessor licenseAccessor = getLicenseAccessor();
	
	LicenseListener licenseListener = new LicenseListener() {

		@Override
		public void licenseLoaded(String licenseeDiscriminator, License license)
				throws LicenseAccessException {
			...
		}

		@Override
		public void licenseAddedToCache(String licenseeDiscriminator, License license)
				throws LicenseAccessException {
			...
		}

		@Override
		public void licenseRemovedFromCache(String licenseeDiscriminator, License license)
				throws LicenseAccessException {
			...
		}
		
	};
	
	licenseAccessor.setLicenseListener(licenseListener);