Useful functions for server and license validation
The following examples use Javascript. Use them as an implementation reference for your programming language of choice.
To validate a message signature in Javascript, you need the jsrsasign library (download).
With this library you can write a function such as:
function isSignatureValid(message, signature, publicKey) {
var publicKeyHex = b64tohex(publicKey);
var signatureHex = b64tohex(signature);
var validator = new KJUR.crypto.Signature({"alg": "SHA1withRSA"});
validator.init(KEYUTIL.getKey(publicKeyHex, null, "pkcs8pub"));
validator.updateString(message);
var valid = validator.verify(signatureHex);
return valid;
}
To join values separated by ^
, create a function such as this:
function joinValues(){
var out = "";
for(var i = 0; i < arguments.length; i++){
if(arguments[i] !== null && arguments[i] !== undefined) {
if(out.length > 0){
out += '^';
}
out += arguments[i];
}
}
return out;
}
Server response validation
To validate a server response, use:
function isServerResponseValid(publicKey, serverResponseJson) {
var response = JSON.parse(serverResponseJson);
var message = "";
if(response.error){
message = joinValues(response.result, response.error);
} else if (response.license){
var license = response.license;
var product = license.product;
message = joinValues(response.result,
product.name, product.variant, product.version, product.releaseDate,
product.shop, product.id, product.variantId, product.shopId,
license.serialKey, license.email, license.firstName, license.lastName,
license.hostSignature, license.expiration, license.supportEnd, license.signedLicenseKey);
} else {
return false;
}
return isSignatureValid(message, response.signature, publicKey);
}
Testing
Here are some message examples you can use to test the isServerResponseValid
function:
Error response:
var publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCCcHCpt1Qtm+Q63WHF8vT174RoPP+go3hJKkAxZBhCBg4dZ5R/IE9hfXJSF5Kg724jrbT0ft8kIhAOyai47J81g+2tbhlTuVbMhNrhBDoNUDqak5dyRHHc+yrEEV1q2Ed3tX8mTvXHLJ4YoI/TBl5Ht7Zd/s6msTIOwVC8KkYvtwIDAQAB";
var serverResponse = '{"result":"SUPPORT_ENDED","signature":"eK4O/z17UUH+IJd8oVxx326+1Nddv3rKcKQvrGqCPFSEm5CaU3S+NnHxw+dIeoZmKKAPilrxt6x0Et0f36C4qdxTqEqiWksQUUyxHJQs2bzx0+Q58zMy5PaKXH3Bgx641x8bT0CTIhZBfgD18S8fnDqtS/LGqBHzDtO0t0wZpv0=","error":"Your license has no upgrade support for version 4.0.0. The support period ended on 2020-12-31"}';
var valid = isServerResponseValid(publicKey, serverResponse); // --> true
License assignment & validation response:
var publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCCcHCpt1Qtm+Q63WHF8vT174RoPP+go3hJKkAxZBhCBg4dZ5R/IE9hfXJSF5Kg724jrbT0ft8kIhAOyai47J81g+2tbhlTuVbMhNrhBDoNUDqak5dyRHHc+yrEEV1q2Ed3tX8mTvXHLJ4YoI/TBl5Ht7Zd/s6msTIOwVC8KkYvtwIDAQAB";
var serverResponse = '{"result":"VALID","signature":"SyrPUZxqgF4V9MS0C+yGKkMt51ie6V32ro8Qrb0ENKHiemHKql1/J0DYGzzS93CEPe2ClsLzMPU09Pbwvama+A8/Qedfg7HdaXnBk7sI6HSutirgKJwGpN+GmxKzMNNiUb0r1LptLDt/mzX+RqCb1CVLfMGImb/S6fgTZC557vE=","license":{"product":{"name":"license for A","variant":"enterprise","version":"4.0.0","releaseDate":"2023-06-01","shop":"shop1","id":1,"variantId":1,"shopId":1},"serialKey":"Q1KR-L6A5-NIXH-JUZ7","email":"me@email.com","firstName":"me","lastName":"mario","hostSignature":"_SIG_","supportEnd":"2023-06-02","signedLicenseKey":"TkcBt6VwQjBJ+IUIHl/OlHrMga1asmtkhPkdzO92Q/mg+yUsGIxzhL6h0u8O61oT3B1IbyQ8Q+OE2Z0VgWqUw4S83q0fR6zOk81TZ1CYdhqDhoAnSa5XEN4lorbFLOcS5KtsjscsqahEKJE/TOmr0qj562t5VknGP7iSF+QcBUo="}}';
var valid = isServerResponseValid(publicKey, serverResponse); // --> true
Trial license assignment & validation response:
var publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQClN9AqdKc1m0SKNPif9zwfDr0A8rHtreuq46eM2NeN8JbC9TeOuo799rgp42Dnt+LCkHnaupzvNTnk59v56Q1sESHSXjklcazCIqOob1AtY8g9M0lR5ktF45Np8UmOrbf27U0BOwykf6GEyzKCp5Z7ayf6A4opiZzqFdGXIOWc1wIDAQAB";
var serverResponse = '{"result":"VALID","signature":"kN8pNVmCwidw8wpmK+b2qmpRGekD2bMPG7KtELve0OU57VwWM7brRRtldnLeGC24zQhMJMP5OovAZKvbnS0gFDB/3U6Hnc61FC1I9Nqu/zJQ/Su898sd3TknlBmEUkf4MEPUnt2yAZYWT9GvyRaxblyNYJlnv/JrQUn77bZI4s0=","license":{"product":{"name":"license for A","variant":"","version":"1.0.0","releaseDate":"2015-12-12","shop":"shop2","id":2,"variantId":0,"shopId":2},"email":"me@email.com","firstName":"me","lastName":"mario","hostSignature":"_SIG_","expiration":"2016-01-15","supportEnd":"2016-01-15","signedLicenseKey":"OTKJm4s+hn04n6npGtbHfFZcd188aI1K2QBICA+NT80Xxum/Qz2ML4UGpYG1AIIB9mzDO+bnDRS7v+jLWzgf0AU1VmBTinoaUwF4v5nZDIFPB1D0oE+hNhtAdJUoPPCVSAyGSeDeUyBx6gVWbS2n3Qo1VxjqEEQiYu+GdE4x07A="}}';
var valid = isServerResponseValid(publicKey, serverResponse); // --> true
Validating local license integrity
Use this to make sure no one has tampered with the license information your program stores locally:
function isLocalLicenseValid(publicKey, localLicenseJson) {
var license = JSON.parse(localLicenseJson);
var product = license.product;
var message = joinValues(
product.name, product.variant, product.version, product.releaseDate,
product.shop, product.id, product.variantId, product.shopId,
license.serialKey, license.email, license.firstName, license.lastName,
license.hostSignature, license.expiration, license.supportEnd);
return isSignatureValid(message, license.signedLicenseKey, publicKey);
}
Testing
Here are some message examples you can use to test the isLocalLicenseValid
function:
Full license details:
var publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCCcHCpt1Qtm+Q63WHF8vT174RoPP+go3hJKkAxZBhCBg4dZ5R/IE9hfXJSF5Kg724jrbT0ft8kIhAOyai47J81g+2tbhlTuVbMhNrhBDoNUDqak5dyRHHc+yrEEV1q2Ed3tX8mTvXHLJ4YoI/TBl5Ht7Zd/s6msTIOwVC8KkYvtwIDAQAB";
var localLicenseJson = '{"product":{"name":"license for A","variant":"enterprise","version":"4.0.0","releaseDate":"2023-06-01","shop":"shop1","id":1,"variantId":1,"shopId":1},"serialKey":"Q1KR-L6A5-NIXH-JUZ7","email":"me@email.com","firstName":"me","lastName":"mario","hostSignature":"_SIG_","supportEnd":"2023-06-02","signedLicenseKey":"TkcBt6VwQjBJ+IUIHl/OlHrMga1asmtkhPkdzO92Q/mg+yUsGIxzhL6h0u8O61oT3B1IbyQ8Q+OE2Z0VgWqUw4S83q0fR6zOk81TZ1CYdhqDhoAnSa5XEN4lorbFLOcS5KtsjscsqahEKJE/TOmr0qj562t5VknGP7iSF+QcBUo="}';
var valid = isLocalLicenseValid(publicKey, localLicenseJson); --> true
Trial license details:
var publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQClN9AqdKc1m0SKNPif9zwfDr0A8rHtreuq46eM2NeN8JbC9TeOuo799rgp42Dnt+LCkHnaupzvNTnk59v56Q1sESHSXjklcazCIqOob1AtY8g9M0lR5ktF45Np8UmOrbf27U0BOwykf6GEyzKCp5Z7ayf6A4opiZzqFdGXIOWc1wIDAQAB";
var localLicenseJson = '{"product":{"name":"license for A","variant":"","version":"1.0.0","releaseDate":"2015-12-12","shop":"shop2","id":2,"variantId":0,"shopId":2},"email":"me@email.com","firstName":"me","lastName":"mario","hostSignature":"_SIG_","expiration":"2016-01-15","supportEnd":"2016-01-15","signedLicenseKey":"OTKJm4s+hn04n6npGtbHfFZcd188aI1K2QBICA+NT80Xxum/Qz2ML4UGpYG1AIIB9mzDO+bnDRS7v+jLWzgf0AU1VmBTinoaUwF4v5nZDIFPB1D0oE+hNhtAdJUoPPCVSAyGSeDeUyBx6gVWbS2n3Qo1VxjqEEQiYu+GdE4x07A="}';
var valid = isLocalLicenseValid(publicKey, localLicenseJson); // --> true
Validating local license details
Finally, this function checks the license information against the product currently in use, the current host that is running your software, then validates the expiration and support end dates:
function validateLocalLicense(myProductDetailsJson, localLicenseJson, currentHostSignature) {
var myProduct = JSON.parse(myProductDetailsJson).product;
//first check if license contents have not been tampered with.
if (!isLocalLicenseValid(myProduct.publicKey, localLicenseJson)) {
return 'INVALID';
}
var license = JSON.parse(localLicenseJson);
if (myProduct.id !== license.product.id) {
return 'INVALID';
}
if (myProduct.shopId !== license.product.shopId) {
return 'INVALID';
}
if (myProduct.variantId > 0) { //product expects a license for a specific variant. The variant in the license must match
if (myProduct.variantId !== license.product.variantId) {
return 'INVALID';
}
} // else the variant is determined by the license purchased by the customer
// validate that the computer being used is the one in the license
if (currentHostSignature !== license.hostSignature) { // this is a basic string comparison. Use something more sophisticated here if you need.
return 'UNKNOWN_HOST';
}
var currentDate = new Date().getTime();
var productReleaseDate = new Date(myProduct.releaseDate).getTime();
//check if license expired
if (license.expiration !== null && license.expiration !== undefined) {
var licenseExpiration = new Date(license.expiration).getTime();
if (currentDate > licenseExpiration || productReleaseDate > licenseExpiration) {
if (license.serialKey === null || license.serialKey === undefined) { // no serial key? This is a trial license.
return 'TRIAL_EXPIRED';
} else {
return 'EXPIRED';
}
}
}
//check if license can be used with current product version (i.e. it's been released before the support period ends)
if (license.supportEnd !== null && license.supportEnd !== undefined) {
var licenseSupportEnd = new Date(license.supportEnd).getTime();
if (productReleaseDate > licenseSupportEnd) {
return 'SUPPORT_ENDED';
}
}
return 'VALID';
}
Testing
Here are some message examples you can use to test the validateLocalLicense
function:
Full license details:
var localLicenseJson = '{"product":{"name":"license for A","variant":"enterprise","version":"4.0.0","releaseDate":"2023-06-01","shop":"shop1","id":1,"variantId":1,"shopId":1},"serialKey":"Q1KR-L6A5-NIXH-JUZ7","email":"me@email.com","firstName":"me","lastName":"mario","hostSignature":"_SIG_","supportEnd":"2023-06-02","signedLicenseKey":"TkcBt6VwQjBJ+IUIHl/OlHrMga1asmtkhPkdzO92Q/mg+yUsGIxzhL6h0u8O61oT3B1IbyQ8Q+OE2Z0VgWqUw4S83q0fR6zOk81TZ1CYdhqDhoAnSa5XEN4lorbFLOcS5KtsjscsqahEKJE/TOmr0qj562t5VknGP7iSF+QcBUo="}';
var productJson = '{"product":{"name":"license for A","variant":"enterprise","version":"4.0.0","releaseDate":"2023-06-01","shop":"shop1","id":1,"variantId":1,"shopId":1,"publicKey":"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCCcHCpt1Qtm+Q63WHF8vT174RoPP+go3hJKkAxZBhCBg4dZ5R/IE9hfXJSF5Kg724jrbT0ft8kIhAOyai47J81g+2tbhlTuVbMhNrhBDoNUDqak5dyRHHc+yrEEV1q2Ed3tX8mTvXHLJ4YoI/TBl5Ht7Zd/s6msTIOwVC8KkYvtwIDAQAB"}}';
var currentHostSignature = '_SIG_';
var validationResult = validateLocalLicense(productJson, localLicenseJson, currentHostSignature);
// --> VALID
License copied to another device:
var localLicenseJson = '{"product":{"name":"license for A","variant":"enterprise","version":"4.0.0","releaseDate":"2023-06-01","shop":"shop1","id":1,"variantId":1,"shopId":1},"serialKey":"Q1KR-L6A5-NIXH-JUZ7","email":"me@email.com","firstName":"me","lastName":"mario","hostSignature":"_SIG_","supportEnd":"2023-06-02","signedLicenseKey":"TkcBt6VwQjBJ+IUIHl/OlHrMga1asmtkhPkdzO92Q/mg+yUsGIxzhL6h0u8O61oT3B1IbyQ8Q+OE2Z0VgWqUw4S83q0fR6zOk81TZ1CYdhqDhoAnSa5XEN4lorbFLOcS5KtsjscsqahEKJE/TOmr0qj562t5VknGP7iSF+QcBUo="}';
var productJson = '{"product":{"name":"license for A","variant":"enterprise","version":"4.0.0","releaseDate":"2023-06-01","shop":"shop1","id":1,"variantId":1,"shopId":1,"publicKey":"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCCcHCpt1Qtm+Q63WHF8vT174RoPP+go3hJKkAxZBhCBg4dZ5R/IE9hfXJSF5Kg724jrbT0ft8kIhAOyai47J81g+2tbhlTuVbMhNrhBDoNUDqak5dyRHHc+yrEEV1q2Ed3tX8mTvXHLJ4YoI/TBl5Ht7Zd/s6msTIOwVC8KkYvtwIDAQAB"}}';
var currentHostSignature = '_Some Other Computer Hardware_';
var validationResult = validateLocalLicense(productJson, localLicenseJson, currentHostSignature);
// --> UNKNOWN_HOST
Product updated to version 4.5.3, released on 2023-09-04, after support period:
var localLicenseJson = '{"product":{"name":"license for A","variant":"enterprise","version":"4.0.0","releaseDate":"2023-06-01","shop":"shop1","id":1,"variantId":1,"shopId":1},"serialKey":"Q1KR-L6A5-NIXH-JUZ7","email":"me@email.com","firstName":"me","lastName":"mario","hostSignature":"_SIG_","supportEnd":"2023-06-02","signedLicenseKey":"TkcBt6VwQjBJ+IUIHl/OlHrMga1asmtkhPkdzO92Q/mg+yUsGIxzhL6h0u8O61oT3B1IbyQ8Q+OE2Z0VgWqUw4S83q0fR6zOk81TZ1CYdhqDhoAnSa5XEN4lorbFLOcS5KtsjscsqahEKJE/TOmr0qj562t5VknGP7iSF+QcBUo="}';
var productJson = '{"product":{"name":"license for A","variant":"enterprise","version":"4.0.2","releaseDate":"2023-09-04","shop":"shop1","id":1,"variantId":1,"shopId":1,"publicKey":"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCCcHCpt1Qtm+Q63WHF8vT174RoPP+go3hJKkAxZBhCBg4dZ5R/IE9hfXJSF5Kg724jrbT0ft8kIhAOyai47J81g+2tbhlTuVbMhNrhBDoNUDqak5dyRHHc+yrEEV1q2Ed3tX8mTvXHLJ4YoI/TBl5Ht7Zd/s6msTIOwVC8KkYvtwIDAQAB"}}';
var currentHostSignature = '_SIG_';
var validationResult = validateLocalLicense(productJson, localLicenseJson, currentHostSignature);
// --> SUPPORT_ENDED
Expired trial license:
var localLicenseJson = '{"product":{"name":"license for A","variant":"","version":"1.0.0","releaseDate":"2015-12-12","shop":"shop2","id":2,"variantId":0,"shopId":2},"email":"me@email.com","firstName":"me","lastName":"mario","hostSignature":"_SIG_","expiration":"2016-01-15","supportEnd":"2016-01-15","signedLicenseKey":"OTKJm4s+hn04n6npGtbHfFZcd188aI1K2QBICA+NT80Xxum/Qz2ML4UGpYG1AIIB9mzDO+bnDRS7v+jLWzgf0AU1VmBTinoaUwF4v5nZDIFPB1D0oE+hNhtAdJUoPPCVSAyGSeDeUyBx6gVWbS2n3Qo1VxjqEEQiYu+GdE4x07A="}';
var productJson = '{"product":{"name":"license for A","variant":"","version":"1.0.0","releaseDate":"2015-12-12","shop":"shop2","id":2,"variantId":0,"shopId":2,"publicKey":"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQClN9AqdKc1m0SKNPif9zwfDr0A8rHtreuq46eM2NeN8JbC9TeOuo799rgp42Dnt+LCkHnaupzvNTnk59v56Q1sESHSXjklcazCIqOob1AtY8g9M0lR5ktF45Np8UmOrbf27U0BOwykf6GEyzKCp5Z7ayf6A4opiZzqFdGXIOWc1wIDAQAB"}}'
var currentHostSignature = '_SIG_';
var validationResult = validateLocalLicense(productJson, localLicenseJson, currentHostSignature);
// --> TRIAL_EXPIRED
Further Reading
If you find a bug
We deal with errors very seriously and stop the world to fix bugs in less than 24 hours whenever possible. It’s rare to have known issues dangling around for longer than that. We provide a test environment for you to test whether our updates work for you as soon as the adjustments are made.
If you have suggestions or find a bug don’t hesitate to open an issue here or send us send us an e-mail.
We are happy to help if you have any questions in regards to how to use our app for your specific use case. Just send us an e-mail with your query and we’ll reply as soon as humanely possible.
We can work for you
If you don’t have the resources or don’t really want to waste time coding we can build a custom solution for you using our products. We deliver quickly as we know the ins and outs of everything we are dealing with. Send us an e-mail to sales@univocity.com with your requirements and we’ll be happy to assist.
The univocity team.