Don't use client side rsa private key for license validation

548 views
Skip to first unread message

Benny G

unread,
Feb 24, 2017, 12:41:43 PM2/24/17
to StarUML
When using RSA for generating software license keys, it's a very bad idea to add the private key to your application! 
I know it's easy because you can encrypt a license with the public key when selling it and the application can decrypt it with the private key to check it.

The problem with this approach is, that a user can simply derive the public key from the private key and thus generate valid licenses...

You can solve this problem by using signing instead of encryption. This way you can sign on server-side with the private key 
and check the signature with the public key on client side and thus you no longer need to add the private key to your application!
Here is a nice example on how to do this for StarUML:

// npm install node-rsa
var NodeRSA = require('node-rsa');

/**
* Class to generate licenses. This code and the rsa private key should be on your side (e.g: server-side code)
* and not be part of your distributed application!
*
* @param rsaPrivateKey
* @constructor
*/
function RSALicenseGenerator(rsaPrivateKey) {
/**
* RSA private key
*/
this.rsaPrivateKey = rsaPrivateKey;

/**
* Generates a license by given params
*
* @param name
* @param product
* @param licenseType
* @param quantity
* @returns {String}
*/
this.generateLicense = function(name, product, licenseType, quantity) {
// build license string by params. we will separate each param with a newline
var licenseString = name + "\n" + product + "\n" + licenseType + "\n" + quantity;

// use the rsa private key to generate a signature for this license string
var rsa = new NodeRSA(this.rsaPrivateKey);
var signature = rsa.sign(licenseString, 'base64');

// create an object containing all the license info's including the signature
var licenseObject = {
name: name,
product: product,
licenseType: licenseType,
quantity: quantity,
signature: signature
};

// convert license object to json string and convert it to base64. this is what you can give to the user as license key
var license = new Buffer(JSON.stringify(licenseObject)).toString('base64');

return license;
}
};


/**
* Class to read license details and validate if a license is valid.
* This code should be part of your application (client-side code) together with your rsa public key
*
* @param rsaPublicKey
* @param starUMLVersion
* @constructor
*/
function RSALicenseReader(rsaPublicKey, starUMLVersion) {
/**
* RSA public key
*/
this.rsaPublicKey = rsaPublicKey;

/**
* StarUML version
*/
this.starUMLVersion = starUMLVersion;

/**
* Validates a given license
*
* @param licenseKey
* @return {Boolean}
*/
this.isValidLicense = function(licenseKey) {
// get license object by base64 license key
var licenseObject = this.getLicenseObject(licenseKey);

// build license string by params. we will separate each param with a newline
var licenseString = licenseObject.name + "\n" + licenseObject.product
+ "\n" + licenseObject.licenseType + "\n" + licenseObject.quantity;

/**
* Use the rsa public key to check the signature for this license string.
*/
var rsa = new NodeRSA(this.rsaPublicKey);
var isValidLicense = rsa.verify(licenseString, licenseObject.signature, 'utf8', 'base64');

// also check if license matches expected StarUML version
var isMatchingStarUMLVersion = (licenseObject.product == this.starUMLVersion);

// returns true if license is valid and star uml version matches expected version
return (isValidLicense && isMatchingStarUMLVersion);
};

/**
* Returns license object by given license key
*
* @param licenseKey
* @return {Object}
*/
this.getLicenseObject = function(licenseKey) {
// convert license from base64 to json string and parse json into object
var licenseObject = JSON.parse(new Buffer(licenseKey, 'base64').toString('utf8'));

return licenseObject;
}
}

// generate dummy rsa private/public key pair
var dummyRsa = new NodeRSA({b:512});

/**
* This part is what you do on your side (e.g. server-side code) to issue a valid license
*/
var privateKey = dummyRsa.exportKey('private');
var licenseGenerator = new RSALicenseGenerator(privateKey);
var licenseKey = licenseGenerator.generateLicense('John Doe', 'STARUML.V2', 'PS', 1);

/**
* This is what the application does (client-side code) to validate the license and get the license information
*/
var publicKey = dummyRsa.exportKey('public');
var licenseReader = new RSALicenseReader(publicKey, 'STARUML.V2');
if (licenseReader.isValidLicense(licenseKey)) {
var licenseObject = licenseReader.getLicenseObject(licenseKey);
console.log('This license is valid and was issued to:', licenseObject.name);
}


StarUML

unread,
Feb 25, 2017, 10:59:21 AM2/25/17
to StarUML
Hi,

Thank you for your feedback. We will consider your idea in the next major upgrade.

Thanks,
StarUML Team

2017년 2월 25일 토요일 오전 2시 41분 43초 UTC+9, Benny G 님의 말:
Reply all
Reply to author
Forward
0 new messages