com.floreantpos.license.FiveStarPOSLicenseManager.java Source code

Java tutorial

Introduction

Here is the source code for com.floreantpos.license.FiveStarPOSLicenseManager.java

Source

package com.floreantpos.license;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URISyntaxException;
import java.net.URLDecoder;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.Signature;
import java.security.SignatureException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.Date;
import java.util.Properties;
import java.util.UUID;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.time.DateUtils;

import oshi.SystemInfo;
import oshi.hardware.ComputerSystem;
import oshi.hardware.HardwareAbstractionLayer;
import oshi.software.os.OperatingSystem;

/**
 * @author Dali Freire - dalifreire@gmail.com
 */
public final class FiveStarPOSLicenseManager {

    public static final String LICENSE_FILE = "license.dat";
    public static final String TEMPLATE_FILE = "template.dat";
    public static final String PUBLIC_KEY_FILE = "public.key";
    public static final String PRIVATE_KEY_FILE = "private.key";

    private static FiveStarPOSLicenseManager instance;

    public static void main(String[] args) throws FileNotFoundException, IOException, NoSuchAlgorithmException,
            InvalidKeyException, InvalidKeySpecException, SignatureException, URISyntaxException {

        /* checks if we need generate the public/private keys */
        boolean genKeysParam = FiveStarPOSLicenseManager.genKeys(args);
        InputStream privateKey = null;
        if (genKeysParam) {

            FiveStarPOSKeyGenerator.createKeys(PUBLIC_KEY_FILE, PRIVATE_KEY_FILE);
            privateKey = new FileInputStream(new File(PRIVATE_KEY_FILE));

        } else {

            File privateKeyFile = FiveStarPOSLicenseManager.getPrivateKey(args);
            if (privateKeyFile.exists()) {
                privateKey = new FileInputStream(new File(PRIVATE_KEY_FILE));
            } else {
                privateKey = FiveStarPOSLicenseManager.class
                        .getResourceAsStream(String.format("/license/%s", PRIVATE_KEY_FILE));
            }

        }

        /* checks if we need create the template file for a new license */
        File templateFile = FiveStarPOSLicenseManager.getTemplateFile(args);
        if (!templateFile.exists()) {
            FiveStarPOSLicenseManager.getInstance().createTemplateFile(templateFile);
        }

        /* creates the new license */
        File licenseFile = new File(LICENSE_FILE);
        FiveStarPOSLicenseManager.getInstance().generateNewLicense(templateFile, privateKey, licenseFile);

    }

    /**
     * Get the {@link FiveStarPOSLicenseManager} instance.
     * 
     * @return {@link FiveStarPOSLicenseManager}
     */
    public static FiveStarPOSLicenseManager getInstance() {
        if (instance == null) {
            instance = new FiveStarPOSLicenseManager();
        }
        return instance;
    }

    /**
     * Returns the license or throw a {@link LicenseException} if something is
     * wrong.
     * 
     * @return {@link License}
     * @throws LicenseException
     */
    public final License getLicense() throws LicenseException {

        try {

            File jarFile = new File(URLDecoder
                    .decode(getClass().getProtectionDomain().getCodeSource().getLocation().getPath(), "UTF-8"));
            String jarDirectory = (jarFile.getAbsolutePath().endsWith(".jar") ? jarFile.getParent()
                    : jarFile.getParentFile().getParent());
            String licenseFilePath = String.format("%s%s%s", jarDirectory, File.separator, LICENSE_FILE);
            File licenseFile = new File(licenseFilePath);
            if (!licenseFile.exists()) {
                throw new LicenseException("License file not found! " + licenseFile.getAbsolutePath());
            }
            return loadLicense(licenseFile);

        } catch (LicenseException e) {
            throw e;
        } catch (Exception e) {
            throw new LicenseException("License file not found!", e);
        }
    }

    /**
     * Returns a generated key that identifies the machine.
     * 
     * @return {@link String}
     */
    public final String getMachineKey() {

        SystemInfo si = new SystemInfo();
        HardwareAbstractionLayer hal = si.getHardware();
        ComputerSystem cs = hal.getComputerSystem();
        OperatingSystem os = si.getOperatingSystem();

        return String.format("%s#%s#%s", hal.getProcessor().getProcessorID(), os.getFamily(),
                cs.getFirmware().getDescription());
    }

