Java tutorial
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); } } }