├── .gitignore ├── example ├── private.key ├── public.key ├── template.dat └── license.dat ├── .travis.yml ├── .editorconfig ├── src └── main │ └── java │ └── ro │ └── fortsoft │ └── licensius │ ├── LicenseNotFoundException.java │ ├── LicenseException.java │ ├── LicenseVerifier.java │ ├── PropertiesUtils.java │ ├── KeyGenerator.java │ ├── OrderedProperties.java │ ├── DateUtils.java │ ├── License.java │ ├── LicenseTool.java │ ├── IoUtils.java │ ├── LicenseGenerator.java │ └── LicenseManager.java ├── pom.xml └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | target/ 3 | *.iml 4 | **/.settings 5 | **/.project 6 | **/.classpath -------------------------------------------------------------------------------- /example/private.key: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decebals/licensius/HEAD/example/private.key -------------------------------------------------------------------------------- /example/public.key: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decebals/licensius/HEAD/example/public.key -------------------------------------------------------------------------------- /example/template.dat: -------------------------------------------------------------------------------- 1 | expirationDate=9/12/2020 2 | companyName=HomeOffice 3 | emailAddress=office@home.ro 4 | -------------------------------------------------------------------------------- /example/license.dat: -------------------------------------------------------------------------------- 1 | #License file 2 | #Mon Feb 16 18:20:16 EET 2015 3 | expirationDate=9/12/2020 4 | companyName=HomeOffice 5 | emailAddress=office@home.ro 6 | signature=MCwCFH07yp/QSUzwUQl6gh/5jJApMi3RAhQZVDZhAKXa/BsE2NLgWyoPJsE6dA\=\= 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | jdk: 3 | - openjdk7 4 | # JDK7 is not supported anymore; https://github.com/travis-ci/travis-ci/issues/7884#issuecomment-308451879 5 | # - oraclejdk7 6 | after_success: 7 | - mvn clean cobertura:cobertura coveralls:report 8 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # Pippo code formatting styles 2 | # Checkout http://editorconfig.org/ for more information and how to use it 3 | 4 | root = true 5 | 6 | [*] 7 | charset = utf-8 8 | end_of_line = lf 9 | indent_style = space 10 | indent_size = 4 11 | 12 | #[{*.java,*.xml,*.js,*.css] 13 | trim_trailing_whitespace = true 14 | insert_final_newline = true 15 | 16 | [*.md] 17 | trim_trailing_whitespace = false 18 | -------------------------------------------------------------------------------- /src/main/java/ro/fortsoft/licensius/LicenseNotFoundException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package ro.fortsoft.licensius; 17 | 18 | /** 19 | * @author Decebal Suiu 20 | */ 21 | public class LicenseNotFoundException extends Exception { 22 | 23 | public LicenseNotFoundException() { 24 | super("License file not found"); 25 | } 26 | 27 | public LicenseNotFoundException(String message) { 28 | super(message); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/ro/fortsoft/licensius/LicenseException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package ro.fortsoft.licensius; 17 | 18 | /** 19 | * @author Decebal Suiu 20 | */ 21 | public class LicenseException extends Exception { 22 | 23 | public LicenseException() { 24 | super("Invalid license"); 25 | } 26 | 27 | public LicenseException(Throwable cause) { 28 | super("Invalid license", cause); 29 | } 30 | 31 | public LicenseException(String message) { 32 | super(message); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/ro/fortsoft/licensius/LicenseVerifier.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package ro.fortsoft.licensius; 17 | 18 | /** 19 | * @author Decebal Suiu 20 | */ 21 | public class LicenseVerifier { 22 | 23 | public static void main(String[] args) { 24 | LicenseManager licenseManager = LicenseManager.getInstance(); 25 | try { 26 | License license = licenseManager.getLicense(); 27 | System.out.println("license = " + license); 28 | boolean valid = licenseManager.isValidLicense(license); 29 | System.out.println("valid = " + valid); 30 | } catch (Exception e) { 31 | e.printStackTrace(); 32 | } 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/ro/fortsoft/licensius/PropertiesUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package ro.fortsoft.licensius; 17 | 18 | import java.io.FileInputStream; 19 | import java.io.IOException; 20 | import java.io.InputStream; 21 | import java.util.Properties; 22 | 23 | /** 24 | * @author Decebal Suiu 25 | */ 26 | public class PropertiesUtils { 27 | 28 | public static Properties loadProperties(String propertiesFile) throws IOException { 29 | InputStream input = null; 30 | try { 31 | input = new FileInputStream(propertiesFile); 32 | Properties properties = new OrderedProperties(); 33 | properties.load(input); 34 | 35 | return properties; 36 | } finally { 37 | IoUtils.close(input); 38 | } 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/ro/fortsoft/licensius/KeyGenerator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package ro.fortsoft.licensius; 17 | 18 | import java.security.KeyPair; 19 | import java.security.KeyPairGenerator; 20 | import java.security.SecureRandom; 21 | 22 | /** 23 | * @author Decebal Suiu 24 | */ 25 | public class KeyGenerator { 26 | 27 | public static void createKeys(String publicUri, String privateUri) throws LicenseException { 28 | try { 29 | KeyPairGenerator keyGen = KeyPairGenerator.getInstance("DSA"); 30 | keyGen.initialize(1024, new SecureRandom()); 31 | KeyPair keyPair = keyGen.generateKeyPair(); 32 | 33 | IoUtils.writeFile(publicUri, keyPair.getPublic().getEncoded()); 34 | IoUtils.writeFile(privateUri, keyPair.getPrivate().getEncoded()); 35 | } catch (Exception e) { 36 | throw new LicenseException(e); 37 | } 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/ro/fortsoft/licensius/OrderedProperties.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package ro.fortsoft.licensius; 17 | 18 | import java.util.Collections; 19 | import java.util.Enumeration; 20 | import java.util.LinkedHashMap; 21 | import java.util.Map; 22 | import java.util.Properties; 23 | import java.util.Set; 24 | 25 | /** 26 | * @author Decebal Suiu 27 | */ 28 | public class OrderedProperties extends Properties { 29 | 30 | private final Map linkedMap = new LinkedHashMap<>(); 31 | 32 | @Override 33 | public Object get(Object key) { 34 | return linkedMap.get(key); 35 | } 36 | 37 | @Override 38 | public Object put(Object key, Object value) { 39 | return linkedMap.put(key, value); 40 | } 41 | 42 | @Override 43 | public Object remove(Object key) { 44 | return linkedMap.remove(key); 45 | } 46 | 47 | @Override 48 | public void clear() { 49 | linkedMap.clear(); 50 | } 51 | 52 | @Override 53 | public Enumeration keys() { 54 | return Collections.enumeration(linkedMap.keySet()); 55 | } 56 | 57 | @Override 58 | public Enumeration elements() { 59 | return Collections.enumeration(linkedMap.values()); 60 | } 61 | 62 | @Override 63 | public Set> entrySet() { 64 | return linkedMap.entrySet(); 65 | } 66 | 67 | @Override 68 | public int size() { 69 | return linkedMap.size(); 70 | } 71 | 72 | @Override 73 | public String getProperty(String key) { 74 | return (String) linkedMap.get(key); 75 | } 76 | 77 | @Override 78 | public synchronized boolean containsKey(Object key) { 79 | return linkedMap.containsKey(key); 80 | } 81 | 82 | @Override 83 | public String toString() { 84 | return linkedMap.toString(); 85 | } 86 | 87 | } 88 | -------------------------------------------------------------------------------- /src/main/java/ro/fortsoft/licensius/DateUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package ro.fortsoft.licensius; 17 | 18 | import java.util.Calendar; 19 | import java.util.Date; 20 | 21 | /** 22 | * @author Decebal Suiu 23 | */ 24 | public class DateUtils { 25 | 26 | /** 27 | * Get number of days between two dates 28 | * 29 | * @param first first date 30 | * @param second second date 31 | * @return number of days if first date less than second date, 32 | * 0 if first date is bigger than second date, 33 | * 1 if dates are the same 34 | */ 35 | public static int getNumberOfDays(Date first, Date second) { 36 | int compare = first.compareTo(second); 37 | if (compare > 0) { 38 | return 0; 39 | } else if (compare == 0) { 40 | return 1; 41 | } 42 | 43 | Calendar calendar = Calendar.getInstance(); 44 | calendar.setTime(first); 45 | 46 | int firstDay = calendar.get(Calendar.DAY_OF_YEAR); 47 | int firstYear = calendar.get(Calendar.YEAR); 48 | int firstDays = calendar.getActualMaximum(Calendar.DAY_OF_YEAR); 49 | 50 | calendar.setTime(second); 51 | 52 | int secondDay = calendar.get(Calendar.DAY_OF_YEAR); 53 | int secondYear = calendar.get(Calendar.YEAR); 54 | 55 | int result = 0; 56 | 57 | // if dates in the same year 58 | if (firstYear == secondYear) { 59 | result = secondDay - firstDay + 1; 60 | } else { 61 | // days from the first year 62 | result += firstDays - firstDay + 1; 63 | 64 | // add days from all years between the two dates years 65 | for (int i = firstYear + 1; i < secondYear; i++) { 66 | calendar.set(i, 0, 0); 67 | result += calendar.getActualMaximum(Calendar.DAY_OF_YEAR); 68 | } 69 | 70 | // days from last year 71 | result += secondDay; 72 | } 73 | 74 | return result; 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/ro/fortsoft/licensius/License.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package ro.fortsoft.licensius; 17 | 18 | import java.text.DateFormat; 19 | import java.text.ParseException; 20 | import java.text.SimpleDateFormat; 21 | import java.util.ArrayList; 22 | import java.util.Date; 23 | import java.util.Enumeration; 24 | import java.util.List; 25 | import java.util.Properties; 26 | import java.util.Set; 27 | 28 | /** 29 | * Class for storing license data. It can store any number of features and their values like Properties. 30 | * 31 | * @author Decebal Suiu 32 | */ 33 | public class License { 34 | 35 | public static final String EXPIRATION_DATE = "expirationDate"; 36 | public static final DateFormat DATE_FORMAT = new SimpleDateFormat("MM/dd/yyyy"); 37 | 38 | private Properties features; 39 | 40 | public License(Properties features) throws LicenseException { 41 | this.features = features; 42 | } 43 | 44 | public Date getExpirationDate() { 45 | try { 46 | return DATE_FORMAT.parse(getFeature(EXPIRATION_DATE)); 47 | } catch (ParseException e) { 48 | throw new RuntimeException(e); 49 | } 50 | } 51 | 52 | public String getExpirationDateAsString() { 53 | return getFeature(EXPIRATION_DATE); 54 | } 55 | 56 | public boolean isExpired() { 57 | return System.currentTimeMillis() > getExpirationDate().getTime(); 58 | } 59 | 60 | public int getDaysTillExpire() { 61 | return DateUtils.getNumberOfDays(new Date(), getExpirationDate()); 62 | } 63 | 64 | public String getFeature(String name) { 65 | return features.getProperty(name); 66 | } 67 | 68 | public List getFeatureNames() { 69 | List featureNames = new ArrayList<>(); 70 | Enumeration keys = features.propertyNames(); 71 | while (keys.hasMoreElements()) { 72 | featureNames.add((String) keys.nextElement()); 73 | } 74 | 75 | return featureNames; 76 | } 77 | 78 | @Override 79 | public String toString() { 80 | return features.toString(); 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/ro/fortsoft/licensius/LicenseTool.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package ro.fortsoft.licensius; 17 | 18 | import java.io.File; 19 | import java.io.FileInputStream; 20 | import java.io.OutputStream; 21 | import java.util.Properties; 22 | 23 | /** 24 | * @author Decebal Suiu 25 | */ 26 | public class LicenseTool { 27 | 28 | public static final String TEMPLATE_FILE = "template.dat"; 29 | public static final String PRIVATE_KEY_FILE = "private.key"; 30 | 31 | public static void main(String[] args) { 32 | try { 33 | new LicenseTool().createLicense(); 34 | } catch (Exception e) { 35 | e.printStackTrace(); 36 | } 37 | } 38 | 39 | public static void generateLicense(Properties features, OutputStream output, String privateKeyFile) throws LicenseException { 40 | LicenseGenerator.generateLicense(features, output, privateKeyFile); 41 | } 42 | 43 | public void createLicense() throws Exception { 44 | if (!existsKeyFiles()) { 45 | generateKeys(); 46 | } 47 | 48 | Properties properties = new OrderedProperties(); 49 | properties.load(new FileInputStream(TEMPLATE_FILE)); 50 | 51 | LicenseGenerator.generateLicense(properties, "private.key"); 52 | 53 | System.out.println("License generated in '" + LicenseManager.LICENSE_FILE + "' file."); 54 | } 55 | 56 | private boolean existsKeyFiles() { 57 | return existsPrivateKeyFile() && existsPublicKeyFile(); 58 | } 59 | 60 | private boolean existsPrivateKeyFile() { 61 | File privateKeyFile = new File(PRIVATE_KEY_FILE); 62 | boolean exists = privateKeyFile.exists() && privateKeyFile.isFile(); 63 | 64 | return exists; 65 | } 66 | 67 | private boolean existsPublicKeyFile() { 68 | File publicKeyFile = new File(LicenseManager.PUBLIC_KEY_FILE); 69 | boolean exists = publicKeyFile.exists() && publicKeyFile.isFile(); 70 | 71 | return exists; 72 | } 73 | 74 | private void generateKeys() throws LicenseException { 75 | KeyGenerator.createKeys(LicenseManager.PUBLIC_KEY_FILE, PRIVATE_KEY_FILE); 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/ro/fortsoft/licensius/IoUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package ro.fortsoft.licensius; 17 | 18 | import java.io.ByteArrayOutputStream; 19 | import java.io.Closeable; 20 | import java.io.FileInputStream; 21 | import java.io.FileOutputStream; 22 | import java.io.IOException; 23 | import java.io.InputStream; 24 | 25 | /** 26 | * @author Decebal Suiu 27 | */ 28 | public class IoUtils { 29 | 30 | public static byte[] getBytesFromFile(String file) throws IOException { 31 | return getBytes(new FileInputStream(file)); 32 | } 33 | 34 | public static byte[] getBytesFromResource(String resource) throws IOException { 35 | ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); 36 | byte[] bytes = getBytes(classLoader.getResourceAsStream(resource)); 37 | 38 | return bytes; 39 | } 40 | 41 | public static void writeFile(String file, byte[] bytes) throws IOException { 42 | FileOutputStream output = null; 43 | try { 44 | output = new FileOutputStream(file); 45 | output.write(bytes); 46 | } finally { 47 | IoUtils.close(output); 48 | } 49 | } 50 | 51 | public static byte[] getBytes(InputStream input) throws IOException { 52 | ByteArrayOutputStream output = null; 53 | try { 54 | output = new ByteArrayOutputStream(); 55 | 56 | byte buffer[] = new byte[1024]; 57 | int length; 58 | while ((length = input.read(buffer)) >= 0) { 59 | output.write(buffer, 0, length); 60 | } 61 | 62 | return output.toByteArray(); 63 | } finally { 64 | IoUtils.close(output); 65 | IoUtils.close(input); 66 | } 67 | } 68 | 69 | /** 70 | * Silently closes a Closeable. 71 | * 72 | * @return the exception or null if no exception thrown 73 | */ 74 | public static IOException close(Closeable closeable) { 75 | try { 76 | if (closeable != null) { 77 | closeable.close(); 78 | } 79 | } catch (IOException e) { 80 | return e; 81 | } 82 | 83 | return null; 84 | } 85 | 86 | } 87 | -------------------------------------------------------------------------------- /src/main/java/ro/fortsoft/licensius/LicenseGenerator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package ro.fortsoft.licensius; 17 | 18 | import javax.xml.bind.DatatypeConverter; 19 | import java.io.FileNotFoundException; 20 | import java.io.FileOutputStream; 21 | import java.io.OutputStream; 22 | import java.security.KeyFactory; 23 | import java.security.PrivateKey; 24 | import java.security.Signature; 25 | import java.security.spec.PKCS8EncodedKeySpec; 26 | import java.util.Properties; 27 | 28 | /** 29 | * @author Decebal Suiu 30 | */ 31 | public class LicenseGenerator { 32 | 33 | public static String generateLicense(Properties features, OutputStream output, String privateKeyFile) throws LicenseException { 34 | try { 35 | PrivateKey privateKey = readPrivateKey(privateKeyFile); 36 | 37 | String encoded = features.toString(); 38 | String signature = sign(encoded.getBytes(), privateKey); 39 | 40 | Properties properties = new OrderedProperties(); 41 | properties.putAll(features); 42 | properties.setProperty(LicenseManager.SIGNATURE, signature); 43 | properties.store(output, "License file"); 44 | 45 | return signature; 46 | } catch (Exception e) { 47 | throw new LicenseException(e); 48 | } 49 | } 50 | 51 | public static String generateLicense(Properties features, String privateKeyFile) throws LicenseException { 52 | OutputStream output = null; 53 | try { 54 | output = new FileOutputStream(LicenseManager.LICENSE_FILE); 55 | String license = generateLicense(features, output, privateKeyFile); 56 | 57 | return license; 58 | } catch (FileNotFoundException e) { 59 | throw new LicenseException(e); 60 | } finally { 61 | IoUtils.close(output); 62 | } 63 | } 64 | 65 | private static PrivateKey readPrivateKey(String uri) throws Exception { 66 | PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(IoUtils.getBytesFromFile(uri)); 67 | KeyFactory keyFactory = KeyFactory.getInstance("DSA"); 68 | PrivateKey key = keyFactory.generatePrivate(keySpec); 69 | 70 | return key; 71 | } 72 | 73 | private static String sign(byte[] message, PrivateKey privateKey) throws Exception { 74 | Signature dsa = Signature.getInstance("SHA/DSA"); 75 | dsa.initSign(privateKey); 76 | dsa.update(message); 77 | 78 | byte[] signature = dsa.sign(); 79 | 80 | // TODO use java.util.Base64 from java 8 81 | String encoded = DatatypeConverter.printBase64Binary(signature); 82 | 83 | return encoded; 84 | } 85 | 86 | } 87 | -------------------------------------------------------------------------------- /src/main/java/ro/fortsoft/licensius/LicenseManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package ro.fortsoft.licensius; 17 | 18 | import javax.xml.bind.DatatypeConverter; 19 | import java.io.File; 20 | import java.security.KeyFactory; 21 | import java.security.PublicKey; 22 | import java.security.Signature; 23 | import java.security.spec.X509EncodedKeySpec; 24 | import java.util.Properties; 25 | 26 | /** 27 | * @author Decebal Suiu 28 | */ 29 | public class LicenseManager { 30 | 31 | public static final String LICENSE_FILE = "license.dat"; 32 | public static final String PUBLIC_KEY_FILE = "public.key"; 33 | public static final String SIGNATURE = "signature"; 34 | 35 | private static LicenseManager licenseManager = new LicenseManager(); 36 | 37 | public static LicenseManager getInstance() { 38 | return licenseManager; 39 | } 40 | 41 | public boolean isValidLicense(License license) throws LicenseNotFoundException, LicenseException { 42 | return !license.isExpired(); 43 | } 44 | 45 | public License getLicense() throws LicenseNotFoundException, LicenseException { 46 | if (!new File(LICENSE_FILE).exists()) { 47 | throw new LicenseNotFoundException(); 48 | } 49 | 50 | License license; 51 | try { 52 | license = loadLicense(); 53 | } catch (Exception e) { 54 | throw new LicenseException(e); 55 | } 56 | 57 | return license; 58 | } 59 | 60 | private License loadLicense() throws Exception { 61 | Properties features = PropertiesUtils.loadProperties(LICENSE_FILE); 62 | if (!features.containsKey(SIGNATURE)) { 63 | throw new LicenseException("Missing signature"); 64 | } 65 | 66 | String signature = (String) features.remove(SIGNATURE); 67 | String encoded = features.toString(); 68 | 69 | PublicKey publicKey = readPublicKey(PUBLIC_KEY_FILE); 70 | 71 | if (!verify(encoded.getBytes(), signature, publicKey)) { 72 | throw new LicenseException(); 73 | } 74 | 75 | return new License(features); 76 | } 77 | 78 | private PublicKey readPublicKey(String uri) throws Exception { 79 | byte[] bytes; 80 | File file = new File(uri); 81 | if (file.exists() && file.isFile()) { 82 | bytes = IoUtils.getBytesFromFile(uri); 83 | } else { 84 | bytes = IoUtils.getBytesFromResource(uri); 85 | } 86 | 87 | X509EncodedKeySpec keySpec = new X509EncodedKeySpec(bytes); 88 | KeyFactory keyFactory = KeyFactory.getInstance("DSA"); 89 | 90 | return keyFactory.generatePublic(keySpec); 91 | } 92 | 93 | private boolean verify(byte[] message, String signature, PublicKey publicKey) throws Exception { 94 | Signature dsa = Signature.getInstance("SHA/DSA"); 95 | dsa.initVerify(publicKey); 96 | dsa.update(message); 97 | 98 | // TODO use java.util.Base64 from java 8 99 | byte[] decoded = DatatypeConverter.parseBase64Binary(signature); 100 | 101 | return dsa.verify(decoded); 102 | } 103 | 104 | } 105 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | org.sonatype.oss 6 | oss-parent 7 | 7 8 | 9 | 10 | 4.0.0 11 | ro.fortsoft.licensius 12 | licensius 13 | 0.2.0-SNAPSHOT 14 | jar 15 | Licensius 16 | Tiny License Framework for Java 17 | 18 | 19 | 20 | The Apache Software License, Version 2.0 21 | http://www.apache.org/licenses/LICENSE-2.0.txt 22 | repo 23 | 24 | 25 | 26 | 27 | scm:git:https://github.com/decebals/licensius.git 28 | scm:git:https://github.com/decebals/licensius.git 29 | git@github.com/decebals/licensius.git 30 | HEAD 31 | 32 | 33 | 34 | UTF-8 35 | 1.7 36 | 1.7.7 37 | 4.11 38 | 39 | 40 | 41 | 42 | 43 | true 44 | org.apache.maven.plugins 45 | maven-compiler-plugin 46 | 2.5.1 47 | 48 | ${java.version} 49 | ${java.version} 50 | true 51 | UTF-8 52 | true 53 | true 54 | 55 | 56 | 57 | 58 | 59 | 60 | false 61 | src/main/resources 62 | 63 | 64 | false 65 | src/main/java 66 | 67 | ** 68 | 69 | 70 | **/*.java 71 | 72 | 73 | 74 | 75 | 76 | 77 | false 78 | src/test/resources 79 | 80 | 81 | false 82 | src/test/java 83 | 84 | ** 85 | 86 | 87 | **/*.java 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | org.slf4j 97 | slf4j-api 98 | ${slf4j.version} 99 | 100 | 101 | 102 | 103 | junit 104 | junit 105 | ${junit.version} 106 | test 107 | 108 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Tiny License Framework for Java 2 | ===================== 3 | [![Travis CI Build Status](https://travis-ci.org/decebals/licensius.png)](https://travis-ci.org/decebals/licensius) 4 | [![Maven Central](http://img.shields.io/maven-central/v/ro.fortsoft.licensius/licensius.svg)](http://search.maven.org/#search|ga|1|ro.fortsoft.licensius) 5 | 6 | It's an open source (Apache license) tiny (only 14KB) license framework in Java, with zero dependencies and a quick learning curve. 7 | 8 | How to use 9 | ------------------- 10 | ```java 11 | LicenseManager licenseManager = LicenseManager.getInstance(); 12 | License license = licenseManager.getLicense(); 13 | licenseManager.isValidLicense(license); 14 | ``` 15 | 16 | In `private.key` file exists the private key used to create the license. 17 | In `public.key` file exists the public key used to validate the license. 18 | The public key (`public.key`) is delivered with your application jar. 19 | 20 | To create a new license, you must create a template file (`template.dat`) that contains all license's components 21 | and to run `LicenseTool`. 22 | If the private key or the public key doesn't exists then Licensius will create these files for you. 23 | The license will be a `license.dat` file. 24 | 25 | By default Licensius using DSA algorithm (the key length in bits is 1024) for generating keys (private and public). 26 | 27 | Licensius uses `X509EncodedKeySpec` to encode the public key and `PKCS8EncodedKeySpec` to encode the private key. Both `public.key` file and `private.key` file are binaries. 28 | 29 | In `example` folder you have an example for license template (input) and and its associated license (output). 30 | 31 | The content for `template.dat` can be (see example folder): 32 | ```properties 33 | expirationDate=9/12/2020 34 | companyName=HomeOffice 35 | emailAddress=office@home.ro 36 | ``` 37 | 38 | In this case the content for `license.dat` (the license) is: 39 | ```properties 40 | #License file 41 | #Fri Feb 13 14:57:14 EET 2015 42 | expirationDate=9/12/2020 43 | companyName=HomeOffice 44 | emailAddress=office@home.ro 45 | signature=MCwCFBEzxwxCPSrWwYj1lsfyDDMKEhMmAhRtDi2klZeDojMK3HBF7xaC3OBj9A\=\= 46 | ``` 47 | Using Maven 48 | ------------------- 49 | In your pom.xml you must define the dependencies to Licensius artifacts with: 50 | 51 | ```xml 52 | 53 | ro.fortsoft.licensius 54 | licensius 55 | ${licensius.version} 56 | 57 | ``` 58 | 59 | where ${licensius.version} is the last licensius version. 60 | 61 | You may want to check for the latest released version using [Maven Search](http://search.maven.org/#search%7Cga%7C1%7Clicensius) 62 | 63 | How to build 64 | ------------------- 65 | Requirements: 66 | - [Git](http://git-scm.com/) 67 | - JDK 7 (test with `java -version`) 68 | - [Apache Maven 3](http://maven.apache.org/) (test with `mvn -version`) 69 | 70 | Steps: 71 | - create a local clone of this repository (with `git clone https://github.com/decebals/licensius.git`) 72 | - go to project's folder (with `cd licensius`) 73 | - build the artifacts (with `mvn clean package` or `mvn clean install`) 74 | 75 | After above steps a folder _licensius/target_ is created and all goodies are in that folder. 76 | 77 | Versioning 78 | ------------ 79 | Licensius will be maintained under the Semantic Versioning guidelines as much as possible. 80 | 81 | Releases will be numbered with the follow format: 82 | 83 | `..` 84 | 85 | And constructed with the following guidelines: 86 | 87 | * Breaking backward compatibility bumps the major 88 | * New additions without breaking backward compatibility bumps the minor 89 | * Bug fixes and misc changes bump the patch 90 | 91 | For more information on SemVer, please visit http://semver.org/. 92 | 93 | License 94 | -------------- 95 | Copyright 2015 Decebal Suiu 96 | 97 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with 98 | the License. You may obtain a copy of the License in the LICENSE file, or at: 99 | 100 | http://www.apache.org/licenses/LICENSE-2.0 101 | 102 | Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 103 | an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 104 | specific language governing permissions and limitations under the License. 105 | 106 | --------------------------------------------------------------------------------