    /**
     * Check if we need to generate the public/private keys.
     * 
     * @param args
     * @return boolean
     */
    private static boolean genKeys(String[] args) {
        if (args != null) {
            for (String string : args) {
                boolean genKeys = StringUtils.contains(string, "-k=true");
                if (genKeys) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * Get the template file based on command line arguments.
     * 
     * @param args
     * @return File
     */
    private static File getTemplateFile(String[] args) {
        if (args != null) {
            String templateFileUri = null;
            for (String string : args) {
                if (StringUtils.contains(string, "-t=")) {
                    templateFileUri = StringUtils.splitByWholeSeparatorPreserveAllTokens(string, "-t=")[1];
                    break;
                }
            }
            if (StringUtils.isNotBlank(templateFileUri)) {
                return new File(templateFileUri);
            }
        }
        return new File(TEMPLATE_FILE);
    }

    /**
     * Get the private key file based on command line arguments.
     * 
     * @param args
     * @return File
     */
    private static File getPrivateKey(String[] args) {
        if (args != null) {
            for (String string : args) {
                if (StringUtils.contains(string, "-p=")) {
                    return new File(StringUtils.split(string, "-p=")[1]);
                }
            }
        }
        return new File(PRIVATE_KEY_FILE);
    }

    /**
     * Creates a new template file.
     * 
     * @param templateFile
     * @throws IOException
     */
    private final void createTemplateFile(File templateFile) throws IOException {

        OutputStream output = new FileOutputStream(templateFile);

        Properties properties = new OrderedProperties();
        properties.put(License.LICENSED_TO, "************");
        properties.put(License.PURCHASE_ID, UUID.randomUUID().toString());
        properties.put(License.PURCHASE_DATE, License.DATE_FORMAT.format(new Date()));
        properties.put(License.MACHINE_KEY, "************");
        properties.put(License.BACK_OFFICE, "false");
        properties.put(License.KITCHEN_DISPLAY, "false");
        properties.put(License.EXPIRATION_DATE, License.DATE_FORMAT.format(DateUtils.addDays(new Date(), 30)));
        properties.store(output, "License template file");

        output.close();
    }

    /**
     * Generates a new license file returning the signature for the generated
     * license.
     * 
     * @param templateFile
     * @param privateKey
     * @param licenseFile
     * @return String
     * @throws FileNotFoundException
     * @throws IOException
     * @throws InvalidKeyException
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     * @throws SignatureException
     */
    private final String generateNewLicense(File templateFile, InputStream privateKey, File licenseFile)
            throws FileNotFoundException, IOException, InvalidKeyException, NoSuchAlgorithmException,
            InvalidKeySpecException, SignatureException {

        /* reads the template file */
        Properties properties = new OrderedProperties();
        properties.load(new FileInputStream(templateFile));

        /* check if the required properties is filled */
        if (!properties.containsKey(License.LICENSED_TO)) {
            properties.put(License.LICENSED_TO, "************");
        }
        if (!properties.containsKey(License.PURCHASE_ID)) {
            properties.put(License.PURCHASE_ID, UUID.randomUUID().toString());
        }
        if (!properties.containsKey(License.PURCHASE_DATE)) {
            properties.put(License.PURCHASE_DATE, License.DATE_FORMAT.format(new Date()));
        }
        String machineKey = properties.getProperty(License.MACHINE_KEY);
        if (StringUtils.isBlank(machineKey)) {
            properties.put(License.MACHINE_KEY, getMachineKey());
        }
        if (!properties.containsKey(License.EXPIRATION_DATE)) {
            properties.put(License.EXPIRATION_DATE, License.DATE_FORMAT.format(DateUtils.addMonths(new Date(), 1)));
        }
        if (!properties.containsKey(License.BACK_OFFICE)) {
            properties.put(License.BACK_OFFICE, "false");
        }
        if (!properties.containsKey(License.KITCHEN_DISPLAY)) {
            properties.put(License.KITCHEN_DISPLAY, "false");
        }

        /* generates the license.dat file */
        return FiveStarPOSLicenseGenerator.generateLicense(properties, privateKey, licenseFile);
    }

    private final License loadLicense(File licenseFile) throws LicenseException {

        Properties properties = loadProperties(licenseFile);
        if (!properties.containsKey(License.SIGNATURE)) {
            throw new LicenseException("Invalid license key! Please contact our support.");
        }

        String signature = (String) properties.remove(License.SIGNATURE);
        String encoded = properties.toString();

        PublicKey publicKey = readPublicKey(String.format("/license/%s", PUBLIC_KEY_FILE));

        if (!verify(encoded.getBytes(), signature, publicKey)) {
            throw new LicenseException("Invalid license key! Please contact our support.");
        }

        License license = new License(properties);
        if (license.isExpired()) {
            throw new LicenseException("License key expired! Please contact our support.");
        }
        return license;
    }

    private final PublicKey readPublicKey(String uri) throws LicenseException {
        try {

            InputStream inputStream = getClass().getResourceAsStream(uri);
            byte[] bytes = IOUtils.toByteArray(inputStream);

            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(bytes);
            KeyFactory keyFactory = KeyFactory.getInstance("DSA");

            return keyFactory.generatePublic(keySpec);

        } catch (Exception e) {
            throw new LicenseException("Invalid license key! Please contact our support.", e);
        }
    }

    private boolean verify(byte[] message, String signature, PublicKey publicKey) throws LicenseException {
        try {

            Signature dsa = Signature.getInstance("SHA/DSA");
            dsa.initVerify(publicKey);
            dsa.update(message);

            byte[] decoded = Base64.getDecoder().decode(signature);
            return dsa.verify(decoded);

        } catch (Exception e) {
            throw new LicenseException("Invalid license key! Please contact our support.", e);
        }
    }

    private Properties loadProperties(File propertiesFile) throws LicenseException {
        InputStream input = null;
        try {
            input = new FileInputStream(propertiesFile);
            Properties properties = new OrderedProperties();
            properties.load(input);

            return properties;
        } catch (Exception e) {
            throw new LicenseException("Invalid license file! Please contact our support.", e);
        } finally {
            IOUtils.closeQuietly(input);
        }
    }

}