├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── base ├── .gitignore ├── pom.xml └── src │ └── main │ └── java │ └── ro │ └── esolutions │ └── licensing │ ├── encryption │ ├── FilePrivateKeyDataProvider.java │ ├── PrivateKeyDataProvider.java │ ├── RSAKeyPairGenerator.java │ └── RSAKeyPairGeneratorInterface.java │ ├── exception │ └── RSA2048NotSupportedException.java │ └── licensor │ ├── LicenseCreator.java │ └── LicenseCreatorProperties.java ├── core ├── .gitignore ├── pom.xml └── src │ └── main │ └── java │ └── ro │ └── esolutions │ └── licensing │ ├── DataSignatureManager.java │ ├── DefaultLicenseValidator.java │ ├── DeserializingLicenseProvider.java │ ├── Feature.java │ ├── FeatureRestriction.java │ ├── FeatureRestrictionOperand.java │ ├── FileLicenseProvider.java │ ├── License.java │ ├── LicenseManager.java │ ├── LicenseManagerProperties.java │ ├── LicenseProvider.java │ ├── LicenseSecurityManager.java │ ├── LicenseValidator.java │ ├── ObjectSerializer.java │ ├── SignedLicense.java │ ├── encryption │ ├── Encryptor.java │ ├── FilePublicKeyDataProvider.java │ ├── Hasher.java │ ├── KeyFileUtilities.java │ ├── PasswordProvider.java │ └── PublicKeyDataProvider.java │ ├── exception │ ├── AlgorithmNotSupportedException.java │ ├── CorruptSignatureException.java │ ├── ExpiredLicenseException.java │ ├── FailedToDecryptException.java │ ├── InappropriateKeyException.java │ ├── InappropriateKeySpecificationException.java │ ├── InsecureEnvironmentError.java │ ├── InvalidLicenseException.java │ ├── InvalidSignatureException.java │ ├── KeyNotFoundException.java │ ├── ObjectDeserializationException.java │ ├── ObjectSerializationException.java │ └── ObjectTypeNotExpectedException.java │ └── immutable │ ├── Immutable.java │ ├── ImmutableAbstractCollection.java │ ├── ImmutableArrayList.java │ ├── ImmutableIterator.java │ ├── ImmutableLinkedHashSet.java │ ├── ImmutableListIterator.java │ ├── ImmutableModifiedThroughReflectionException.java │ └── ValidObject.java └── pom.xml /.gitignore: -------------------------------------------------------------------------------- 1 | license-manager.iml 2 | .idea/ 3 | 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | jdk: openjdk17 3 | script: mvn -B -V clean install 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # license-manager 2 | Java library for generation and validation of software licenses (forked from OddSource/java-license-manager). 3 | 4 | [![Build Status](https://travis-ci.org/eSolutionsGrup/license-manager.svg?branch=master)](https://travis-ci.org/eSolutionsGrup/license-manager) 5 | -------------------------------------------------------------------------------- /base/.gitignore: -------------------------------------------------------------------------------- 1 | license-manager-base.iml 2 | target 3 | -------------------------------------------------------------------------------- /base/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 19 | 21 | 4.0.0 22 | 23 | 24 | ro.esolutions 25 | license-manager 26 | 1.0.4-SNAPSHOT 27 | 28 | 29 | license-manager-base 30 | jar 31 | 32 | License Manager - Base 33 | 34 | 35 | 36 | ro.esolutions 37 | license-manager-core 38 | ${project.version} 39 | 40 | 41 | 42 | commons-io 43 | commons-io 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /base/src/main/java/ro/esolutions/licensing/encryption/FilePrivateKeyDataProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * FilePrivateKeyDataProvider.java from LicenseManager modified Thursday, January 24, 2013 16:41:55 CST (-0600). 3 | * 4 | * Copyright 2010-2013 the original author or authors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package ro.esolutions.licensing.encryption; 19 | 20 | import org.apache.commons.io.FileUtils; 21 | 22 | import java.io.File; 23 | import java.io.FileNotFoundException; 24 | import java.io.IOException; 25 | 26 | import ro.esolutions.licensing.exception.KeyNotFoundException; 27 | 28 | /** 29 | * A default implementation of {@link PrivateKeyDataProvider} that reads the private key from a file.
30 | *
31 | * This provider is immutable. Once created, the file that the private key is located at cannot be changed. 32 | * 33 | * @author Nick Williams 34 | * @version 1.0.0 35 | * @since 1.0.0 36 | */ 37 | public class FilePrivateKeyDataProvider implements PrivateKeyDataProvider { 38 | private final File privateKeyFile; 39 | 40 | /** 41 | * Create a new provider, specifying the file from which the private key can be read. 42 | * 43 | * @param privateKeyFile the private key file 44 | */ 45 | public FilePrivateKeyDataProvider(final File privateKeyFile) { 46 | this.privateKeyFile = privateKeyFile.getAbsoluteFile(); 47 | } 48 | 49 | /** 50 | * Create a new provider, specifying the name of the file from which the private key can be read. 51 | * 52 | * @param privateKeyFileName The private key file name 53 | */ 54 | public FilePrivateKeyDataProvider(final String privateKeyFileName) { 55 | this.privateKeyFile = new File(privateKeyFileName).getAbsoluteFile(); 56 | } 57 | 58 | /** 59 | * This method returns the data from the file containing the encrypted 60 | * private key from the public/private key pair. The contract for this 61 | * method can be fulfilled by storing the data in a byte array literal 62 | * in the source code itself.
63 | *
64 | * It is imperative that you obfuscate the bytecode for the 65 | * implementation of this class. It is also imperative that the byte 66 | * array exist only for the life of this method (i.e., DO NOT store it as 67 | * an instance or class field). 68 | * 69 | * @return the encrypted file contents from the private key file. 70 | * @throws ro.esolutions.licensing.exception.KeyNotFoundException if the key data could not be retrieved; an 71 | * acceptable message or chained cause must be 72 | * provided. 73 | */ 74 | @Override 75 | public byte[] getEncryptedPrivateKeyData() throws KeyNotFoundException { 76 | try { 77 | return FileUtils.readFileToByteArray(this.privateKeyFile); 78 | } catch (final FileNotFoundException e) { 79 | throw new KeyNotFoundException("The private key file [" + this.privateKeyFile.getPath() + 80 | "] does not exist."); 81 | } catch (final IOException e) { 82 | throw new KeyNotFoundException("Could not read from the private key file [" + 83 | this.privateKeyFile.getPath() + "].", e); 84 | } 85 | } 86 | 87 | /** 88 | * Gets the file that the private key is located at. 89 | * 90 | * @return the file. 91 | */ 92 | public File getPrivateKeyFile() { 93 | return this.privateKeyFile; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /base/src/main/java/ro/esolutions/licensing/encryption/PrivateKeyDataProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * PrivateKeyDataProvider.java from LicenseManager modified Thursday, January 24, 2013 16:37:10 CST (-0600). 3 | * 4 | * Copyright 2010-2013 the original author or authors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package ro.esolutions.licensing.encryption; 20 | 21 | import ro.esolutions.licensing.exception.KeyNotFoundException; 22 | 23 | /** 24 | * Specifies an interface for retrieving the private key file data. This 25 | * interface only needs to be implemented in the application generating the 26 | * licenses. It need not (and for security reasons should not) be implemented 27 | * in the application using the licenses. 28 | * 29 | * @author Nick Williams 30 | * @version 1.0.0 31 | * @since 1.0.0 32 | */ 33 | public interface PrivateKeyDataProvider { 34 | /** 35 | * This method returns the data from the file containing the encrypted 36 | * private key from the public/private key pair. The contract for this 37 | * method can be fulfilled by storing the data in a byte array literal 38 | * in the source code itself.
39 | *
40 | * It is imperative that you obfuscate the bytecode for the 41 | * implementation of this class. It is also imperative that the byte 42 | * array exist only for the life of this method (i.e., DO NOT store it as 43 | * an instance or class field). 44 | * 45 | * @return the encrypted file contents from the private key file. 46 | * @throws KeyNotFoundException if the key data could not be retrieved; an acceptable message or chained cause must 47 | * be provided. 48 | */ 49 | byte[] getEncryptedPrivateKeyData() throws KeyNotFoundException; 50 | } 51 | -------------------------------------------------------------------------------- /base/src/main/java/ro/esolutions/licensing/encryption/RSAKeyPairGenerator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * RSAKeyPairGenerator.java from LicenseManager modified Thursday, January 24, 2013 16:37:10 CST (-0600). 3 | * 4 | * Copyright 2010-2013 the original author or authors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package ro.esolutions.licensing.encryption; 20 | 21 | import java.io.File; 22 | import java.io.IOException; 23 | import java.security.InvalidParameterException; 24 | import java.security.KeyPair; 25 | import java.security.KeyPairGenerator; 26 | import java.security.NoSuchAlgorithmException; 27 | import java.security.PrivateKey; 28 | import java.security.PublicKey; 29 | 30 | import ro.esolutions.licensing.exception.AlgorithmNotSupportedException; 31 | import ro.esolutions.licensing.exception.InappropriateKeyException; 32 | import ro.esolutions.licensing.exception.InappropriateKeySpecificationException; 33 | import ro.esolutions.licensing.exception.RSA2048NotSupportedException; 34 | 35 | /** 36 | * The generator one should use to create public/private key pairs for use with 37 | * the application. 38 | * 39 | * @author Nick Williams 40 | * @version 1.0.0 41 | * @since 1.0.0 42 | */ 43 | public final class RSAKeyPairGenerator implements RSAKeyPairGeneratorInterface { 44 | 45 | /** 46 | * Generates a key pair with RSA 2048-bit security. 47 | * 48 | * @return a public/private key pair. 49 | * @throws RSA2048NotSupportedException if RSA or 2048-bit encryption are not supported. 50 | */ 51 | @Override 52 | public KeyPair generateKeyPair() throws RSA2048NotSupportedException { 53 | KeyPairGenerator keyGenerator; 54 | 55 | try { 56 | keyGenerator = KeyPairGenerator.getInstance(KeyFileUtilities.KEY_ALGORITHM); 57 | } catch (final NoSuchAlgorithmException e) { 58 | throw new RSA2048NotSupportedException("RSA keys are not supported on your system. Contact your system administrator for assistance.", e); 59 | } 60 | 61 | try { 62 | keyGenerator.initialize(2048); 63 | } catch (final InvalidParameterException e) { 64 | throw new RSA2048NotSupportedException("RSA is supported on your system, but 2048-bit keys are not. Contact your system administrator for assistance.", e); 65 | } 66 | 67 | return keyGenerator.generateKeyPair(); 68 | } 69 | 70 | /** 71 | * Saves the key pair specified to output files specified, encrypting both with the specified password. 72 | * 73 | * @param keyPair The key pair to save to the files specified 74 | * @param privateOutputFileName The name of the file to save the encrypted private key to 75 | * @param publicOutputFileName The name of the file to save the encrypted public key to 76 | * @param password The password to encrypt both keys with 77 | * @throws IOException if an error occurs while writing to the files. 78 | * @throws AlgorithmNotSupportedException If the encryption algorithm is not supported 79 | * @throws InappropriateKeyException If the public or private keys are invalid 80 | * @throws InappropriateKeySpecificationException If the public or private keys are invalid 81 | */ 82 | @Override 83 | public void saveKeyPairToFiles(final KeyPair keyPair,final String privateOutputFileName,final String publicOutputFileName, 84 | final char[] password) 85 | throws IOException, AlgorithmNotSupportedException, InappropriateKeyException, 86 | InappropriateKeySpecificationException { 87 | this.saveKeyPairToFiles(keyPair, privateOutputFileName, publicOutputFileName, password, password); 88 | } 89 | 90 | /** 91 | * Saves the key pair specified to output files specified, encrypting each with their specified passwords. 92 | * 93 | * @param keyPair The key pair to save to the files specified 94 | * @param privateOutputFileName The name of the file to save the encrypted private key to 95 | * @param publicOutputFileName The name of the file to save the encrypted public key to 96 | * @param privatePassword The password to encrypt the private key with 97 | * @param publicPassword The password to encrypt the public key with 98 | * @throws IOException if an error occurs while writing to the files. 99 | * @throws AlgorithmNotSupportedException If the encryption algorithm is not supported 100 | * @throws InappropriateKeyException If the public or private keys are invalid 101 | * @throws InappropriateKeySpecificationException If the public or private keys are invalid 102 | */ 103 | @Override 104 | public void saveKeyPairToFiles(final KeyPair keyPair,final String privateOutputFileName,final String publicOutputFileName, 105 | final char[] privatePassword,final char[] publicPassword) 106 | throws IOException, AlgorithmNotSupportedException, InappropriateKeyException, 107 | InappropriateKeySpecificationException { 108 | final PrivateKey privateKey = keyPair.getPrivate(); 109 | final PublicKey publicKey = keyPair.getPublic(); 110 | 111 | KeyFileUtilities.writeEncryptedPrivateKey(privateKey, new File(privateOutputFileName), privatePassword); 112 | KeyFileUtilities.writeEncryptedPublicKey(publicKey, new File(publicOutputFileName), publicPassword); 113 | } 114 | 115 | /** 116 | * Saves the public and private keys specified to the respective 117 | * {@link RSAKeyPairGeneratorInterface.GeneratedClassDescriptor#getJavaFileContents() javaFileContents} fields in 118 | * the provided {@link RSAKeyPairGeneratorInterface.GeneratedClassDescriptor}s, encrypting both with the specified 119 | * password. 120 | * 121 | * @param keyPair The key pair to save 122 | * @param privateKeyProvider An object describing the {@link PrivateKeyDataProvider} class to generate, and into 123 | * which the generated code will be saved 124 | * @param publicKeyProvider An object describing the {@link PublicKeyDataProvider} class to generate, and into 125 | * which the generated code will be saved 126 | * @param password The password to encrypt the keys with 127 | * @throws AlgorithmNotSupportedException If the encryption algorithm is not supported 128 | * @throws InappropriateKeyException If the public or private keys are invalid 129 | * @throws InappropriateKeySpecificationException If the public or private keys are invalid 130 | */ 131 | @Override 132 | public void saveKeyPairToProviders(final KeyPair keyPair,final GeneratedClassDescriptor privateKeyProvider, 133 | final GeneratedClassDescriptor publicKeyProvider,final char[] password) 134 | throws AlgorithmNotSupportedException, InappropriateKeyException, InappropriateKeySpecificationException { 135 | this.saveKeyPairToProviders(keyPair, privateKeyProvider, publicKeyProvider, password, password); 136 | } 137 | 138 | /** 139 | * Saves the public and private keys specified to the respective 140 | * {@link RSAKeyPairGeneratorInterface.GeneratedClassDescriptor#getJavaFileContents() javaFileContents} fields in 141 | * the provided {@link RSAKeyPairGeneratorInterface.GeneratedClassDescriptor}s, encrypting each with their 142 | * respective passwords. 143 | * 144 | * @param keyPair The key pair to save 145 | * @param privateKeyProvider An object describing the {@link PrivateKeyDataProvider} class to generate, and into 146 | * which the generated code will be saved 147 | * @param publicKeyProvider An object describing the {@link PublicKeyDataProvider} class to generate, and into 148 | * which the generated code will be saved 149 | * @param privatePassword The password to encrypt the private key with 150 | * @param publicPassword The password to encrypt the public key with 151 | * @throws AlgorithmNotSupportedException If the encryption algorithm is not supported 152 | * @throws InappropriateKeyException If the public or private keys are invalid 153 | * @throws InappropriateKeySpecificationException If the public or private keys are invalid 154 | */ 155 | @Override 156 | public void saveKeyPairToProviders(final KeyPair keyPair,final GeneratedClassDescriptor privateKeyProvider, 157 | final GeneratedClassDescriptor publicKeyProvider,final char[] privatePassword, 158 | final char[] publicPassword) 159 | throws AlgorithmNotSupportedException, InappropriateKeyException, InappropriateKeySpecificationException { 160 | if (keyPair == null) 161 | throw new IllegalArgumentException("Parameter keyPair cannot be null."); 162 | 163 | if (privateKeyProvider == null) 164 | throw new IllegalArgumentException("Parameter privateKeyProvider cannot be null."); 165 | 166 | if (publicKeyProvider == null) 167 | throw new IllegalArgumentException("Parameter publicKeyProvider cannot be null."); 168 | 169 | if (passwordInValid(privatePassword)) 170 | throw new IllegalArgumentException("Parameter privatePassword cannot be null or zero-length."); 171 | 172 | if (passwordInValid(publicPassword)) 173 | throw new IllegalArgumentException("Parameter publicPassword cannot be null or zero-length."); 174 | 175 | final byte[] privateKey = KeyFileUtilities.writeEncryptedPrivateKey(keyPair.getPrivate(), privatePassword); 176 | final byte[] publicKey = KeyFileUtilities.writeEncryptedPublicKey(keyPair.getPublic(), publicPassword); 177 | 178 | final String privateKeyCode = this.arrayToCodeString(this.byteArrayToIntArray(privateKey), "byte"); 179 | final String publicKeyCode = this.arrayToCodeString(this.byteArrayToIntArray(publicKey), "byte"); 180 | 181 | privateKeyProvider.setJavaFileContents(this.generateJavaCode( 182 | privateKeyProvider.getPackageName(), 183 | privateKeyProvider.getClassName(), 184 | "PrivateKeyDataProvider", 185 | new String[]{ 186 | "ro.esolutions.licensing.encryption.PrivateKeyDataProvider", 187 | "ro.esolutions.licensing.exception.KeyNotFoundException" 188 | }, 189 | "public byte[] getEncryptedPrivateKeyData() throws KeyNotFoundException", 190 | privateKeyCode 191 | )); 192 | 193 | publicKeyProvider.setJavaFileContents(this.generateJavaCode( 194 | publicKeyProvider.getPackageName(), 195 | publicKeyProvider.getClassName(), 196 | "PublicKeyDataProvider", 197 | new String[]{ 198 | "ro.esolutions.licensing.encryption.PublicKeyDataProvider", 199 | "ro.esolutions.licensing.exception.KeyNotFoundException" 200 | }, 201 | "public byte[] getEncryptedPublicKeyData() throws KeyNotFoundException", 202 | publicKeyCode 203 | )); 204 | } 205 | 206 | /** 207 | * Saves the password specified to the 208 | * {@link RSAKeyPairGeneratorInterface.GeneratedClassDescriptor#getJavaFileContents() javaFileContents} field in 209 | * the provided {@link RSAKeyPairGeneratorInterface.GeneratedClassDescriptor}. 210 | * 211 | * @param password The password to save to the specified Java class 212 | * @param passwordProvider An object describing the {@link PasswordProvider} class to generate, and into which the 213 | * generated code will be saved 214 | */ 215 | @Override 216 | public void savePasswordToProvider(final char[] password,final GeneratedClassDescriptor passwordProvider) { 217 | if (passwordInValid(password)) 218 | throw new IllegalArgumentException("Parameter password cannot be null or zero-length."); 219 | 220 | if (passwordProvider == null) 221 | throw new IllegalArgumentException("Parameter passwordProvider cannot be null."); 222 | 223 | final String passwordCode = this.arrayToCodeString(this.charArrayToIntArray(password), "char"); 224 | 225 | passwordProvider.setJavaFileContents(this.generateJavaCode( 226 | passwordProvider.getPackageName(), 227 | passwordProvider.getClassName(), 228 | "PasswordProvider", 229 | new String[]{"ro.esolutions.licensing.encryption.PasswordProvider"}, 230 | "public char[] getPassword()", 231 | passwordCode 232 | )); 233 | } 234 | 235 | private boolean passwordInValid(final char[] password) { 236 | return password == null || password.length == 0; 237 | } 238 | 239 | /** 240 | * Generates a final, compilable Java class implementing the specified interface with a single, one-statement 241 | * method that returns a simple value. 242 | * 243 | * @param packageName The package name the class should be contained in, or {@code null} if no package 244 | * @param className The name of the class to create 245 | * @param interfaceName The interface this class should implement, or null if no interface 246 | * @param imports An array of classes to import at the top of the Java code 247 | * @param methodSignature The signature of the sole method in the class 248 | * @param returnValue The statement that the method should return (will be prepended with "return ") 249 | * @return the Java code for the specified class. 250 | */ 251 | protected String generateJavaCode(final String packageName,final String className,final String interfaceName, 252 | final String[] imports,final String methodSignature,final String returnValue) { 253 | final StringBuilder stringBuilder = new StringBuilder(); 254 | 255 | if (packageName != null && packageName.trim().length() > 0) 256 | stringBuilder.append("package ").append(packageName.trim()).append(";\r\n\r\n"); 257 | 258 | if (imports != null && imports.length > 0) { 259 | for (String importClass : imports) 260 | stringBuilder.append("import ").append(importClass.trim()).append(";\r\n"); 261 | stringBuilder.append("\r\n"); 262 | } 263 | 264 | final boolean hasInterface = interfaceName != null && interfaceName.trim().length() > 0; 265 | 266 | stringBuilder.append("public final class ").append(className.trim()); 267 | if (hasInterface) 268 | stringBuilder.append(" implements ").append(interfaceName.trim()); 269 | stringBuilder.append("\r\n"); 270 | stringBuilder.append("{\r\n"); 271 | if (hasInterface) 272 | stringBuilder.append("\t@Override\r\n"); 273 | stringBuilder.append("\t").append(methodSignature.trim()).append("\r\n"); 274 | stringBuilder.append("\t{\r\n"); 275 | 276 | stringBuilder.append("\t\treturn ").append(returnValue).append(";\r\n"); 277 | 278 | stringBuilder.append("\t}\r\n"); 279 | 280 | stringBuilder.append("}"); 281 | 282 | return stringBuilder.toString(); 283 | } 284 | 285 | /** 286 | * Takes an array of integer-representable primitives ({@code byte}, {@code char}, {@code short}, {@code int}) 287 | * and returns a Java code array-literal instantiation of the array, with values in hexadecimal literal format. 288 | * It is the user's responsibility to ensure that the values contained in the array can fit within the smaller 289 | * precision of the array type, if applicable. 290 | * 291 | * @param values The array of values to include in the array code 292 | * @param type The data type ({@code byte}, {@code char}, {@code short}, {@code int}) of the array to return 293 | * @return the Java code representation of this array. 294 | */ 295 | protected String arrayToCodeString(final int[] values,final String type) { 296 | final StringBuilder stringBuilder = new StringBuilder("new ").append(type).append("[] {\r\n\t\t\t\t"); 297 | int i = 0, j = 1; 298 | for (int value : values) { 299 | if (i++ > 0) 300 | stringBuilder.append(", "); 301 | if (j++ > 8) { 302 | j = 2; 303 | stringBuilder.append("\r\n\t\t\t\t"); 304 | } 305 | stringBuilder.append("0x"); 306 | stringBuilder.append(String.format("%08x", value).toUpperCase()); 307 | } 308 | stringBuilder.append("\r\n\t\t}"); 309 | return stringBuilder.toString(); 310 | } 311 | 312 | /** 313 | * Converts a {@code byte} array to an {@code int} array. 314 | * 315 | * @param array The {@code byte} array to convert 316 | * @return the converted array as an {@code int} array. 317 | */ 318 | protected int[] byteArrayToIntArray(final byte[] array) { 319 | final int[] a = new int[array.length]; 320 | int i = 0; 321 | for (byte b : array) 322 | a[i++] = b; 323 | return a; 324 | } 325 | 326 | /** 327 | * Converts a {@code char} array to an {@code int} array. 328 | * 329 | * @param array The {@code char} array to convert 330 | * @return the converted array as an {@code int} array. 331 | */ 332 | protected int[] charArrayToIntArray(final char[] array) { 333 | final int[] a = new int[array.length]; 334 | int i = 0; 335 | for (char c : array) 336 | a[i++] = c; 337 | return a; 338 | } 339 | } 340 | -------------------------------------------------------------------------------- /base/src/main/java/ro/esolutions/licensing/encryption/RSAKeyPairGeneratorInterface.java: -------------------------------------------------------------------------------- 1 | /* 2 | * RSAKeyPairGeneratorInterface.java from LicenseManager modified Thursday, January 24, 2013 16:37:10 CST (-0600). 3 | * 4 | * Copyright 2010-2013 the original author or authors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package ro.esolutions.licensing.encryption; 20 | 21 | import java.io.IOException; 22 | import java.security.KeyPair; 23 | 24 | import ro.esolutions.licensing.exception.AlgorithmNotSupportedException; 25 | import ro.esolutions.licensing.exception.InappropriateKeyException; 26 | import ro.esolutions.licensing.exception.InappropriateKeySpecificationException; 27 | import ro.esolutions.licensing.exception.RSA2048NotSupportedException; 28 | 29 | /** 30 | * An interface for the key pair generator to make unit testing possible. This interface is only implemented by 31 | * {@link RSAKeyPairGenerator}. 32 | * 33 | * @author Nick Williams 34 | * @version 1.0.0 35 | * @since 1.0.0 36 | */ 37 | public interface RSAKeyPairGeneratorInterface { 38 | /** 39 | * Generates a key pair with RSA 2048-bit security. 40 | * 41 | * @return a public/private key pair. 42 | * @throws RSA2048NotSupportedException if RSA or 2048-bit encryption are not supported. 43 | */ 44 | KeyPair generateKeyPair() throws RSA2048NotSupportedException; 45 | 46 | /** 47 | * Saves the key pair specified to output files specified, encrypting both with the specified password. 48 | * 49 | * @param keyPair The key pair to save to the files specified 50 | * @param privateOutputFileName The name of the file to save the encrypted private key to 51 | * @param publicOutputFileName The name of the file to save the encrypted public key to 52 | * @param password The password to encrypt both keys with 53 | * @throws IOException if an error occurs while writing to the files. 54 | * @throws AlgorithmNotSupportedException If the encryption algorithm is not supported 55 | * @throws InappropriateKeyException If the public or private keys are invalid 56 | * @throws InappropriateKeySpecificationException If the public or private keys are invalid 57 | */ 58 | void saveKeyPairToFiles(final KeyPair keyPair,final String privateOutputFileName,final String publicOutputFileName, 59 | final char[] password) 60 | throws IOException, AlgorithmNotSupportedException, InappropriateKeyException, 61 | InappropriateKeySpecificationException; 62 | 63 | /** 64 | * Saves the key pair specified to output files specified, encrypting each with their specified passwords. 65 | * 66 | * @param keyPair The key pair to save to the files specified 67 | * @param privateOutputFileName The name of the file to save the encrypted private key to 68 | * @param publicOutputFileName The name of the file to save the encrypted public key to 69 | * @param privatePassword The password to encrypt the private key with 70 | * @param publicPassword The password to encrypt the public key with 71 | * @throws IOException if an error occurs while writing to the files. 72 | * @throws AlgorithmNotSupportedException If the encryption algorithm is not supported 73 | * @throws InappropriateKeyException If the public or private keys are invalid 74 | * @throws InappropriateKeySpecificationException If the public or private keys are invalid 75 | */ 76 | void saveKeyPairToFiles(final KeyPair keyPair,final String privateOutputFileName,final String publicOutputFileName, 77 | final char[] privatePassword,final char[] publicPassword) 78 | throws IOException, AlgorithmNotSupportedException, InappropriateKeyException, 79 | InappropriateKeySpecificationException; 80 | 81 | /** 82 | * Saves the public and private keys specified to the respective 83 | * {@link RSAKeyPairGeneratorInterface.GeneratedClassDescriptor#getJavaFileContents() javaFileContents} fields in 84 | * the provided {@link GeneratedClassDescriptor}s, encrypting both with the specified password. 85 | * 86 | * @param keyPair The key pair to save 87 | * @param privateKeyProvider An object describing the {@link PrivateKeyDataProvider} class to generate, and into 88 | * which the generated code will be saved 89 | * @param publicKeyProvider An object describing the {@link PublicKeyDataProvider} class to generate, and into 90 | * which the generated code will be saved 91 | * @param password The password to encrypt the keys with 92 | * @throws AlgorithmNotSupportedException If the encryption algorithm is not supported 93 | * @throws InappropriateKeyException If the public or private keys are invalid 94 | * @throws InappropriateKeySpecificationException If the public or private keys are invalid 95 | */ 96 | void saveKeyPairToProviders(final KeyPair keyPair,final GeneratedClassDescriptor privateKeyProvider, 97 | final GeneratedClassDescriptor publicKeyProvider,final char[] password) 98 | throws AlgorithmNotSupportedException, InappropriateKeyException, InappropriateKeySpecificationException; 99 | 100 | /** 101 | * Saves the public and private keys specified to the respective 102 | * {@link RSAKeyPairGeneratorInterface.GeneratedClassDescriptor#getJavaFileContents() javaFileContents} fields in 103 | * the provided {@link GeneratedClassDescriptor}s, encrypting each with their respective passwords. 104 | * 105 | * @param keyPair The key pair to save 106 | * @param privateKeyProvider An object describing the {@link PrivateKeyDataProvider} class to generate, and into 107 | * which the generated code will be saved 108 | * @param publicKeyProvider An object describing the {@link PublicKeyDataProvider} class to generate, and into 109 | * which the generated code will be saved 110 | * @param privatePassword The password to encrypt the private key with 111 | * @param publicPassword The password to encrypt the public key with 112 | * @throws AlgorithmNotSupportedException If the encryption algorithm is not supported 113 | * @throws InappropriateKeyException If the public or private keys are invalid 114 | * @throws InappropriateKeySpecificationException If the public or private keys are invalid 115 | */ 116 | void saveKeyPairToProviders(final KeyPair keyPair,final GeneratedClassDescriptor privateKeyProvider, 117 | final GeneratedClassDescriptor publicKeyProvider,final char[] privatePassword, 118 | final char[] publicPassword) 119 | throws AlgorithmNotSupportedException, InappropriateKeyException, InappropriateKeySpecificationException; 120 | 121 | /** 122 | * Saves the password specified to the 123 | * {@link RSAKeyPairGeneratorInterface.GeneratedClassDescriptor#getJavaFileContents() javaFileContents} field in 124 | * the provided {@link GeneratedClassDescriptor}. 125 | * 126 | * @param password The password to save to the specified Java class 127 | * @param passwordProvider An object describing the {@link PasswordProvider} class to generate, and into which the 128 | * generated code will be saved 129 | */ 130 | void savePasswordToProvider(final char[] password,final GeneratedClassDescriptor passwordProvider); 131 | 132 | class GeneratedClassDescriptor { 133 | private String packageName; 134 | 135 | private String className; 136 | 137 | private String javaFileContents; 138 | 139 | public String getPackageName() { 140 | return this.packageName; 141 | } 142 | 143 | public GeneratedClassDescriptor setPackageName(final String packageName) { 144 | this.packageName = packageName; 145 | return this; 146 | } 147 | 148 | public String getClassName() { 149 | return this.className; 150 | } 151 | 152 | public GeneratedClassDescriptor setClassName(final String className) { 153 | this.className = className; 154 | return this; 155 | } 156 | 157 | public String getJavaFileContents() { 158 | return javaFileContents; 159 | } 160 | 161 | public GeneratedClassDescriptor setJavaFileContents(final String javaFileContents) { 162 | this.javaFileContents = javaFileContents; 163 | return this; 164 | } 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /base/src/main/java/ro/esolutions/licensing/exception/RSA2048NotSupportedException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * RSA2048NotSupportedException.java from LicenseManager modified Friday, September 21, 2012 07:46:54 CDT (-0500). 3 | * 4 | * Copyright 2010-2013 the original author or authors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package ro.esolutions.licensing.exception; 20 | 21 | /** 22 | * This exception is thrown when 2048-bit RSA security, which is required for 23 | * this library to run, is not supported with the current JVM. 24 | * 25 | * @author Nick Williams 26 | * @version 1.0.0 27 | * @since 1.0.0 28 | */ 29 | @SuppressWarnings("unused") 30 | public class RSA2048NotSupportedException extends RuntimeException { 31 | 32 | private static final String MESSAGE = "2048-bit RSA Security is not supported on this system."; 33 | 34 | public RSA2048NotSupportedException() { 35 | super(MESSAGE); 36 | } 37 | 38 | public RSA2048NotSupportedException(final String message) { 39 | super(message); 40 | } 41 | 42 | public RSA2048NotSupportedException(final Throwable cause) { 43 | super(MESSAGE, cause); 44 | } 45 | 46 | public RSA2048NotSupportedException(final String message,final Throwable cause) { 47 | super(message, cause); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /base/src/main/java/ro/esolutions/licensing/licensor/LicenseCreator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * LicenseCreator.java from LicenseManager modified Thursday, January 24, 2013 16:39:57 CST (-0600). 3 | * 4 | * Copyright 2010-2013 the original author or authors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package ro.esolutions.licensing.licensor; 20 | 21 | import java.security.PrivateKey; 22 | import java.util.Arrays; 23 | 24 | import ro.esolutions.licensing.DataSignatureManager; 25 | import ro.esolutions.licensing.License; 26 | import ro.esolutions.licensing.ObjectSerializer; 27 | import ro.esolutions.licensing.SignedLicense; 28 | import ro.esolutions.licensing.encryption.Encryptor; 29 | import ro.esolutions.licensing.encryption.KeyFileUtilities; 30 | import ro.esolutions.licensing.encryption.PasswordProvider; 31 | import ro.esolutions.licensing.encryption.PrivateKeyDataProvider; 32 | import ro.esolutions.licensing.exception.AlgorithmNotSupportedException; 33 | import ro.esolutions.licensing.exception.InappropriateKeyException; 34 | import ro.esolutions.licensing.exception.InappropriateKeySpecificationException; 35 | import ro.esolutions.licensing.exception.KeyNotFoundException; 36 | import ro.esolutions.licensing.exception.ObjectSerializationException; 37 | 38 | /** 39 | * This class manages the creation of licenses in the master application. Use this class within your license generation 40 | * software to sign and serialize license for distribution to the client. This class is not needed for the client 41 | * application, and in fact you should not use this class in your client application. For this reason, it is 42 | * in the package of classes (ro.esolutions.licensing.licensor) that is packaged separately from the 43 | * distributable client binary.
44 | *
45 | * Before getting the creator instance for the first time, relevant properties should be set in 46 | * {@link LicenseCreatorProperties}. The values in this class will be used to instantiate the license creator. 47 | * After setting all the necessary properties there, one can retrieve an instance using {@link #getInstance()}. Be 48 | * sure to set all the properties first; once {@link #getInstance()} is called for the first time, any changes to 49 | * {@link LicenseCreatorProperties} will be ignored.
50 | * 51 | * @author Nick Williams 52 | * @version 1.0.6 53 | * @since 1.0.0 54 | */ 55 | public final class LicenseCreator { 56 | private static LicenseCreator instance; 57 | 58 | private final PrivateKeyDataProvider privateKeyDataProvider; 59 | 60 | private final PasswordProvider privateKeyPasswordProvider; 61 | 62 | private LicenseCreator() { 63 | if (LicenseCreatorProperties.getPrivateKeyDataProvider() == null) 64 | throw new IllegalArgumentException("Parameter privateKeyDataProvider must not be null."); 65 | 66 | if (LicenseCreatorProperties.getPrivateKeyPasswordProvider() == null) 67 | throw new IllegalArgumentException("Parameter privateKeyPasswordProvider must not be null."); 68 | 69 | this.privateKeyPasswordProvider = LicenseCreatorProperties.getPrivateKeyPasswordProvider(); 70 | this.privateKeyDataProvider = LicenseCreatorProperties.getPrivateKeyDataProvider(); 71 | } 72 | 73 | /** 74 | * Returns the license creator instance. Before this method can be called the first time, all of the parameters must 75 | * bet set in {@link LicenseCreatorProperties}. See the documentation for that class for more details. 76 | * 77 | * @return the license creator instance. 78 | * @throws IllegalArgumentException if {@link LicenseCreatorProperties#setPrivateKeyDataProvider(PrivateKeyDataProvider) 79 | * privateKeyDataProvider} or {@link LicenseCreatorProperties#setPrivateKeyPasswordProvider(PasswordProvider) 80 | * privateKeyPasswordProvider} are null 81 | */ 82 | public static synchronized LicenseCreator getInstance() { 83 | if (LicenseCreator.instance == null) { 84 | LicenseCreator.instance = new LicenseCreator(); 85 | } 86 | 87 | return LicenseCreator.instance; 88 | } 89 | 90 | /** 91 | * Takes a license object and creates a secure version of it for serialization and delivery to the customer. 92 | * 93 | * @param license The license object to be signed 94 | * @param licensePassword The password to encrypt the license with 95 | * @return the signed license object. 96 | * @throws AlgorithmNotSupportedException if the encryption algorithm is not supported. 97 | * @throws KeyNotFoundException if the public key data could not be found. 98 | * @throws InappropriateKeySpecificationException if an inappropriate key specification is provided. 99 | * @throws InappropriateKeyException if the key type and cipher type do not match. 100 | */ 101 | public final SignedLicense signLicense(License license, char[] licensePassword) 102 | throws AlgorithmNotSupportedException, KeyNotFoundException, InappropriateKeySpecificationException, 103 | InappropriateKeyException { 104 | PrivateKey key; 105 | { 106 | char[] password = this.privateKeyPasswordProvider.getPassword(); 107 | byte[] keyData = this.privateKeyDataProvider.getEncryptedPrivateKeyData(); 108 | 109 | key = KeyFileUtilities.readEncryptedPrivateKey(keyData, password); 110 | 111 | Arrays.fill(password, '\u0000'); 112 | Arrays.fill(keyData, (byte) 0); 113 | } 114 | 115 | byte[] encrypted = Encryptor.encryptRaw(license.serialize(), licensePassword); 116 | 117 | byte[] signature = new DataSignatureManager().signData(key, encrypted); 118 | 119 | SignedLicense signed = new SignedLicense(encrypted, signature); 120 | 121 | Arrays.fill(encrypted, (byte) 0); 122 | Arrays.fill(signature, (byte) 0); 123 | 124 | return signed; 125 | } 126 | 127 | /** 128 | * Takes a license object and creates a secure version of it for serialization and delivery to the customer. 129 | * 130 | * @param license The license object to be signed 131 | * @return the signed license object. 132 | * @throws AlgorithmNotSupportedException if the encryption algorithm is not supported. 133 | * @throws KeyNotFoundException if the public key data could not be found. 134 | * @throws InappropriateKeySpecificationException if an inappropriate key specification is provided. 135 | * @throws InappropriateKeyException if the key type and cipher type do not match. 136 | */ 137 | public final SignedLicense signLicense(License license) 138 | throws AlgorithmNotSupportedException, KeyNotFoundException, InappropriateKeySpecificationException, 139 | InappropriateKeyException { 140 | return this.signLicense(license, this.privateKeyPasswordProvider.getPassword()); 141 | } 142 | 143 | /** 144 | * Takes a license object and creates a secure and serialized version of it for delivery to the customer. 145 | * 146 | * @param license The license object to be signed and serialized 147 | * @param licensePassword The password to encrypt the license with 148 | * @return the signed and serialized license object. 149 | * @throws AlgorithmNotSupportedException if the encryption algorithm is not supported. 150 | * @throws KeyNotFoundException if the public key data could not be found. 151 | * @throws InappropriateKeySpecificationException if an inappropriate key specification is provided. 152 | * @throws InappropriateKeyException if the key type and cipher type do not match. 153 | * @throws ObjectSerializationException if an error is encountered while serializing the key. 154 | */ 155 | public final byte[] signAndSerializeLicense(License license, char[] licensePassword) 156 | throws AlgorithmNotSupportedException, KeyNotFoundException, InappropriateKeySpecificationException, 157 | InappropriateKeyException, ObjectSerializationException { 158 | return new ObjectSerializer().writeObject(this.signLicense(license, licensePassword)); 159 | } 160 | 161 | /** 162 | * Takes a license object and creates a secure and serialized version of it for delivery to the customer. 163 | * 164 | * @param license The license object to be signed and serialized 165 | * @return the signed and serialized license object. 166 | * @throws AlgorithmNotSupportedException if the encryption algorithm is not supported. 167 | * @throws KeyNotFoundException if the public key data could not be found. 168 | * @throws InappropriateKeySpecificationException if an inappropriate key specification is provided. 169 | * @throws InappropriateKeyException if the key type and cipher type do not match. 170 | * @throws ObjectSerializationException if an error is encountered while serializing the key. 171 | */ 172 | public final byte[] signAndSerializeLicense(License license) 173 | throws AlgorithmNotSupportedException, KeyNotFoundException, InappropriateKeySpecificationException, 174 | InappropriateKeyException, ObjectSerializationException { 175 | return new ObjectSerializer().writeObject(this.signLicense(license)); 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /base/src/main/java/ro/esolutions/licensing/licensor/LicenseCreatorProperties.java: -------------------------------------------------------------------------------- 1 | /* 2 | * LicenseCreatorProperties.java from LicenseManager modified Thursday, January 24, 2013 16:37:10 CST (-0600). 3 | * 4 | * Copyright 2010-2013 the original author or authors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package ro.esolutions.licensing.licensor; 20 | 21 | import ro.esolutions.licensing.encryption.PasswordProvider; 22 | import ro.esolutions.licensing.encryption.PrivateKeyDataProvider; 23 | 24 | /** 25 | * This class is used to set properties that will be used to instantiate the {@link LicenseCreator}. Read the 26 | * documentation for each property below. 27 | * 28 | * @author Nick Williams 29 | * @version 1.0.0 30 | * @since 1.0.0 31 | */ 32 | public final class LicenseCreatorProperties { 33 | private static PrivateKeyDataProvider privateKeyDataProvider; 34 | 35 | private static PasswordProvider privateKeyPasswordProvider; 36 | 37 | /** 38 | * Sets the provider of the data for the private key used to sign the license object.
39 | *
40 | * This field is required. 41 | * 42 | * @param privateKeyDataProvider The provider of the data for the private key used to sign the license object 43 | */ 44 | public static void setPrivateKeyDataProvider(PrivateKeyDataProvider privateKeyDataProvider) { 45 | LicenseCreatorProperties.privateKeyDataProvider = privateKeyDataProvider; 46 | } 47 | 48 | static PrivateKeyDataProvider getPrivateKeyDataProvider() { 49 | return LicenseCreatorProperties.privateKeyDataProvider; 50 | } 51 | 52 | /** 53 | * Sets the provider of the password for decrypting the private key.
54 | *
55 | * This field is required. 56 | * 57 | * @param privateKeyPasswordProvider The provider of the password for decrypting the private key 58 | */ 59 | public static void setPrivateKeyPasswordProvider(PasswordProvider privateKeyPasswordProvider) { 60 | LicenseCreatorProperties.privateKeyPasswordProvider = privateKeyPasswordProvider; 61 | } 62 | 63 | static PasswordProvider getPrivateKeyPasswordProvider() { 64 | return LicenseCreatorProperties.privateKeyPasswordProvider; 65 | } 66 | 67 | /** 68 | * This class cannot be instantiated. 69 | */ 70 | private LicenseCreatorProperties() { 71 | throw new RuntimeException("This class cannot be instantiated."); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /core/.gitignore: -------------------------------------------------------------------------------- 1 | license-manager-core.iml 2 | target 3 | -------------------------------------------------------------------------------- /core/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 19 | 21 | 4.0.0 22 | 23 | 24 | ro.esolutions 25 | license-manager 26 | 1.0.4-SNAPSHOT 27 | 28 | 29 | license-manager-core 30 | jar 31 | 32 | License Manager - Core 33 | 34 | 35 | 36 | commons-codec 37 | commons-codec 38 | 39 | 40 | 41 | commons-io 42 | commons-io 43 | 44 | 45 | com.google.guava 46 | guava 47 | compile 48 | 49 | 50 | commons-lang 51 | commons-lang 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /core/src/main/java/ro/esolutions/licensing/DataSignatureManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DataSignatureManager.java from LicenseManager modified Tuesday, February 21, 2012 10:59:35 CST (-0600). 3 | * 4 | * Copyright 2010-2013 the original author or authors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package ro.esolutions.licensing; 20 | 21 | import java.security.InvalidKeyException; 22 | import java.security.NoSuchAlgorithmException; 23 | import java.security.PrivateKey; 24 | import java.security.PublicKey; 25 | import java.security.Signature; 26 | import java.security.SignatureException; 27 | 28 | import ro.esolutions.licensing.encryption.KeyFileUtilities; 29 | import ro.esolutions.licensing.exception.AlgorithmNotSupportedException; 30 | import ro.esolutions.licensing.exception.CorruptSignatureException; 31 | import ro.esolutions.licensing.exception.InappropriateKeyException; 32 | import ro.esolutions.licensing.exception.InvalidSignatureException; 33 | 34 | public final class DataSignatureManager { 35 | public final byte[] signData(final PrivateKey key, final byte[] data) throws AlgorithmNotSupportedException, 36 | InappropriateKeyException { 37 | 38 | final Signature signature = this.getSignature(); 39 | 40 | try { 41 | signature.initSign(key); 42 | } catch (final InvalidKeyException e) { 43 | throw new InappropriateKeyException("While initializing the signature object with the public key.", e); 44 | } 45 | 46 | try { 47 | signature.update(data); 48 | } catch (final SignatureException e) { 49 | throw new RuntimeException("This should never happen.", e); 50 | } 51 | 52 | try { 53 | return signature.sign(); 54 | } catch (final SignatureException e) { 55 | throw new RuntimeException("This should never happen.", e); 56 | } 57 | } 58 | 59 | public final void verifySignature(final PublicKey key, final byte[] data, final byte[] signatureContent) 60 | throws AlgorithmNotSupportedException, InappropriateKeyException, CorruptSignatureException, InvalidSignatureException { 61 | 62 | final Signature signature = this.getSignature(); 63 | 64 | try { 65 | signature.initVerify(key); 66 | } catch (final InvalidKeyException e) { 67 | throw new InappropriateKeyException("While initializing the signature object with the public key.", e); 68 | } 69 | 70 | try { 71 | signature.update(data); 72 | } catch (final SignatureException e) { 73 | throw new RuntimeException("This should never happen.", e); 74 | } 75 | 76 | try { 77 | if (!signature.verify(signatureContent)) 78 | throw new InvalidSignatureException("The license signature is invalid."); 79 | } catch (final SignatureException e) { 80 | throw new CorruptSignatureException("While verifying the signature.", e); 81 | } 82 | } 83 | 84 | private Signature getSignature() { 85 | try { 86 | return Signature.getInstance("SHA1with" + KeyFileUtilities.KEY_ALGORITHM); 87 | } catch (final NoSuchAlgorithmException e) { 88 | throw new AlgorithmNotSupportedException("SHA-1 with " + KeyFileUtilities.KEY_ALGORITHM); 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /core/src/main/java/ro/esolutions/licensing/DefaultLicenseValidator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DefaultLicenseValidator.java from LicenseManager modified Tuesday, February 21, 2012 10:59:34 CST (-0600). 3 | * 4 | * Copyright 2010-2013 the original author or authors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package ro.esolutions.licensing; 20 | 21 | import java.time.Instant; 22 | import java.time.format.DateTimeFormatter; 23 | 24 | import ro.esolutions.licensing.exception.ExpiredLicenseException; 25 | import ro.esolutions.licensing.exception.InvalidLicenseException; 26 | 27 | public class DefaultLicenseValidator implements LicenseValidator { 28 | 29 | @Override 30 | public void validateLicense(final License license) throws InvalidLicenseException { 31 | final Instant time = Instant.now(); 32 | if (license.getGoodAfterDate().isAfter(time)) 33 | throw new InvalidLicenseException("The " + this.getLicenseDescription(license) + 34 | " does not take effect until " + this.getFormattedDate(license.getGoodAfterDate()) + "."); 35 | if (license.getGoodBeforeDate().isBefore(time)) 36 | throw new ExpiredLicenseException("The " + this.getLicenseDescription(license) + 37 | " expired on " + this.getFormattedDate(license.getGoodAfterDate()) + "."); 38 | } 39 | 40 | public String getLicenseDescription(final License license) { 41 | return license.getSubject() + " license for " + license.getHolder(); 42 | } 43 | 44 | public String getFormattedDate(final Instant time) { 45 | return DateTimeFormatter.ISO_INSTANT.format(time); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /core/src/main/java/ro/esolutions/licensing/DeserializingLicenseProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * DeserializingLicenseProvider.java from LicenseManager modified Tuesday, May 29, 2012 19:41:03 CDT (-0500). 3 | * 4 | * Copyright 2010-2013 the original author or authors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package ro.esolutions.licensing; 20 | 21 | /** 22 | * An abstract implementation of the {@link LicenseProvider} interface that assumes the license will be stored in 23 | * serialized form. Users need only implement the method returning the raw byte data of the serialized license in order 24 | * to complete this implementation. 25 | * 26 | * @author Nick Williams 27 | * @version 1.0.0 28 | * @since 1.0.0 29 | */ 30 | public abstract class DeserializingLicenseProvider implements LicenseProvider { 31 | /** 32 | * Gets the stored, still-encrypted license content and signature from the persistence store. 33 | * 34 | * @param context The context for which to get the license 35 | * @return the signed license object. 36 | */ 37 | @Override 38 | public final SignedLicense getLicense(final Object context) { 39 | final byte[] data = this.getLicenseData(context); 40 | 41 | return data == null ? null : this.deserializeLicense(data); 42 | } 43 | 44 | public final SignedLicense deserializeLicense(final byte[] data) { 45 | return new ObjectSerializer().readObject(SignedLicense.class, data); 46 | } 47 | 48 | /** 49 | * Gets the stored, still-encrypted, still-serialized license content and signature from the persistence store. If 50 | * no license is found, this method should return null (not an empty array). 51 | * 52 | * @param context The context for which to get the license 53 | * @return the signed license data. 54 | */ 55 | protected abstract byte[] getLicenseData(final Object context); 56 | } 57 | -------------------------------------------------------------------------------- /core/src/main/java/ro/esolutions/licensing/Feature.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Feature.java from LicenseManager modified Wednesday, November 30, 2016 14:14:50 EET (+0200). 3 | * 4 | * Copyright 2010-2016 the original author or authors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package ro.esolutions.licensing; 20 | 21 | import com.google.common.base.MoreObjects; 22 | 23 | import org.apache.commons.lang.builder.EqualsBuilder; 24 | import org.apache.commons.lang.builder.HashCodeBuilder; 25 | 26 | import java.io.Serializable; 27 | import java.time.Instant; 28 | 29 | public class Feature implements Cloneable, Serializable { 30 | private static final long serialVersionUID = 1L; 31 | 32 | private final String name; 33 | private final int seats; 34 | private final Instant goodBeforeDate; 35 | 36 | private Feature(final Builder builder) { 37 | this.name = builder.name; 38 | this.seats = builder.seats; 39 | this.goodBeforeDate = builder.goodBeforeDate; 40 | } 41 | 42 | public static Feature.Builder of(final String name) { 43 | return new Feature.Builder(name); 44 | } 45 | 46 | public int getSeats() { 47 | return seats; 48 | } 49 | 50 | public final String getName() { 51 | return name; 52 | } 53 | 54 | public final Instant getGoodBeforeDate() { 55 | return goodBeforeDate; 56 | } 57 | 58 | /** 59 | * Deserializes a string representation of a feature into a feature. 60 | * 61 | * @param input The string representation of a feature, generated with {@link #toString()}. 62 | * @return the unserialized feature. 63 | */ 64 | static Feature fromString(final String input) { 65 | if (input == null) 66 | throw new IllegalArgumentException("The input argument did not contain exactly two parts."); 67 | 68 | final String[] parts = input.split("" + (char) 0x1F); 69 | if (parts.length != 3) 70 | throw new IllegalArgumentException("The input argument did not contain exactly two parts."); 71 | 72 | return Feature.of(parts[0]) 73 | .seats(Integer.parseInt(parts[1])) 74 | .goodBeforeDate(Instant.parse(parts[2])) 75 | .build(); 76 | } 77 | 78 | /** 79 | * Indicates whether these features are the same feature. Important note: Two features can be the same 80 | * feature (equal) and have different expiration dates. 81 | * 82 | * @param object The feature to check for equality against 83 | * @return {@code true} if the features are the same, {@code false} otherwise. 84 | */ 85 | @Override 86 | public final boolean equals(final Object object) { 87 | return EqualsBuilder.reflectionEquals(this, object); 88 | } 89 | 90 | @Override 91 | public final int hashCode() { 92 | return HashCodeBuilder.reflectionHashCode(this); 93 | } 94 | 95 | public final String toString() { 96 | return MoreObjects.toStringHelper(this) 97 | .add("name", name) 98 | .add("seats", seats) 99 | .add("goodBeforeDate", goodBeforeDate) 100 | .toString(); 101 | } 102 | 103 | @Override 104 | @SuppressWarnings("CloneDoesntCallSuperClone") 105 | public Feature clone() { 106 | return Feature.of(this.name) 107 | .seats(this.seats) 108 | .goodBeforeDate(this.goodBeforeDate) 109 | .build(); 110 | } 111 | 112 | public static class Builder { 113 | private String name; 114 | private int seats; 115 | private Instant goodBeforeDate; 116 | 117 | private Builder(final String name) { 118 | this.name = name; 119 | } 120 | 121 | public Builder seats(final int seats) { 122 | this.seats = seats; 123 | return this; 124 | } 125 | 126 | public Builder goodBeforeDate(final Instant goodBeforeDate) { 127 | this.goodBeforeDate = goodBeforeDate; 128 | return this; 129 | } 130 | 131 | public Feature build() { 132 | return new Feature(this); 133 | } 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /core/src/main/java/ro/esolutions/licensing/FeatureRestriction.java: -------------------------------------------------------------------------------- 1 | /* 2 | * FeatureRestriction.java from LicenseManager modified Tuesday, February 21, 2012 10:59:35 CST (-0600). 3 | * 4 | * Copyright 2010-2013 the original author or authors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package ro.esolutions.licensing; 20 | 21 | import java.lang.annotation.Documented; 22 | import java.lang.annotation.ElementType; 23 | import java.lang.annotation.Retention; 24 | import java.lang.annotation.RetentionPolicy; 25 | import java.lang.annotation.Target; 26 | 27 | /** 28 | * An annotation for indicating license-restricted methods, packages and types. For example, one might set an AspectJ 29 | * pointcut that is intercepted and asserts that annotated accesses are done with permission. If not, it might throw an 30 | * exception, which would be caught by the user interface (such as a servlet filter in a Java EE environment) and 31 | * handled accordingly. 32 | * 33 | * @author Nick Williams 34 | * @version 1.0.0 35 | * @since 1.0.0 36 | */ 37 | @Documented 38 | @Target({ElementType.METHOD, ElementType.PACKAGE, ElementType.TYPE}) 39 | @Retention(RetentionPolicy.RUNTIME) 40 | public @interface FeatureRestriction { 41 | String[] value(); 42 | 43 | FeatureRestrictionOperand operand() default FeatureRestrictionOperand.AND; 44 | } 45 | -------------------------------------------------------------------------------- /core/src/main/java/ro/esolutions/licensing/FeatureRestrictionOperand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * FeatureRestrictionOperand.java from LicenseManager modified Tuesday, February 21, 2012 10:59:35 CST (-0600). 3 | * 4 | * Copyright 2010-2013 the original author or authors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package ro.esolutions.licensing; 20 | 21 | /** 22 | * Indicates whether feature restrictions are "all-or-nothing" ({@link #AND}) or "any" ({@link #OR}). 23 | * 24 | * @author Nick Williams 25 | * @version 1.0.0 26 | * @since 1.0.0 27 | */ 28 | public enum FeatureRestrictionOperand { 29 | AND, 30 | OR 31 | } 32 | -------------------------------------------------------------------------------- /core/src/main/java/ro/esolutions/licensing/FileLicenseProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * FileLicenseProvider.java from LicenseManager modified Tuesday, September 4, 2012 14:17:42 CDT (-0500). 3 | * 4 | * Copyright 2010-2013 the original author or authors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package ro.esolutions.licensing; 20 | 21 | import org.apache.commons.codec.binary.Base64; 22 | import org.apache.commons.io.FileUtils; 23 | 24 | import java.io.File; 25 | import java.io.IOException; 26 | import java.net.URISyntaxException; 27 | import java.net.URL; 28 | 29 | /** 30 | * A default implementation of the {@link LicenseProvider} that assumes the binary data from the signed and serialized 31 | * license is stored in a file. Various properties of this provider allow configuration of file prefixes (such as 32 | * a directory), file suffixes (such as an extension), whether or not the file can be found on the classpath, and 33 | * whether or not the contents of the file are Base64 encoded.
34 | *
35 | * This implementation also assumes that license contexts (lookup keys) are always either strings or have a meaningful 36 | * {@link Object#toString()} implementation that can be used within the file name. 37 | * 38 | * @author Nick Williams 39 | * @version 1.0.0 40 | * @since 1.0.0 41 | */ 42 | public class FileLicenseProvider extends DeserializingLicenseProvider { 43 | protected ClassLoader classLoader; 44 | 45 | private String filePrefix = ""; 46 | 47 | private String fileSuffix = ""; 48 | 49 | private boolean fileOnClasspath = false; 50 | 51 | private boolean base64Encoded = false; 52 | 53 | /** 54 | * Constructs a file-based license provider with the same class loader as the loader of this class and 55 | * {@link #setFileOnClasspath(boolean) fileOnClasspath} set to {@code false}. The class loader is only used if 56 | * {@code fileOnClasspath} is subsequently changed to {@code true}. 57 | */ 58 | public FileLicenseProvider() { 59 | this.classLoader = this.getClass().getClassLoader(); 60 | this.fileOnClasspath = false; 61 | } 62 | 63 | /** 64 | * Constructs a file-based license provider with the provided class loader and 65 | * {@link #setFileOnClasspath(boolean) fileOnClasspath} set to {@code true}. The class loader will be used to 66 | * locate the file unless {@code fileOnClasspath} is subsequently changed to {@code false}. 67 | * 68 | * @param classLoader The class loader to use for finding the file 69 | */ 70 | public FileLicenseProvider(final ClassLoader classLoader) { 71 | if (classLoader == null) { 72 | throw new IllegalArgumentException("Argument classLoader cannot be null."); 73 | } 74 | this.classLoader = classLoader; 75 | this.fileOnClasspath = true; 76 | } 77 | 78 | /** 79 | * Gets the stored, still-encrypted, still-serialized license content and signature from the persistence store. 80 | * Returns null (not an empty array) if no license is found. 81 | * 82 | * @param context The context for which to get the license 83 | * @return the signed license data. 84 | */ 85 | @Override 86 | protected byte[] getLicenseData(final Object context) { 87 | if (context == null) { 88 | throw new IllegalArgumentException("Argument context cannot be null."); 89 | } 90 | final File file = this.getLicenseFile(context); 91 | if (file == null || !file.exists() || !file.canRead()) { 92 | return null; 93 | } 94 | try { 95 | byte[] data = FileUtils.readFileToByteArray(file); 96 | 97 | if (this.isBase64Encoded()) { 98 | data = Base64.decodeBase64(data); 99 | } 100 | 101 | return data; 102 | } catch (final IOException e) { 103 | return null; 104 | } 105 | } 106 | 107 | /** 108 | * Gets the license file handle. Returns null if if no license is found, but if a license is found, this may 109 | * return a file handle to a non-existent file. So, the file should be checked for existence and readability. 110 | * 111 | * @param context The context for which to get the license 112 | * @return the license file handle. 113 | */ 114 | protected File getLicenseFile(final Object context) { 115 | String fileName = this.getFilePrefix() + context.toString() + this.getFileSuffix(); 116 | 117 | if (this.isFileOnClasspath()) { 118 | if (fileName.startsWith("/")) { 119 | fileName = fileName.substring(1); 120 | } 121 | final URL url = this.classLoader.getResource(fileName); 122 | if (url == null) { 123 | fileName = null; 124 | } else { 125 | try { 126 | return new File(url.toURI()); 127 | } catch (final URISyntaxException e) { 128 | return new File(url.getPath()); 129 | } 130 | } 131 | } 132 | 133 | return fileName == null ? null : new File(fileName); 134 | } 135 | 136 | /** 137 | * Gets the prefix that will be prepended to the file name before looking for it. For example, if a license 138 | * context was "customer01" and the file name was "C:\product\licenses\file-customer01.lic", then the prefix 139 | * would be "C:\product\licenses\file-" and the suffix would be ".lic". 140 | * 141 | * @return the file prefix. 142 | */ 143 | public String getFilePrefix() { 144 | return this.filePrefix; 145 | } 146 | 147 | /** 148 | * Sets the prefix that will be prepended to the file name before looking for it. For example, if a license 149 | * context was "customer01" and the file name was "C:\product\licenses\file-customer01.lic", then the prefix 150 | * would be "C:\product\licenses\file-" and the suffix would be ".lic". 151 | * 152 | * @param filePrefix The file prefix 153 | */ 154 | public void setFilePrefix(final String filePrefix) { 155 | if (filePrefix == null) { 156 | throw new IllegalArgumentException("Argument filePrefix cannot be null."); 157 | } 158 | this.filePrefix = filePrefix; 159 | } 160 | 161 | /** 162 | * Gets the file suffix that will be appended to the file name before looking for it. For example, if a license 163 | * context was "customer01" and the file name was "C:\product\licenses\file-customer01.lic", then the prefix 164 | * would be "C:\product\licenses\file-" and the suffix would be ".lic". 165 | * 166 | * @return the file suffix. 167 | */ 168 | public String getFileSuffix() { 169 | return this.fileSuffix; 170 | } 171 | 172 | /** 173 | * Sets the file suffix that will be appended to the file name before looking for it. For example, if a license 174 | * context was "customer01" and the file name was "C:\product\licenses\file-customer01.lic", then the prefix 175 | * would be "C:\product\licenses\file-" and the suffix would be ".lic". 176 | * 177 | * @param fileSuffix The file suffix 178 | */ 179 | public void setFileSuffix(final String fileSuffix) { 180 | if (fileSuffix == null) { 181 | throw new IllegalArgumentException("Argument fileSuffix cannot be null."); 182 | } 183 | this.fileSuffix = fileSuffix; 184 | } 185 | 186 | /** 187 | * Indicates whether the file should be found on the file system or on the classpath via a class loader. If 188 | * {@code false} it will be looked for on the file system; if {@code true} it will be looked for on the classpath. 189 | * If {@code true}, the file prefix should be the package-path and prefix and the suffix the suffix.
190 | *
191 | * For example, if a license context was "customer02" and the file name was "file-customer02.lic" and was located 192 | * in the package ro.esolutions.licensing.licenses, then the prefix would be 193 | * "net/nicholaswilliams/java/licensing/licenses/file-" and the suffix should be ".lic". 194 | * 195 | * @return whether the file is on the classpath. 196 | */ 197 | public boolean isFileOnClasspath() { 198 | return this.fileOnClasspath; 199 | } 200 | 201 | /** 202 | * Sets whether the file should be found on the file system or on the classpath via a class loader. If 203 | * {@code false} it will be looked for on the file system; if {@code true} it will be looked for on the classpath. 204 | * If {@code true}, the file prefix should be the package-path and prefix and the suffix the suffix.
205 | *
206 | * For example, if a license context was "customer02" and the file name was "file-customer02.lic" and was located 207 | * in the package ro.esolutions.licensing.licenses, then the prefix would be 208 | * "net/nicholaswilliams/java/licensing/licenses/file-" and the suffix should be ".lic". 209 | * 210 | * @param fileOnClasspath Whether the file is on the classpath 211 | */ 212 | public void setFileOnClasspath(final boolean fileOnClasspath) { 213 | this.fileOnClasspath = fileOnClasspath; 214 | } 215 | 216 | /** 217 | * Indicates whether the file is Base64 encoded. If the file is Base64 encoded, its data will be decoded before 218 | * being returned by {@link #getLicenseData(Object)}. 219 | * 220 | * @return whether the file is Base64 encoded. 221 | */ 222 | public boolean isBase64Encoded() { 223 | return this.base64Encoded; 224 | } 225 | 226 | /** 227 | * Sets whether the file is Base64 encoded. If the file is Base64 encoded, its data will be decoded before 228 | * being returned by {@link #getLicenseData(Object)}. 229 | * 230 | * @param base64Encoded Whether the file is Base64 encoded. 231 | */ 232 | public void setBase64Encoded(final boolean base64Encoded) { 233 | this.base64Encoded = base64Encoded; 234 | } 235 | } 236 | -------------------------------------------------------------------------------- /core/src/main/java/ro/esolutions/licensing/License.java: -------------------------------------------------------------------------------- 1 | /* 2 | * License.java from LicenseManager modified Monday, April 8, 2013 12:10:38 CDT (-0500). 3 | * 4 | * Copyright 2010-2013 the original author or authors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package ro.esolutions.licensing; 19 | 20 | import com.google.common.base.MoreObjects; 21 | import com.google.common.base.Strings; 22 | import org.apache.commons.lang.SerializationUtils; 23 | import org.apache.commons.lang.builder.EqualsBuilder; 24 | import org.apache.commons.lang.builder.HashCodeBuilder; 25 | import ro.esolutions.licensing.immutable.ImmutableLinkedHashSet; 26 | 27 | import java.io.Serializable; 28 | import java.time.Instant; 29 | import java.util.Arrays; 30 | import java.util.LinkedHashSet; 31 | import java.util.Objects; 32 | import java.util.Set; 33 | 34 | public final class License implements Serializable, Cloneable { 35 | private static final long serialVersionUID = 1L; 36 | 37 | private final String productKey; 38 | private final String holder; 39 | private final String issuer; 40 | private final String subject; 41 | private final Instant issueDate; 42 | private final Instant goodAfterDate; 43 | private final Instant goodBeforeDate; 44 | private final int seats; 45 | private final ImmutableLinkedHashSet features; 46 | 47 | private License(final License.Builder builder) { 48 | this.productKey = Strings.nullToEmpty(builder.productKey); 49 | this.holder = Strings.nullToEmpty(builder.holder); 50 | this.issuer = Strings.nullToEmpty(builder.issuer); 51 | this.subject = Strings.nullToEmpty(builder.subject); 52 | this.issueDate = builder.issueDate; 53 | this.goodAfterDate = builder.goodAfterDate; 54 | this.goodBeforeDate = builder.goodBeforeDate; 55 | this.seats = builder.seats; 56 | this.features = new ImmutableLinkedHashSet<>(builder.features); 57 | } 58 | 59 | public final byte[] serialize() { 60 | return SerializationUtils.serialize(this); 61 | } 62 | 63 | static License deserialize(byte[] data) { 64 | return (License) SerializationUtils.deserialize(data); 65 | } 66 | 67 | public final String getProductKey() { 68 | return this.productKey; 69 | } 70 | 71 | public final String getIssuer() { 72 | return this.issuer; 73 | } 74 | 75 | public final String getHolder() { 76 | return this.holder; 77 | } 78 | 79 | public final String getSubject() { 80 | return this.subject; 81 | } 82 | 83 | public final Instant getIssueDate() { 84 | return this.issueDate; 85 | } 86 | 87 | public final Instant getGoodAfterDate() { 88 | return this.goodAfterDate; 89 | } 90 | 91 | public final Instant getGoodBeforeDate() { 92 | return this.goodBeforeDate; 93 | } 94 | 95 | public final int getSeats() { 96 | return this.seats; 97 | } 98 | 99 | public final ImmutableLinkedHashSet getFeatures() { 100 | return this.features.clone(); 101 | } 102 | 103 | public final boolean hasLicenseForFeature(final Feature feature) { 104 | return hasLicenseForFeature(feature.getName()); 105 | } 106 | 107 | public final boolean hasLicenseForFeature(final String featureName) { 108 | return this.features.stream().filter(f -> Objects.equals(f.getName(), featureName)) 109 | .findAny() 110 | .map(feature -> feature.getGoodBeforeDate() == null || feature.getGoodBeforeDate().isAfter(Instant.now())) 111 | .orElse(false); 112 | } 113 | 114 | public final boolean hasLicenseForAnyFeature(final Feature... features) { 115 | return Arrays.stream(features) 116 | .map(Feature::getName) 117 | .anyMatch(this::hasLicenseForFeature); 118 | } 119 | 120 | public final boolean hasLicenseForAnyFeature(final String... featureNames) { 121 | return Arrays.stream(featureNames) 122 | .anyMatch(this::hasLicenseForFeature); 123 | } 124 | 125 | public final boolean hasLicenseForAllFeatures(final Feature... features) { 126 | return Arrays.stream(features) 127 | .map(Feature::getName) 128 | .allMatch(this::hasLicenseForFeature); 129 | } 130 | 131 | public final boolean hasLicenseForAllFeatures(final String... featureNames) { 132 | return Arrays.stream(featureNames) 133 | .allMatch(this::hasLicenseForFeature); 134 | } 135 | 136 | @Override 137 | public final boolean equals(final Object object) { 138 | return EqualsBuilder.reflectionEquals(this, object); 139 | } 140 | 141 | @Override 142 | public final int hashCode() { 143 | return HashCodeBuilder.reflectionHashCode(this); 144 | } 145 | 146 | @Override 147 | public final String toString() { 148 | return MoreObjects.toStringHelper(License.class) 149 | .add("productKey", productKey) 150 | .add("holder", holder) 151 | .add("issuer", issuer) 152 | .add("subject", subject) 153 | .add("issueDate", issueDate) 154 | .add("validFrom", goodAfterDate) 155 | .add("goodBeforeDate", goodBeforeDate) 156 | .add("seats", seats) 157 | .add("features", features) 158 | .toString(); 159 | } 160 | 161 | @Override 162 | @SuppressWarnings("CloneDoesntCallSuperClone") 163 | public final License clone() { 164 | final License.Builder builder = new License.Builder() 165 | .withProductKey(this.productKey) 166 | .withHolder(this.holder) 167 | .withIssuer(this.issuer) 168 | .withSubject(this.subject) 169 | .withIssueDate(this.issueDate) 170 | .withGoodAfter(this.goodAfterDate) 171 | .withGoodBefore(this.goodBeforeDate) 172 | .withSeats(this.seats); 173 | 174 | features.forEach(builder::withFeature); 175 | 176 | return builder.build(); 177 | } 178 | 179 | public static final class Builder { 180 | private String productKey; 181 | private String holder; 182 | private String issuer; 183 | private String subject; 184 | private Instant issueDate = Instant.now(); 185 | private Instant goodAfterDate = Instant.MIN; 186 | private Instant goodBeforeDate = Instant.MAX; 187 | private int seats = Integer.MAX_VALUE; 188 | private Set features = new LinkedHashSet<>(); 189 | 190 | public Builder withProductKey(final String productKey) { 191 | this.productKey = productKey; 192 | return this; 193 | } 194 | 195 | public Builder withIssuer(final String issuer) { 196 | this.issuer = issuer; 197 | return this; 198 | } 199 | 200 | public Builder withHolder(final String holder) { 201 | this.holder = holder; 202 | return this; 203 | } 204 | 205 | public Builder withSubject(final String subject) { 206 | this.subject = subject; 207 | return this; 208 | } 209 | 210 | public Builder withIssueDate(final Instant issueDate) { 211 | this.issueDate = issueDate; 212 | return this; 213 | } 214 | 215 | public Builder withGoodAfter(final Instant goodAfterDate) { 216 | this.goodAfterDate = goodAfterDate; 217 | return this; 218 | } 219 | 220 | public Builder withGoodBefore(final Instant goodBeforeDate) { 221 | this.goodBeforeDate = goodBeforeDate; 222 | return this; 223 | } 224 | 225 | public Builder withSeats(final int seats) { 226 | this.seats = seats; 227 | return this; 228 | } 229 | 230 | public Builder withFeature(final String featureName) { 231 | this.features.add(Feature.of(featureName).build()); 232 | return this; 233 | } 234 | 235 | public Builder withFeature(final Feature feature) { 236 | this.features.add(feature); 237 | return this; 238 | } 239 | 240 | public License build() { 241 | return new License(this); 242 | } 243 | } 244 | } 245 | -------------------------------------------------------------------------------- /core/src/main/java/ro/esolutions/licensing/LicenseManagerProperties.java: -------------------------------------------------------------------------------- 1 | /* 2 | * LicenseManagerProperties.java from LicenseManager modified Thursday, May 17, 2012 21:31:40 CDT (-0500). 3 | * 4 | * Copyright 2010-2013 the original author or authors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package ro.esolutions.licensing; 20 | 21 | import ro.esolutions.licensing.encryption.PasswordProvider; 22 | import ro.esolutions.licensing.encryption.PublicKeyDataProvider; 23 | 24 | /** 25 | * This class is used to set properties that will be used to instantiate the {@link LicenseManager}. Read the 26 | * documentation for each property below. 27 | * 28 | * @author Nick Williams 29 | * @version 1.0.0 30 | * @since 1.0.0 31 | */ 32 | public final class LicenseManagerProperties { 33 | private static PublicKeyDataProvider publicKeyDataProvider; 34 | 35 | private static PasswordProvider publicKeyPasswordProvider; 36 | 37 | private static LicenseProvider licenseProvider; 38 | 39 | private static PasswordProvider licensePasswordProvider; 40 | 41 | private static LicenseValidator licenseValidator; 42 | 43 | private static int cacheTimeInMinutes; 44 | 45 | /** 46 | * Sets the provider of the data for the public key companion to the private key used to sign the license 47 | * object.
48 | *
49 | * This field is required. 50 | * 51 | * @param publicKeyDataProvider The provider of the data for the public key companion to the private key used to 52 | * sign the license object 53 | */ 54 | public static void setPublicKeyDataProvider(final PublicKeyDataProvider publicKeyDataProvider) { 55 | LicenseManagerProperties.publicKeyDataProvider = publicKeyDataProvider; 56 | } 57 | 58 | static PublicKeyDataProvider getPublicKeyDataProvider() { 59 | return LicenseManagerProperties.publicKeyDataProvider; 60 | } 61 | 62 | /** 63 | * Sets the provider of the password for decrypting the public key.
64 | *
65 | * This field is required. 66 | * 67 | * @param publicKeyPasswordProvider The provider of the password for decrypting the public key 68 | */ 69 | public static void setPublicKeyPasswordProvider(final PasswordProvider publicKeyPasswordProvider) { 70 | LicenseManagerProperties.publicKeyPasswordProvider = publicKeyPasswordProvider; 71 | } 72 | 73 | static PasswordProvider getPublicKeyPasswordProvider() { 74 | return LicenseManagerProperties.publicKeyPasswordProvider; 75 | } 76 | 77 | /** 78 | * Sets the provider of the persisted license data.
79 | *
80 | * This field is required. 81 | * 82 | * @param licenseProvider The provider of the persisted license data 83 | */ 84 | public static void setLicenseProvider(final LicenseProvider licenseProvider) { 85 | LicenseManagerProperties.licenseProvider = licenseProvider; 86 | } 87 | 88 | static LicenseProvider getLicenseProvider() { 89 | return LicenseManagerProperties.licenseProvider; 90 | } 91 | 92 | /** 93 | * Sets the provider of the password for the persisted license data.
94 | *
95 | * This field is optional. If not provided, the 96 | * {@link #setPublicKeyPasswordProvider(PasswordProvider) publicKeyPasswordProvider} will be used to decrypt 97 | * licenses. 98 | * 99 | * @param licensePasswordProvider The provider of the password for decrypting license data 100 | */ 101 | public static void setLicensePasswordProvider(final PasswordProvider licensePasswordProvider) { 102 | LicenseManagerProperties.licensePasswordProvider = licensePasswordProvider; 103 | } 104 | 105 | static PasswordProvider getLicensePasswordProvider() { 106 | return LicenseManagerProperties.licensePasswordProvider; 107 | } 108 | 109 | /** 110 | * Sets the validator implementation that validates all licenses; if null, licenses are assumed to always be valid. 111 | * If you do not want to validate licenses automatically, you do not need to provide a validator, or you may set 112 | * it to null.
113 | *
114 | * This field is optional and defaults to no validation. 115 | * 116 | * @param licenseValidator The validator implementation that validates all licenses; if null, licenses are assumed 117 | * to always be valid 118 | */ 119 | public static void setLicenseValidator(final LicenseValidator licenseValidator) { 120 | LicenseManagerProperties.licenseValidator = licenseValidator; 121 | } 122 | 123 | static LicenseValidator getLicenseValidator() { 124 | return LicenseManagerProperties.licenseValidator; 125 | } 126 | 127 | /** 128 | * Sets the length of time in minutes to cache license information (for performance reasons, anything less than 1 129 | * minute results in a 10-second cache life; the cache cannot be disabled completely).
130 | *
131 | * This field is optional and defaults to 10 seconds. 132 | * 133 | * @param cacheTimeInMinutes The length of time in minutes to cache license information 134 | */ 135 | public static void setCacheTimeInMinutes(final int cacheTimeInMinutes) { 136 | LicenseManagerProperties.cacheTimeInMinutes = cacheTimeInMinutes; 137 | } 138 | 139 | static int getCacheTimeInMinutes() { 140 | return cacheTimeInMinutes; 141 | } 142 | 143 | /** 144 | * This class cannot be instantiated. 145 | */ 146 | private LicenseManagerProperties() { 147 | throw new RuntimeException("This class cannot be instantiated."); 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /core/src/main/java/ro/esolutions/licensing/LicenseProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * LicenseProvider.java from LicenseManager modified Tuesday, February 21, 2012 10:59:35 CST (-0600). 3 | * 4 | * Copyright 2010-2013 the original author or authors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package ro.esolutions.licensing; 20 | 21 | /** 22 | * This specifies an interface for providing and persisting the stored, still-encrypted license content and signature 23 | * object. 24 | * 25 | * @author Nick Williams 26 | * @version 1.0.0 27 | * @since 1.0.0 28 | */ 29 | public interface LicenseProvider { 30 | /** 31 | * Gets the stored, still-encrypted license content and signature from the persistence store. 32 | * 33 | * @param context The context for which to get the license 34 | * @return the signed license object. 35 | */ 36 | SignedLicense getLicense(final Object context); 37 | } 38 | -------------------------------------------------------------------------------- /core/src/main/java/ro/esolutions/licensing/LicenseValidator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * LicenseValidator.java from LicenseManager modified Tuesday, February 21, 2012 10:59:35 CST (-0600). 3 | * 4 | * Copyright 2010-2013 the original author or authors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package ro.esolutions.licensing; 20 | 21 | import ro.esolutions.licensing.exception.InvalidLicenseException; 22 | 23 | /** 24 | * Specifies an interface for validating licenses. Users of License Manager do not have to implement this interface. 25 | * If it is not implemented, it is assumed that all licenses are valid. Users are encouraged, however, to implement 26 | * interface.
27 | *
28 | * There is a default implementation, {@link DefaultLicenseValidator}, that ensures the current date is between the 29 | * license's good-after and good-before dates (the license has taken effect and hasn't expired). 30 | * 31 | * @author Nick Williams 32 | * @version 1.0.1 33 | * @since 1.0.0 34 | */ 35 | public interface LicenseValidator { 36 | /** 37 | * Validates the license provided and throws an exception if the license is invalid for any reason 38 | * (expired, not who it belongs to, etc.). 39 | * 40 | * @param license The license to validate 41 | * @throws InvalidLicenseException when the license is invalid for any reason; the 42 | * implementer is required to provide adequate 43 | * description in this exception to indicate why 44 | * the license is invalid; extending the exception 45 | * is encouraged. 46 | * @throws ro.esolutions.licensing.exception.ExpiredLicenseException when the license is expired. 47 | */ 48 | void validateLicense(final License license) throws InvalidLicenseException; 49 | } 50 | -------------------------------------------------------------------------------- /core/src/main/java/ro/esolutions/licensing/ObjectSerializer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ObjectSerializer.java from LicenseManager modified Thursday, May 17, 2012 21:31:40 CDT (-0500). 3 | * 4 | * Copyright 2010-2013 the original author or authors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package ro.esolutions.licensing; 20 | 21 | import org.apache.commons.io.output.ByteArrayOutputStream; 22 | import ro.esolutions.licensing.exception.ObjectDeserializationException; 23 | import ro.esolutions.licensing.exception.ObjectSerializationException; 24 | import ro.esolutions.licensing.exception.ObjectTypeNotExpectedException; 25 | 26 | import java.io.*; 27 | 28 | /** 29 | * This is a helper class for writing any object and reading simple objects (no 30 | * arrays, collections, or generic top-level objects) to and from byte arrays. 31 | * 32 | * @author Nick Williams 33 | * @version 1.0.0 34 | * @since 1.0.0 35 | */ 36 | public final class ObjectSerializer { 37 | 38 | public final T readObject(final Class expectedType, final byte[] byteStream) 39 | throws ObjectDeserializationException { 40 | try (final ByteArrayInputStream bytes = new ByteArrayInputStream(byteStream); 41 | final ObjectInputStream stream = new ObjectInputStream(bytes)) { 42 | 43 | final Object allegedObject = stream.readObject(); 44 | if (!expectedType.isInstance(allegedObject)) { 45 | throw new ObjectTypeNotExpectedException(expectedType.getName(), allegedObject.getClass().getName()); 46 | } 47 | 48 | return expectedType.cast(allegedObject); 49 | } catch (final IOException e) { 50 | throw new ObjectDeserializationException("An I/O error occurred while reading the object from the byte array.", e); 51 | } catch (final ClassNotFoundException | NoClassDefFoundError e) { 52 | throw new ObjectTypeNotExpectedException(expectedType.getName(), e.getMessage(), e); 53 | } 54 | } 55 | 56 | /** 57 | * Serializes the {@link Serializable} object passed and returns it as a byte array. 58 | * 59 | * @param object The object to serialize 60 | * @return the byte stream with the object serialized in it. 61 | * @throws ObjectSerializationException if an I/O exception occurs while serializing the object. 62 | */ 63 | public final byte[] writeObject(final Serializable object) throws ObjectSerializationException { 64 | 65 | try (final ByteArrayOutputStream bytes = new ByteArrayOutputStream(); 66 | final ObjectOutputStream stream = new ObjectOutputStream(bytes)) { 67 | stream.writeObject(object); 68 | return bytes.toByteArray(); 69 | } catch (final IOException e) { 70 | throw new ObjectSerializationException(e); 71 | } 72 | 73 | 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /core/src/main/java/ro/esolutions/licensing/SignedLicense.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SignedLicense.java from LicenseManager modified Tuesday, February 21, 2012 10:59:35 CST (-0600). 3 | * 4 | * Copyright 2010-2013 the original author or authors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package ro.esolutions.licensing; 20 | 21 | import java.io.Serializable; 22 | import java.util.Arrays; 23 | 24 | /** 25 | * This class contains the encrypted license content and the signature for the 26 | * encrypted license content. 27 | * 28 | * @author Nick Williams 29 | * @version 1.0.0 30 | * @since 1.0.0 31 | */ 32 | public final class SignedLicense implements Serializable { 33 | private final static long serialVersionUID = -8465360339059185020L; 34 | 35 | private final byte[] licenseContent; 36 | private final byte[] signatureContent; 37 | 38 | public SignedLicense(final byte[] licenseContent,final byte[] signatureContent) { 39 | this.licenseContent = Arrays.copyOf(licenseContent, licenseContent.length); 40 | this.signatureContent = Arrays.copyOf(signatureContent, signatureContent.length); 41 | } 42 | 43 | /** 44 | * Get the content of the actual license object. This is encrypted and 45 | * corresponds to {@link License}. For security reasons, only a copy of 46 | * the content is returned. 47 | * 48 | * @return the encrypted license content. 49 | */ 50 | public final byte[] getLicenseContent() { 51 | return Arrays.copyOf(this.licenseContent, this.licenseContent.length); 52 | } 53 | 54 | /** 55 | * Get the signature for the license content. For security reasons, only a 56 | * copy of the signature is returned. 57 | * 58 | * @return the license signature. 59 | */ 60 | public final byte[] getSignatureContent() { 61 | return Arrays.copyOf(this.signatureContent, this.signatureContent.length); 62 | } 63 | 64 | /** 65 | * Erase the contents of this object. This is a security feature to write 66 | * zeroes to the license and signature data so that it doesn't hang around 67 | * in memory where it might be reverse engineered. 68 | */ 69 | protected final void erase() { 70 | Arrays.fill(this.licenseContent, (byte) 0); 71 | Arrays.fill(this.signatureContent, (byte) 0); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /core/src/main/java/ro/esolutions/licensing/encryption/Encryptor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Encryptor.java from LicenseManager modified Monday, April 8, 2013 12:11:51 CDT (-0500). 3 | * 4 | * Copyright 2010-2013 the original author or authors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package ro.esolutions.licensing.encryption; 20 | 21 | import org.apache.commons.codec.Charsets; 22 | import org.apache.commons.codec.binary.Base64; 23 | 24 | import java.security.InvalidKeyException; 25 | import java.security.NoSuchAlgorithmException; 26 | import java.security.SecureRandom; 27 | import java.security.spec.InvalidKeySpecException; 28 | 29 | import javax.crypto.BadPaddingException; 30 | import javax.crypto.Cipher; 31 | import javax.crypto.IllegalBlockSizeException; 32 | import javax.crypto.NoSuchPaddingException; 33 | import javax.crypto.SecretKey; 34 | import javax.crypto.SecretKeyFactory; 35 | import javax.crypto.spec.PBEKeySpec; 36 | import javax.crypto.spec.SecretKeySpec; 37 | 38 | import ro.esolutions.licensing.exception.AlgorithmNotSupportedException; 39 | import ro.esolutions.licensing.exception.FailedToDecryptException; 40 | import ro.esolutions.licensing.exception.InappropriateKeyException; 41 | import ro.esolutions.licensing.exception.InappropriateKeySpecificationException; 42 | 43 | /** 44 | * A class for easy, strong, two-way encryption/decryption of strings. Versions prior to 0.9.1-beta used 256-bit AES 45 | * encryption, which is not exportable, and will only work by default on Mac OS X and Linux. The Windows JVM will throw 46 | * an exception without the JCE Unlimited Strength policy file. Versions 0.9.1-beta and higher use 128-bit AES 47 | * encryption that is both exportable and platform-independent.
48 | *
49 | * This encryptor still uses a combination of MD5+DES and SHA-1+AES encryption.
50 | *
51 | * Data encrypted with this class prior to version 0.9.1-beta cannot be decrypted anymore. 52 | * 53 | * @author Nick Williams 54 | * @version 1.5.0 55 | * @since 1.0.0 56 | */ 57 | public final class Encryptor { 58 | private static final int MINIMUM_PADDED_LENGTH = 20; 59 | private static final char[] DEFAULT_PASS_PHRASE = { 60 | 'j', '4', 'K', 'g', 'U', '3', '0', '5', 'P', 'Z', 'p', '\'', 't', 61 | '.', '"', '%', 'o', 'r', 'd', 'A', 'Y', '7', 'q', '*', '?', 'z', 62 | '9', '%', '8', ']', 'a', 'm', 'N', 'L', '(', '0', 'W', 'x', '5', 63 | 'e', 'G', '4', '9', 'b', '1', 's', 'R', 'j', '(', '^', ';', '8', 64 | 'K', 'g', '2', 'w', '0', 'E', 'o', 'M' 65 | }; 66 | 67 | private static final byte[] SALT = { 68 | (byte) 0xA9, (byte) 0xA2, (byte) 0xB5, (byte) 0xDE, 69 | (byte) 0x2A, (byte) 0x8A, (byte) 0x9A, (byte) 0xE6 70 | }; 71 | 72 | private static final int ITERATION_COUNT = 1024; 73 | 74 | // must be 128, 192, 256; 128 is maximum without "unlimited strength" JCE policy files 75 | private static final int AES_KEY_LENGTH = 128; 76 | 77 | private static final SecureRandom SECURE_RANDOM = new SecureRandom(); 78 | 79 | private static Cipher defaultEncryptionCipher; 80 | private static Cipher defaultDecryptionCipher; 81 | 82 | /** 83 | * Encrypt the plain-text string using the default passphrase. 84 | * For encrypting, the data will first be padded to a safe number of 85 | * bytes with randomized data. 86 | * 87 | * @param unencrypted The plain-text string to encrypt 88 | * @return the encrypted string Base64-encoded. 89 | * @see Encryptor#pad(byte[], int) 90 | */ 91 | public static String encrypt(final String unencrypted) { 92 | return Encryptor.encrypt(unencrypted.getBytes(Charsets.UTF_8)); 93 | } 94 | 95 | /** 96 | * Encrypt the plain-text string. For encrypting, the 97 | * string will first be padded to a safe number of 98 | * characters with randomized data. 99 | * 100 | * @param unencrypted The plain-text string to encrypt 101 | * @param passPhrase The passPhrase to encrypt the data with 102 | * @return the encrypted string Base64-encoded. 103 | */ 104 | public static String encrypt(final String unencrypted, final char[] passPhrase) { 105 | return Encryptor.encrypt(unencrypted.getBytes(Charsets.UTF_8), passPhrase); 106 | } 107 | 108 | /** 109 | * Encrypt the binary data using the default passphrase. 110 | * For encrypting, the data will first be padded to a safe number of 111 | * bytes with randomized data. 112 | * 113 | * @param unencrypted The binary data to encrypt 114 | * @return the encrypted string Base64-encoded. 115 | * @see Encryptor#pad(byte[], int) 116 | */ 117 | public static String encrypt(final byte[] unencrypted) { 118 | return new String(Base64.encodeBase64URLSafe(Encryptor.encryptRaw(unencrypted)), Charsets.UTF_8); 119 | } 120 | 121 | /** 122 | * Encrypt the binary data. For encrypting, the 123 | * data will first be padded to a safe number of 124 | * bytes with randomized data. 125 | * 126 | * @param unencrypted The binary data to encrypt 127 | * @param passPhrase The passPhrase to encrypt the data with 128 | * @return the encrypted string Base64-encoded. 129 | * @see Encryptor#pad(byte[], int) 130 | */ 131 | public static String encrypt(final byte[] unencrypted, final char[] passPhrase) { 132 | return new String( 133 | Base64.encodeBase64URLSafe(Encryptor.encryptRaw(unencrypted, passPhrase)), Charsets.UTF_8); 134 | } 135 | 136 | /** 137 | * Encrypt the binary data using the default passphrase. 138 | * For encrypting, the data will first be padded to a safe number of 139 | * bytes with randomized data. 140 | * 141 | * @param unencrypted The binary data to encrypt 142 | * @return the encrypted data. 143 | * @see Encryptor#pad(byte[], int) 144 | */ 145 | public static byte[] encryptRaw(final byte[] unencrypted) { 146 | try { 147 | return Encryptor.getDefaultEncryptionCipher() 148 | .doFinal(Encryptor.pad(unencrypted, Encryptor.MINIMUM_PADDED_LENGTH)); 149 | } catch (final IllegalBlockSizeException | BadPaddingException e) { 150 | throw new RuntimeException("While encrypting the data...", e); 151 | } 152 | } 153 | 154 | /** 155 | * Encrypt the binary data. For encrypting, the 156 | * data will first be padded to a safe number of 157 | * bytes with randomized data. 158 | * 159 | * @param unencrypted The binary data to encrypt 160 | * @param passPhrase The passPhrase to encrypt the data with 161 | * @return the encrypted data. 162 | * @see Encryptor#pad(byte[], int) 163 | */ 164 | public static byte[] encryptRaw(final byte[] unencrypted, final char[] passPhrase) { 165 | try { 166 | return Encryptor.getEncryptionCipher(passPhrase) 167 | .doFinal(Encryptor.pad(unencrypted, Encryptor.MINIMUM_PADDED_LENGTH) 168 | ); 169 | } catch (final IllegalBlockSizeException | BadPaddingException e) { 170 | throw new RuntimeException("While encrypting the data...", e); 171 | } 172 | } 173 | 174 | /** 175 | * Decrypt an encrypted string using the default passphrase. 176 | * Any padded data will be removed from the string prior to its return. 177 | * 178 | * @param encrypted The encrypted string to decrypt 179 | * @return the decrypted string. 180 | * @throws FailedToDecryptException when the data was corrupt and undecryptable or when the provided decryption 181 | * password was incorrect. It is impossible to know which is the actual cause. 182 | * @see Encryptor#unPad(byte[]) 183 | */ 184 | public static String decrypt(final String encrypted) { 185 | return Encryptor.decrypt(Base64.decodeBase64(encrypted)); 186 | } 187 | 188 | /** 189 | * Decrypt an encrypted string. Any padded data will 190 | * be removed from the string prior to its return. 191 | * 192 | * @param encrypted The encrypted string to decrypt 193 | * @param passPhrase The passPhrase to decrypt the string with 194 | * @return the decrypted string. 195 | * @throws FailedToDecryptException when the data was corrupt and undecryptable or when the provided decryption 196 | * password was incorrect. It is impossible to know which is the actual cause. 197 | * @see Encryptor#unPad(byte[]) 198 | */ 199 | public static String decrypt(final String encrypted, final char[] passPhrase) { 200 | return Encryptor.decrypt(Base64.decodeBase64(encrypted), passPhrase); 201 | } 202 | 203 | /** 204 | * Decrypt encrypted data using the default passphrase. 205 | * Any padded data will be removed from the string prior to its return. 206 | * 207 | * @param encrypted The encrypted data to decrypt 208 | * @return the decrypted string. 209 | * @throws FailedToDecryptException when the data was corrupt and undecryptable or when the provided decryption 210 | * password was incorrect. It is impossible to know which is the actual cause. 211 | * @see Encryptor#unPad(byte[]) 212 | */ 213 | public static String decrypt(final byte[] encrypted) { 214 | return new String(Encryptor.decryptRaw(encrypted), Charsets.UTF_8); 215 | } 216 | 217 | /** 218 | * Decrypt an encrypted data. Any padded data will 219 | * be removed from the string prior to its return. 220 | * 221 | * @param encrypted The encrypted data to decrypt 222 | * @param passPhrase The passPhrase to decrypt the data with 223 | * @return the decrypted string. 224 | * @throws FailedToDecryptException when the data was corrupt and undecryptable or when the provided decryption 225 | * password was incorrect. It is impossible to know which is the actual cause. 226 | * @see Encryptor#unPad(byte[]) 227 | */ 228 | public static String decrypt(final byte[] encrypted, final char[] passPhrase) { 229 | return new String(Encryptor.decryptRaw(encrypted, passPhrase), Charsets.UTF_8); 230 | } 231 | 232 | /** 233 | * Decrypt encrypted data using the default passphrase. 234 | * Any padded data will be removed from the string prior to its return. 235 | * 236 | * @param encrypted The encrypted data to decrypt 237 | * @return the decrypted binary data. 238 | * @throws FailedToDecryptException when the data was corrupt and undecryptable or when the provided decryption 239 | * password was incorrect. It is impossible to know which is the actual cause. 240 | * @see Encryptor#unPad(byte[]) 241 | */ 242 | public static byte[] decryptRaw(final byte[] encrypted) { 243 | try { 244 | return Encryptor.unPad(Encryptor.getDefaultDecryptionCipher().doFinal(encrypted)); 245 | } catch (final IllegalBlockSizeException | BadPaddingException e) { 246 | throw new FailedToDecryptException(e); 247 | } 248 | } 249 | 250 | /** 251 | * Decrypt encrypted data. Any padded data will 252 | * be removed from the string prior to its return. 253 | * 254 | * @param encrypted The encrypted data to decrypt 255 | * @param passPhrase The passPhrase to decrypt the data with 256 | * @return the decrypted binary data. 257 | * @throws FailedToDecryptException when the data was corrupt and undecryptable or when the provided decryption 258 | * password was incorrect. It is impossible to know which is the actual cause. 259 | * @see Encryptor#unPad(byte[]) 260 | */ 261 | public static byte[] decryptRaw(final byte[] encrypted, final char[] passPhrase) { 262 | try { 263 | return Encryptor.unPad(Encryptor.getDecryptionCipher(passPhrase).doFinal(encrypted)); 264 | } catch (final IllegalBlockSizeException | BadPaddingException e) { 265 | throw new FailedToDecryptException(e); 266 | } 267 | } 268 | 269 | /** 270 | * Pads a {@code byte} array to the specified length. 271 | * The output is pretty simple. The begin {@code byte}s 272 | * are the values from {@code bytes}. The last 273 | * {@code byte}, when cast to an integer, indicates the 274 | * number of end {@code byte}s (including itself) that 275 | * make up the padding. The returned array will always 276 | * be at least one element longer than the input.
277 | *
278 | * For example, if passed an array of 5 {@code byte}s and 279 | * the length 10, the first five {@code byte}s will be the 280 | * values from {@code bytes}. {@code byte}s 6-10 (indexes 281 | * 5-9) will be randomized data and {@code byte} 11 282 | * (index 10) will be the integer 6 cast as a byte. The 283 | * actual returned array will be 11 {@code byte}s long.
284 | *
285 | * If passed an array of 10 {@code byte}s and the length 286 | * of 10, the first 10 {@code byte}s will be the input 287 | * and {@code byte} 11 will be 1. 288 | * 289 | * @param bytes The array of {@code byte}s to pad 290 | * @param length The length to pad the array of {@code byte}s to 291 | * @return the padded {@code byte} array. 292 | * @see Encryptor#unPad(byte[]) 293 | */ 294 | private static byte[] pad(final byte[] bytes, final int length) { 295 | if (bytes.length >= length) { 296 | final byte[] out = new byte[bytes.length + 1]; 297 | System.arraycopy(bytes, 0, out, 0, bytes.length); 298 | out[bytes.length] = (byte) 1; 299 | return out; 300 | } 301 | 302 | final byte[] out = new byte[length + 1]; 303 | 304 | int i = 0; 305 | for (; i < bytes.length; i++) 306 | out[i] = bytes[i]; 307 | 308 | final int padded = length - i; 309 | 310 | // fill the rest with SECURE_RANDOM bytes 311 | final byte[] fill = new byte[padded - 1]; 312 | Encryptor.SECURE_RANDOM.nextBytes(fill); 313 | System.arraycopy(fill, 0, out, i, padded - 1); 314 | 315 | out[length] = (byte) (padded + 1); 316 | 317 | return out; 318 | } 319 | 320 | /** 321 | * Un-pads the specified array of {@code byte}s. Expects 322 | * an input that was padded with 323 | * {@link Encryptor#pad(byte[], int)}. Its behavior is 324 | * unspecified if passed an input that was not the 325 | * result of {@link Encryptor#pad(byte[], int)}.
326 | *
327 | * The returned array will be the {@code byte}s with all 328 | * the padding removed and the original {@code byte}s 329 | * left intact. 330 | * 331 | * @param bytes The array of {@code byte}s to un-pad 332 | * @return the un-padded {@code byte} array. 333 | * @see Encryptor#pad(byte[], int) 334 | */ 335 | private static byte[] unPad(final byte[] bytes) { 336 | final int padded = (int) bytes[bytes.length - 1]; 337 | final int targetLength = bytes.length - padded; 338 | 339 | final byte[] out = new byte[targetLength]; 340 | 341 | System.arraycopy(bytes, 0, out, 0, targetLength); 342 | 343 | return out; 344 | } 345 | 346 | private static SecretKey getSecretKey(final char[] passPhrase) { 347 | try { 348 | final PBEKeySpec keySpec = new PBEKeySpec( 349 | passPhrase, 350 | Encryptor.SALT, 351 | Encryptor.ITERATION_COUNT, 352 | Encryptor.AES_KEY_LENGTH 353 | ); 354 | 355 | final byte[] shortKey = SecretKeyFactory.getInstance("PBEWithMD5AndDES"). 356 | generateSecret(keySpec).getEncoded(); 357 | 358 | final byte[] intermediaryKey = new byte[Encryptor.AES_KEY_LENGTH / 8]; 359 | for (int i = 0, j = 0; i < Encryptor.AES_KEY_LENGTH / 8; i++) { 360 | intermediaryKey[i] = shortKey[j]; 361 | if (++j == shortKey.length) 362 | j = 0; 363 | } 364 | 365 | return new SecretKeySpec(intermediaryKey, "AES"); 366 | } catch (final NoSuchAlgorithmException e) { 367 | throw new AlgorithmNotSupportedException("DES with an MD5 Digest", e); 368 | } catch (final InvalidKeySpecException e) { 369 | throw new InappropriateKeySpecificationException(e); 370 | } 371 | } 372 | 373 | private static Cipher getDefaultEncryptionCipher() { 374 | if (Encryptor.defaultEncryptionCipher == null) 375 | Encryptor.defaultEncryptionCipher = Encryptor.getEncryptionCipher(Encryptor.DEFAULT_PASS_PHRASE); 376 | 377 | return Encryptor.defaultEncryptionCipher; 378 | } 379 | 380 | private static Cipher getEncryptionCipher(final char[] passPhrase) { 381 | return Encryptor.getEncryptionCipher(Encryptor.getSecretKey(passPhrase)); 382 | } 383 | 384 | private static Cipher getEncryptionCipher(final SecretKey secretKey) { 385 | try { 386 | final Cipher cipher = Cipher.getInstance(secretKey.getAlgorithm()); 387 | cipher.init(Cipher.ENCRYPT_MODE, secretKey, Encryptor.SECURE_RANDOM); 388 | return cipher; 389 | } catch (final NoSuchAlgorithmException e) { 390 | throw new AlgorithmNotSupportedException("AES With SHA-1 digest", e); 391 | } catch (final NoSuchPaddingException e) { 392 | throw new RuntimeException(e.getMessage(), e); 393 | } catch (final InvalidKeyException e) { 394 | throw new InappropriateKeyException(e.getMessage(), e); 395 | } 396 | } 397 | 398 | private static Cipher getDefaultDecryptionCipher() { 399 | if (Encryptor.defaultDecryptionCipher == null) { 400 | Encryptor.defaultDecryptionCipher = Encryptor.getDecryptionCipher(Encryptor.DEFAULT_PASS_PHRASE); 401 | } 402 | return Encryptor.defaultDecryptionCipher; 403 | } 404 | 405 | private static Cipher getDecryptionCipher(char[] passPhrase) { 406 | return Encryptor.getDecryptionCipher(Encryptor.getSecretKey(passPhrase)); 407 | } 408 | 409 | private static Cipher getDecryptionCipher(final SecretKey secretKey) { 410 | try { 411 | final Cipher cipher = Cipher.getInstance(secretKey.getAlgorithm()); 412 | cipher.init(Cipher.DECRYPT_MODE, secretKey, Encryptor.SECURE_RANDOM); 413 | return cipher; 414 | } catch (final NoSuchAlgorithmException e) { 415 | throw new AlgorithmNotSupportedException("AES With SHA-1 digest", e); 416 | } catch (final NoSuchPaddingException e) { 417 | throw new RuntimeException(e.getMessage(), e); 418 | } catch (final InvalidKeyException e) { 419 | throw new InappropriateKeyException(e.getMessage(), e); 420 | } 421 | } 422 | 423 | /** 424 | * This class cannot be instantiated. 425 | */ 426 | private Encryptor() { 427 | throw new RuntimeException("This class cannot be instantiated."); 428 | } 429 | } 430 | -------------------------------------------------------------------------------- /core/src/main/java/ro/esolutions/licensing/encryption/FilePublicKeyDataProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * FilePublicKeyDataProvider.java from LicenseManager modified Thursday, May 17, 2012 21:31:40 CDT (-0500). 3 | * 4 | * Copyright 2010-2013 the original author or authors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package ro.esolutions.licensing.encryption; 20 | 21 | import org.apache.commons.io.FileUtils; 22 | import ro.esolutions.licensing.exception.KeyNotFoundException; 23 | 24 | import java.io.File; 25 | import java.io.FileNotFoundException; 26 | import java.io.IOException; 27 | 28 | /** 29 | * A default implementation of {@link PublicKeyDataProvider} that reads the public key from a file.
30 | *
31 | * This provider is immutable. Once created, the file that the public key is located at cannot be changed. 32 | * 33 | * @author Nick Williams 34 | * @version 1.0.0 35 | * @since 1.0.0 36 | */ 37 | public class FilePublicKeyDataProvider implements PublicKeyDataProvider { 38 | private final File publicKeyFile; 39 | 40 | /** 41 | * Create a new provider, specifying the file from which the public key can be read. 42 | * 43 | * @param publicKeyFile the public key file 44 | */ 45 | public FilePublicKeyDataProvider(final File publicKeyFile) { 46 | this.publicKeyFile = publicKeyFile.getAbsoluteFile(); 47 | } 48 | 49 | /** 50 | * Create a new provider, specifying the name of the file from which the public key can be read. 51 | * 52 | * @param publicKeyFileName The public key file name 53 | */ 54 | public FilePublicKeyDataProvider(final String publicKeyFileName) { 55 | this.publicKeyFile = new File(publicKeyFileName).getAbsoluteFile(); 56 | } 57 | 58 | /** 59 | * This method returns the data from the file containing the encrypted 60 | * public key from the public/private key pair. The contract for this 61 | * method can be fulfilled by storing the data in a byte array literal 62 | * in the source code itself.
63 | *
64 | * It is imperative that you obfuscate the bytecode for the 65 | * implementation of this class. It is also imperative that the byte 66 | * array exist only for the life of this method (i.e., DO NOT store it as 67 | * an instance or class field). 68 | * 69 | * @return the encrypted file contents from the public key file. 70 | * @throws KeyNotFoundException if the key data could not be retrieved; an acceptable message or chained cause must 71 | * be provided. 72 | */ 73 | @Override 74 | public byte[] getEncryptedPublicKeyData() throws KeyNotFoundException { 75 | try { 76 | return FileUtils.readFileToByteArray(this.publicKeyFile); 77 | } catch (final FileNotFoundException e) { 78 | throw new KeyNotFoundException("The public key file [" + this.publicKeyFile.getPath() + "] does not exist."); 79 | } catch (final IOException e) { 80 | throw new KeyNotFoundException("Could not read from the public key file [" + this.publicKeyFile.getPath() + "].", e); 81 | } 82 | } 83 | 84 | /** 85 | * Gets the file that the public key is located at. 86 | * 87 | * @return the file. 88 | */ 89 | public File getPublicKeyFile() { 90 | return this.publicKeyFile; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /core/src/main/java/ro/esolutions/licensing/encryption/Hasher.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Hasher.java from LicenseManager modified Monday, April 8, 2013 12:11:51 CDT (-0500). 3 | * 4 | * Copyright 2010-2013 the original author or authors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package ro.esolutions.licensing.encryption; 20 | 21 | import org.apache.commons.codec.Charsets; 22 | import org.apache.commons.codec.binary.Base64; 23 | 24 | import java.security.MessageDigest; 25 | import java.security.NoSuchAlgorithmException; 26 | 27 | import ro.esolutions.licensing.exception.AlgorithmNotSupportedException; 28 | 29 | /** 30 | * Used for creating hash keys of things that won't need to be unencrypted. 31 | * 32 | * @author Nick Williams 33 | * @version 1.0.0 34 | * @since 1.0.0 35 | */ 36 | public class Hasher { 37 | /** 38 | * The ALGORITHM we use to hash strings. 39 | */ 40 | private static final String ALGORITHM = "SHA-512"; 41 | 42 | /** 43 | * The salt that we use to hash strings. 44 | */ 45 | private static final String SALT = 46 | "j4KgU305PZp't.\"%ordAY7q*?z9%8]amNL(0Wx5eG49b1sRj(^;8Kg2w0EoM"; 47 | 48 | /** 49 | * Calculate the SHA-512 message digest hash of the 50 | * provided string and return it with its binary 51 | * data Base64 encoded. 52 | * 53 | * @param string The string to hash 54 | * @return the hashed string Base64 encoded. 55 | */ 56 | public static String hash(final String string) { 57 | try { 58 | final byte[] digest = MessageDigest.getInstance(ALGORITHM).digest((string + SALT).getBytes(Charsets.UTF_8)); 59 | return new String(Base64.encodeBase64(digest),Charsets.UTF_8); 60 | } catch (final NoSuchAlgorithmException e) { 61 | throw new AlgorithmNotSupportedException(ALGORITHM, e); 62 | } 63 | } 64 | 65 | /** 66 | * This class cannot be instantiated. 67 | */ 68 | private Hasher() { 69 | throw new RuntimeException("This class cannot be instantiated."); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /core/src/main/java/ro/esolutions/licensing/encryption/KeyFileUtilities.java: -------------------------------------------------------------------------------- 1 | /* 2 | * KeyFileUtilities.java from LicenseManager modified Tuesday, February 21, 2012 10:59:34 CST (-0600). 3 | * 4 | * Copyright 2010-2013 the original author or authors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package ro.esolutions.licensing.encryption; 20 | 21 | import org.apache.commons.io.FileUtils; 22 | 23 | import java.io.File; 24 | import java.io.IOException; 25 | import java.security.KeyFactory; 26 | import java.security.NoSuchAlgorithmException; 27 | import java.security.PrivateKey; 28 | import java.security.PublicKey; 29 | import java.security.spec.InvalidKeySpecException; 30 | import java.security.spec.PKCS8EncodedKeySpec; 31 | import java.security.spec.X509EncodedKeySpec; 32 | 33 | import ro.esolutions.licensing.exception.AlgorithmNotSupportedException; 34 | import ro.esolutions.licensing.exception.InappropriateKeySpecificationException; 35 | 36 | /** 37 | * A class of utility methods for reading and writing private and public keys 38 | * to files. 39 | * 40 | * @author Nicholas Williamo 41 | * @version 1.0.0 42 | * @since 1.0.0 43 | */ 44 | public class KeyFileUtilities { 45 | public static final String KEY_ALGORITHM = "RSA"; 46 | 47 | protected static void writeEncryptedPrivateKey(final PrivateKey privateKey,final File file, char[] passPhrase) 48 | throws IOException { 49 | 50 | FileUtils.writeByteArrayToFile(file, KeyFileUtilities.writeEncryptedPrivateKey(privateKey, passPhrase)); 51 | } 52 | 53 | protected static void writeEncryptedPublicKey(final PublicKey publicKey,final File file,final char[] passPhrase) 54 | throws IOException { 55 | FileUtils.writeByteArrayToFile(file, KeyFileUtilities.writeEncryptedPublicKey(publicKey, passPhrase)); 56 | } 57 | 58 | protected static PrivateKey readEncryptedPrivateKey(final File file,final char[] passPhrase) throws IOException { 59 | return KeyFileUtilities.readEncryptedPrivateKey(FileUtils.readFileToByteArray(file), passPhrase); 60 | } 61 | 62 | protected static PublicKey readEncryptedPublicKey(final File file,final char[] passPhrase) throws IOException { 63 | return KeyFileUtilities.readEncryptedPublicKey(FileUtils.readFileToByteArray(file), passPhrase); 64 | } 65 | 66 | protected static byte[] writeEncryptedPrivateKey(final PrivateKey privateKey,final char[] passPhrase) { 67 | final PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(privateKey.getEncoded()); 68 | return Encryptor.encryptRaw(pkcs8EncodedKeySpec.getEncoded(), passPhrase); 69 | } 70 | 71 | protected static byte[] writeEncryptedPublicKey(final PublicKey publicKey,final char[] passPhrase) { 72 | final X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(publicKey.getEncoded()); 73 | return Encryptor.encryptRaw(x509EncodedKeySpec.getEncoded(), passPhrase); 74 | } 75 | 76 | public static PrivateKey readEncryptedPrivateKey(final byte[] fileContents,final char[] passPhrase) { 77 | final PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(Encryptor.decryptRaw(fileContents, passPhrase)); 78 | 79 | try { 80 | return KeyFactory.getInstance(KEY_ALGORITHM).generatePrivate(privateKeySpec); 81 | } catch (final NoSuchAlgorithmException e) { 82 | throw new AlgorithmNotSupportedException(KEY_ALGORITHM, e); 83 | } catch (final InvalidKeySpecException e) { 84 | throw new InappropriateKeySpecificationException(e); 85 | } 86 | } 87 | 88 | public static PublicKey readEncryptedPublicKey(final byte[] fileContents,final char[] passPhrase) { 89 | final X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(Encryptor.decryptRaw(fileContents, passPhrase)); 90 | 91 | try { 92 | return KeyFactory.getInstance(KEY_ALGORITHM).generatePublic(publicKeySpec); 93 | } catch (final NoSuchAlgorithmException e) { 94 | throw new AlgorithmNotSupportedException(KEY_ALGORITHM, e); 95 | } catch (final InvalidKeySpecException e) { 96 | throw new InappropriateKeySpecificationException(e); 97 | } 98 | } 99 | 100 | private KeyFileUtilities() { 101 | throw new RuntimeException("This class cannot be instantiated."); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /core/src/main/java/ro/esolutions/licensing/encryption/PasswordProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * PasswordProvider.java from LicenseManager modified Tuesday, April 9, 2013 10:55:17 CDT (-0500). 3 | * 4 | * Copyright 2010-2013 the original author or authors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package ro.esolutions.licensing.encryption; 20 | 21 | /** 22 | * This specifies an interface for providing a password for decrypting a key or license. Every user of this library 23 | * must implement this (one to three times). See the documentation for 24 | * {@code ro.esolutions.licensing.encryption.RSAKeyPairGenerator}, 25 | * {@code ro.esolutions.licensing.licensor.LicenseCreator} and 26 | * {@link ro.esolutions.licensing.LicenseManager} for more information. 27 | * 28 | * @author Nick Williams 29 | * @version 1.5.0 30 | * @since 1.0.0 31 | */ 32 | public interface PasswordProvider { 33 | /** 34 | * When integrating the license manager in your application, you must 35 | * implement this interface. It should return the password that you used 36 | * when encrypting the key or license.
37 | *
38 | * Also, do not implement this using any strings, i.e:
39 | *
40 | * 41 | * return "myInsecurePassword".toCharArray(); 42 | *
43 | *
44 | * Strings persist in memory for a long time, and the string's contents 45 | * cannot be overwritten. Using only a character array allows the license 46 | * manager to overwrite the password when it is finished with it, thus 47 | * keeping it in the memory pool for as short a time as possible.
48 | *
49 | * Finally, the password must be between six and 32 characters 50 | * (inclusively). We recommend it be at least 10 characters.
51 | *
52 | * It is imperative that you obfuscate the bytecode for the 53 | * implementation of this class. It is also imperative that the character 54 | * array exist only for the life of this method (i.e., DO NOT store it as 55 | * an instance or class field). 56 | * 57 | * @return the password in character array form. 58 | */ 59 | char[] getPassword(); 60 | } 61 | -------------------------------------------------------------------------------- /core/src/main/java/ro/esolutions/licensing/encryption/PublicKeyDataProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * PublicKeyDataProvider.java from LicenseManager modified Tuesday, February 21, 2012 10:59:35 CST (-0600). 3 | * 4 | * Copyright 2010-2013 the original author or authors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package ro.esolutions.licensing.encryption; 20 | 21 | import ro.esolutions.licensing.exception.KeyNotFoundException; 22 | 23 | /** 24 | * Specifies an interface for retrieving the public key file data. This 25 | * interface only needs to be implemented in the application using the 26 | * licenses. It need not be implemented in the application generating the 27 | * licenses. 28 | * 29 | * @author Nick Williams 30 | * @version 1.0.0 31 | * @since 1.0.0 32 | */ 33 | public interface PublicKeyDataProvider { 34 | /** 35 | * This method returns the data from the file containing the encrypted 36 | * public key from the public/private key pair. The contract for this 37 | * method can be fulfilled by storing the data in a byte array literal 38 | * in the source code itself.
39 | *
40 | * It is imperative that you obfuscate the bytecode for the 41 | * implementation of this class. It is also imperative that the byte 42 | * array exist only for the life of this method (i.e., DO NOT store it as 43 | * an instance or class field). 44 | * 45 | * @return the encrypted file contents from the public key file. 46 | * @throws KeyNotFoundException if the key data could not be retrieved; an acceptable message or chained cause must 47 | * be provided. 48 | */ 49 | byte[] getEncryptedPublicKeyData() throws KeyNotFoundException; 50 | } 51 | -------------------------------------------------------------------------------- /core/src/main/java/ro/esolutions/licensing/exception/AlgorithmNotSupportedException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * AlgorithmNotSupportedException.java from LicenseManager modified Friday, September 21, 2012 07:46:54 CDT (-0500). 3 | * 4 | * Copyright 2010-2013 the original author or authors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package ro.esolutions.licensing.exception; 20 | 21 | /** 22 | * This exception is thrown when the specified algorithm is not supported by 23 | * this JVM. 24 | * 25 | * @author Nick Williams 26 | * @version 1.0.0 27 | * @since 1.0.0 28 | */ 29 | @SuppressWarnings("unused") 30 | public class AlgorithmNotSupportedException extends RuntimeException { 31 | 32 | public AlgorithmNotSupportedException() { 33 | super("The specified algorithm is not supported on this system."); 34 | } 35 | 36 | public AlgorithmNotSupportedException(final String algorithm) { 37 | super("The algorithm \"" + algorithm + "\" is not supported on this system."); 38 | } 39 | 40 | public AlgorithmNotSupportedException(final Throwable cause) { 41 | super(cause); 42 | } 43 | 44 | public AlgorithmNotSupportedException(final String algorithm,final Throwable cause) { 45 | super("The algorithm \"" + algorithm + "\" is not supported on this system.", cause); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /core/src/main/java/ro/esolutions/licensing/exception/CorruptSignatureException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * CorruptSignatureException.java from LicenseManager modified Friday, September 21, 2012 07:46:54 CDT (-0500). 3 | * 4 | * Copyright 2010-2013 the original author or authors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package ro.esolutions.licensing.exception; 20 | 21 | /** 22 | * This is thrown when a corrupt signature ar non-signature is provided. 23 | * 24 | * @author Nick Williams 25 | * @version 1.0.0 26 | * @since 1.0.0 27 | */ 28 | @SuppressWarnings("unused") 29 | public class CorruptSignatureException extends RuntimeException { 30 | 31 | public CorruptSignatureException() { 32 | super("The signature provided is corrupt or not a signature."); 33 | } 34 | 35 | public CorruptSignatureException(final String message) { 36 | super(message); 37 | } 38 | 39 | public CorruptSignatureException(final Throwable cause) { 40 | super("The signature provided is corrupt or not a signature.", cause); 41 | } 42 | 43 | public CorruptSignatureException(final String message,final Throwable cause) { 44 | super(message, cause); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /core/src/main/java/ro/esolutions/licensing/exception/ExpiredLicenseException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ExpiredLicenseException.java from LicenseManager modified Friday, September 21, 2012 07:46:54 CDT (-0500). 3 | * 4 | * Copyright 2010-2013 the original author or authors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package ro.esolutions.licensing.exception; 20 | 21 | /** 22 | * This exception is thrown whenever a license is validated that has expired. 23 | * 24 | * @author Nick Williams 25 | * @version 1.0.0 26 | * @see ro.esolutions.licensing.LicenseValidator 27 | * @since 1.0.0 28 | */ 29 | @SuppressWarnings("unused") 30 | public class ExpiredLicenseException extends InvalidLicenseException { 31 | 32 | private static final String THE_LICENSE_HAS_EXPIRED = "The license has expired."; 33 | 34 | public ExpiredLicenseException() { 35 | super(THE_LICENSE_HAS_EXPIRED); 36 | } 37 | 38 | public ExpiredLicenseException(final String message) { 39 | super(message); 40 | } 41 | 42 | public ExpiredLicenseException(final Throwable cause) { 43 | super(THE_LICENSE_HAS_EXPIRED, cause); 44 | } 45 | 46 | public ExpiredLicenseException(final String message,final Throwable cause) { 47 | super(message, cause); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /core/src/main/java/ro/esolutions/licensing/exception/FailedToDecryptException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * FailedToDecryptException.java from LicenseManager modified Friday, September 21, 2012 07:46:54 CDT (-0500). 3 | * 4 | * Copyright 2010-2013 the original author or authors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package ro.esolutions.licensing.exception; 20 | 21 | /** 22 | * This exception is thrown when the data was corrupt and undecryptable or when 23 | * the provided decryption password was incorrect. It is impossible to know 24 | * which is the actual cause. 25 | * 26 | * @author Nick Williams 27 | * @version 1.0.0 28 | * @since 1.0.0 29 | */ 30 | @SuppressWarnings("unused") 31 | public class FailedToDecryptException extends RuntimeException { 32 | 33 | private static final String MESSAGE = "Failed to decrypt the data. Either the password was incorrect or the data was corrupt."; 34 | 35 | public FailedToDecryptException() { 36 | super(MESSAGE); 37 | } 38 | 39 | public FailedToDecryptException(final String message) { 40 | super(message); 41 | } 42 | 43 | public FailedToDecryptException(final Throwable cause) { 44 | super(MESSAGE, cause); 45 | } 46 | 47 | public FailedToDecryptException(final String message, final Throwable cause) { 48 | super(message, cause); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /core/src/main/java/ro/esolutions/licensing/exception/InappropriateKeyException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * InappropriateKeyException.java from LicenseManager modified Friday, September 21, 2012 07:46:54 CDT (-0500). 3 | * 4 | * Copyright 2010-2013 the original author or authors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package ro.esolutions.licensing.exception; 20 | 21 | /** 22 | * This exception is thrown when the specified key is inappropriate for the 23 | * given cipher. 24 | * 25 | * @author Nick Williams 26 | * @version 1.0.0 27 | * @since 1.0.0 28 | */ 29 | @SuppressWarnings("unused") 30 | public class InappropriateKeyException extends RuntimeException { 31 | 32 | private static final String MESSAGE = "The specified key is inappropriate for the cipher."; 33 | 34 | public InappropriateKeyException() { 35 | super(MESSAGE); 36 | } 37 | 38 | public InappropriateKeyException(final String message) { 39 | super(message); 40 | } 41 | 42 | public InappropriateKeyException(final Throwable cause) { 43 | super(MESSAGE, cause); 44 | } 45 | 46 | public InappropriateKeyException(final String message, final Throwable cause) { 47 | super(message, cause); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /core/src/main/java/ro/esolutions/licensing/exception/InappropriateKeySpecificationException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * InappropriateKeySpecificationException.java from LicenseManager modified Friday, September 21, 2012 07:46:54 CDT (-0500). 3 | * 4 | * Copyright 2010-2013 the original author or authors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package ro.esolutions.licensing.exception; 20 | 21 | /** 22 | * This exception is thrown when an inappropriate key specification is provided 23 | * for the key factory. 24 | * 25 | * @author Nick Williams 26 | * @version 1.0.0 27 | * @since 1.0.0 28 | */ 29 | @SuppressWarnings("unused") 30 | public class InappropriateKeySpecificationException extends RuntimeException { 31 | private static final String MESSAGE = "The specified key specification is inappropriate for the factory."; 32 | 33 | public InappropriateKeySpecificationException() { 34 | super(MESSAGE); 35 | } 36 | 37 | public InappropriateKeySpecificationException(final String message) { 38 | super(message); 39 | } 40 | 41 | public InappropriateKeySpecificationException(final Throwable cause) { 42 | super(MESSAGE, cause); 43 | } 44 | 45 | public InappropriateKeySpecificationException(final String message,final Throwable cause) { 46 | super(message, cause); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /core/src/main/java/ro/esolutions/licensing/exception/InsecureEnvironmentError.java: -------------------------------------------------------------------------------- 1 | /* 2 | * InsecureEnvironmentException.java from LicenseManager modified Friday, September 21, 2012 07:46:54 CDT (-0500). 3 | * 4 | * Copyright 2010-2013 the original author or authors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package ro.esolutions.licensing.exception; 20 | 21 | /** 22 | * Thrown when a security manager is already installed that allows reflection access but doesn't allow a more secure 23 | * security manager to be installed. 24 | * 25 | * @author Nick Williams 26 | * @version 1.0.0 27 | * @since 1.0.0 28 | */ 29 | @SuppressWarnings("unused") 30 | public class InsecureEnvironmentError extends Error { 31 | 32 | public InsecureEnvironmentError(final String message, final Throwable cause) { 33 | super("The license manager was activated in an insecure environment. " + message, cause); 34 | } 35 | 36 | public InsecureEnvironmentError(final SecurityException cause) { 37 | super("The license manager was activated in an insecure environment. A security manager has already been " + 38 | "installed, but it allows reflection access to the license cache and doesn't allow a new security " + 39 | "manager to be installed.", cause); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /core/src/main/java/ro/esolutions/licensing/exception/InvalidLicenseException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * InvalidLicenseException.java from LicenseManager modified Friday, September 21, 2012 07:46:54 CDT (-0500). 3 | * 4 | * Copyright 2010-2013 the original author or authors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package ro.esolutions.licensing.exception; 20 | 21 | /** 22 | * This exception is thrown whenever a license is validated that isn't valid for some reason. 23 | * 24 | * @author Nick Williams 25 | * @version 1.0.0 26 | * @see ro.esolutions.licensing.LicenseValidator 27 | * @since 1.0.0 28 | */ 29 | @SuppressWarnings("unused") 30 | public class InvalidLicenseException extends RuntimeException { 31 | 32 | private static final String MESSAGE = "The license is not valid."; 33 | 34 | public InvalidLicenseException() { 35 | super(MESSAGE); 36 | } 37 | 38 | public InvalidLicenseException(final String message) { 39 | super(message); 40 | } 41 | 42 | public InvalidLicenseException(final Throwable cause) { 43 | super(MESSAGE, cause); 44 | } 45 | 46 | public InvalidLicenseException(final String message, final Throwable cause) { 47 | super(message, cause); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /core/src/main/java/ro/esolutions/licensing/exception/InvalidSignatureException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * InvalidSignatureException.java from LicenseManager modified Friday, September 21, 2012 07:46:54 CDT (-0500). 3 | * 4 | * Copyright 2010-2013 the original author or authors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package ro.esolutions.licensing.exception; 20 | 21 | /** 22 | * This is thrown when a signature is invalid and cannot be verified. 23 | * 24 | * @author Nick Williams 25 | * @version 1.0.0 26 | * @since 1.0.0 27 | */ 28 | @SuppressWarnings("unused") 29 | public class InvalidSignatureException extends RuntimeException { 30 | 31 | private static final String MESSAGE = "The signature provided is invalid and cannot be verified."; 32 | 33 | public InvalidSignatureException() { 34 | super(MESSAGE); 35 | } 36 | 37 | public InvalidSignatureException(final String message) { 38 | super(message); 39 | } 40 | 41 | public InvalidSignatureException(final Throwable cause) { 42 | super(MESSAGE, cause); 43 | } 44 | 45 | public InvalidSignatureException(final String message, final Throwable cause) { 46 | super(message, cause); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /core/src/main/java/ro/esolutions/licensing/exception/KeyNotFoundException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * KeyNotFoundException.java from LicenseManager modified Friday, September 21, 2012 07:46:54 CDT (-0500). 3 | * 4 | * Copyright 2010-2013 the original author or authors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package ro.esolutions.licensing.exception; 20 | 21 | /** 22 | * This is thrown when the key (file) could not be found. 23 | * 24 | * @author Nick Williams 25 | * @version 1.0.0 26 | * @since 1.0.0 27 | */ 28 | @SuppressWarnings("unused") 29 | public class KeyNotFoundException extends RuntimeException { 30 | 31 | private static final String MESSAGE = "The key file could not be found."; 32 | 33 | public KeyNotFoundException() { 34 | super(MESSAGE); 35 | } 36 | 37 | public KeyNotFoundException(final String message) { 38 | super(message); 39 | } 40 | 41 | public KeyNotFoundException(final Throwable cause) { 42 | super(MESSAGE, cause); 43 | } 44 | 45 | public KeyNotFoundException(final String message, final Throwable cause) { 46 | super(message, cause); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /core/src/main/java/ro/esolutions/licensing/exception/ObjectDeserializationException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ObjectDeserializationException.java from LicenseManager modified Friday, September 21, 2012 07:46:54 CDT (-0500). 3 | * 4 | * Copyright 2010-2013 the original author or authors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package ro.esolutions.licensing.exception; 20 | 21 | /** 22 | * This class is thrown when an error occurs while reading an object from a 23 | * byte array. The general form of this error indicates an I/O problem. 24 | * Sub-classes of this exception indicate more specific problems. 25 | * 26 | * @author Nick Williams 27 | * @version 1.0.0 28 | * @since 1.0.0 29 | */ 30 | @SuppressWarnings("unused") 31 | public class ObjectDeserializationException extends RuntimeException { 32 | 33 | private static final String MESSAGE = "An error occurred while reading the object from the byte array."; 34 | 35 | public ObjectDeserializationException() { 36 | super(MESSAGE); 37 | } 38 | 39 | public ObjectDeserializationException(final String message) { 40 | super(message); 41 | } 42 | 43 | public ObjectDeserializationException(final Throwable cause) { 44 | super(MESSAGE, cause); 45 | } 46 | 47 | public ObjectDeserializationException(final String message, final Throwable cause) { 48 | super(message, cause); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /core/src/main/java/ro/esolutions/licensing/exception/ObjectSerializationException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ObjectSerializationException.java from LicenseManager modified Friday, September 21, 2012 07:46:54 CDT (-0500). 3 | * 4 | * Copyright 2010-2013 the original author or authors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package ro.esolutions.licensing.exception; 20 | 21 | /** 22 | * This class is thrown when an I/O error occurs while writing an object to a 23 | * byte array. 24 | * 25 | * @author Nick Williams 26 | * @version 1.0.0 27 | * @since 1.0.0 28 | */ 29 | @SuppressWarnings("unused") 30 | public class ObjectSerializationException extends RuntimeException { 31 | 32 | private static final String MESSAGE = "An I/O error occurred while writing the object to the byte array."; 33 | 34 | public ObjectSerializationException() { 35 | super(MESSAGE); 36 | } 37 | 38 | public ObjectSerializationException(final String message) { 39 | super(message); 40 | } 41 | 42 | public ObjectSerializationException(final Throwable cause) { 43 | super(MESSAGE, cause); 44 | } 45 | 46 | public ObjectSerializationException(final String message, final Throwable cause) { 47 | super(message, cause); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /core/src/main/java/ro/esolutions/licensing/exception/ObjectTypeNotExpectedException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ObjectTypeNotExpectedException.java from LicenseManager modified Friday, September 21, 2012 07:46:54 CDT (-0500). 3 | * 4 | * Copyright 2010-2013 the original author or authors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package ro.esolutions.licensing.exception; 20 | 21 | /** 22 | * This class class is thrown when an object is deserialized that does not match the type that was expected. 23 | * 24 | * @author Nick Williams 25 | * @version 1.0.0 26 | * @since 1.0.0 27 | */ 28 | @SuppressWarnings("unused") 29 | public class ObjectTypeNotExpectedException extends ObjectDeserializationException { 30 | 31 | private static final String MESSAGE = "The type of object read did not match the type expected."; 32 | 33 | public ObjectTypeNotExpectedException() { 34 | super(MESSAGE); 35 | } 36 | 37 | public ObjectTypeNotExpectedException(final String message) { 38 | super(message); 39 | } 40 | 41 | public ObjectTypeNotExpectedException(final String expectedType,final String encounteredType) { 42 | super("While deserializing an object of expected type \"" + expectedType + "\", got an object of type \"" + 43 | encounteredType + "\" instead."); 44 | } 45 | 46 | public ObjectTypeNotExpectedException(final Throwable cause) { 47 | super("The type of object read did not match the type expected.", cause); 48 | } 49 | 50 | public ObjectTypeNotExpectedException(final String message,final Throwable cause) { 51 | super(message, cause); 52 | } 53 | 54 | public ObjectTypeNotExpectedException(final String expectedType,final String encounteredType,final Throwable cause) { 55 | super("While deserializing an object of expected type \"" + expectedType + "\", got an object of type \"" + 56 | encounteredType + "\" instead.", cause); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /core/src/main/java/ro/esolutions/licensing/immutable/Immutable.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Immutable.java from LicenseManager modified Tuesday, February 21, 2012 10:59:35 CST (-0600). 3 | * 4 | * Copyright 2010-2013 the original author or authors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package ro.esolutions.licensing.immutable; 20 | 21 | /** 22 | * This interface denotes a collection or other object as being unmodifiable. 23 | * 24 | * @author Nick Williams 25 | * @version 1.0.0 26 | * @since 1.0.0 27 | */ 28 | public interface Immutable { 29 | 30 | } 31 | -------------------------------------------------------------------------------- /core/src/main/java/ro/esolutions/licensing/immutable/ImmutableAbstractCollection.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ImmutableAbstractCollection.java from LicenseManager modified Tuesday, February 21, 2012 10:59:34 CST (-0600). 3 | * 4 | * Copyright 2010-2013 the original author or authors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package ro.esolutions.licensing.immutable; 20 | 21 | import java.io.Serializable; 22 | import java.util.Collection; 23 | 24 | /** 25 | * Wraps a collection such that it cannot be modified. There is some overhead 26 | * associated with this due to verification of hash codes on every call to 27 | * prevent tampering with via reflection, but this is well worth it if your goal 28 | * is security and you truly need an unmodifiable collection. 29 | * 30 | * @author Nick Williams 31 | * @version 1.0.0 32 | * @since 1.0.0 33 | */ 34 | public abstract class ImmutableAbstractCollection extends ValidObject 35 | implements Immutable, Collection, Serializable { 36 | private final static long serialVersionUID = -4187794066638697055L; 37 | 38 | final Collection internalCollection; 39 | 40 | private final int internalSize; 41 | 42 | private final int internalHashCode; 43 | 44 | /** 45 | * Constructor that wraps (not copies). 46 | * 47 | * @param collection The collection to decorate, must not be null 48 | * @throws IllegalArgumentException if collection is null 49 | */ 50 | protected ImmutableAbstractCollection(final Collection collection) { 51 | if (collection == null) { 52 | throw new IllegalArgumentException("Parameter collection must not be null."); 53 | } 54 | this.internalCollection = collection; 55 | this.internalSize = this.internalCollection.size(); 56 | this.internalHashCode = this.internalCollection.hashCode(); 57 | } 58 | 59 | /** 60 | * Checks the validity of this object, and throws an 61 | * {@link ImmutableModifiedThroughReflectionException} if that check fails. 62 | * 63 | * @throws ImmutableModifiedThroughReflectionException if the validity check fails. 64 | */ 65 | @Override 66 | protected final void checkValidity() { 67 | if (this.internalSize != this.internalCollection.size() || 68 | this.internalHashCode != this.internalCollection.hashCode()) 69 | throw new ImmutableModifiedThroughReflectionException(); 70 | } 71 | 72 | @Override 73 | public final boolean equals(final Object o) { 74 | synchronized (this.internalCollection) { 75 | this.checkValidity(); 76 | return o == this || ( 77 | o instanceof ImmutableAbstractCollection && 78 | this.internalCollection.equals( 79 | ((ImmutableAbstractCollection) o).internalCollection 80 | ) 81 | ); 82 | } 83 | } 84 | 85 | @Override 86 | public final int hashCode() { 87 | synchronized (this.internalCollection) { 88 | this.checkValidity(); 89 | return this.internalHashCode; 90 | } 91 | } 92 | 93 | @Override 94 | @SuppressWarnings("unchecked") 95 | public final boolean contains(final Object object) { 96 | synchronized (this.internalCollection) { 97 | this.checkValidity(); 98 | try { 99 | return this.internalCollection.contains(object); 100 | } catch (final ClassCastException e) { 101 | return false; 102 | } 103 | } 104 | } 105 | 106 | @Override 107 | public final boolean containsAll(final Collection c) { 108 | synchronized (this.internalCollection) { 109 | this.checkValidity(); 110 | return this.internalCollection.containsAll(c); 111 | } 112 | } 113 | 114 | @Override 115 | public final boolean isEmpty() { 116 | synchronized (this.internalCollection) { 117 | this.checkValidity(); 118 | return this.internalCollection.isEmpty(); 119 | } 120 | } 121 | 122 | @Override 123 | public final ImmutableIterator iterator() { 124 | synchronized (this.internalCollection) { 125 | this.checkValidity(); 126 | return new ImmutableIterator<>(this.internalCollection.iterator(), this); 127 | } 128 | } 129 | 130 | @Override 131 | public final int size() { 132 | synchronized (this.internalCollection) { 133 | this.checkValidity(); 134 | return this.internalCollection.size(); 135 | } 136 | } 137 | 138 | @Override 139 | public final Object[] toArray() { 140 | synchronized (this.internalCollection) { 141 | this.checkValidity(); 142 | return this.internalCollection.toArray(); 143 | } 144 | } 145 | 146 | @Override 147 | @SuppressWarnings("SuspiciousToArrayCall") 148 | public final T[] toArray(final T[] prototype) { 149 | synchronized (this.internalCollection) { 150 | this.checkValidity(); 151 | return this.internalCollection.toArray(prototype); 152 | } 153 | } 154 | 155 | @Override 156 | public final String toString() { 157 | return this.internalCollection.toString(); 158 | } 159 | 160 | @Override 161 | public final boolean add(final E e) { 162 | throw new UnsupportedOperationException("This collection cannot be modified."); 163 | } 164 | 165 | @Override 166 | public final boolean addAll(final Collection c) { 167 | throw new UnsupportedOperationException("This collection cannot be modified."); 168 | } 169 | 170 | @Override 171 | public final void clear() { 172 | throw new UnsupportedOperationException("This collection cannot be modified."); 173 | } 174 | 175 | @Override 176 | public final boolean remove(final Object o) { 177 | throw new UnsupportedOperationException("This collection cannot be modified."); 178 | } 179 | 180 | @Override 181 | public final boolean removeAll(final Collection c) { 182 | throw new UnsupportedOperationException("This collection cannot be modified."); 183 | } 184 | 185 | @Override 186 | public final boolean retainAll(final Collection c) { 187 | throw new UnsupportedOperationException("This collection cannot be modified."); 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /core/src/main/java/ro/esolutions/licensing/immutable/ImmutableArrayList.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ImmutableArrayList.java from LicenseManager modified Thursday, January 24, 2013 23:33:43 CST (-0600). 3 | * 4 | * Copyright 2010-2013 the original author or authors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package ro.esolutions.licensing.immutable; 20 | 21 | import java.io.Serializable; 22 | import java.util.ArrayList; 23 | import java.util.Collection; 24 | import java.util.List; 25 | 26 | /** 27 | * Wraps a list such that it cannot be modified. There is some overhead 28 | * associated with this due to verification of hash codes on every call to 29 | * prevent tampering with via reflection, but this is well worth it if your goal 30 | * is security and you truly need an unmodifiable list. 31 | * 32 | * @author Nick Williams 33 | * @version 1.0.0 34 | * @since 1.0.0 35 | */ 36 | public final class ImmutableArrayList extends ImmutableAbstractCollection implements List, Serializable, Cloneable { 37 | private final static long serialVersionUID = -6912407141647481417L; 38 | 39 | private final ArrayList internalList; 40 | 41 | /** 42 | * Constructor that copies. 43 | * 44 | * @param list the list to decorate, must not be null 45 | * @throws IllegalArgumentException if list is null 46 | */ 47 | public ImmutableArrayList(final List list) { 48 | super(new ArrayList<>(list)); 49 | 50 | this.internalList = (ArrayList) this.internalCollection; 51 | this.internalList.trimToSize(); 52 | } 53 | 54 | @Override 55 | @SuppressWarnings({"unchecked", "CloneDoesntCallSuperClone"}) 56 | public final ImmutableArrayList clone() { 57 | synchronized (this.internalList) { 58 | this.checkValidity(); 59 | return new ImmutableArrayList((List) this.internalList.clone()); 60 | } 61 | } 62 | 63 | @Override 64 | public final E get(final int index) { 65 | synchronized (this.internalList) { 66 | this.checkValidity(); 67 | return this.internalList.get(index); 68 | } 69 | } 70 | 71 | @Override 72 | public final int indexOf(final Object o) { 73 | synchronized (this.internalList) { 74 | this.checkValidity(); 75 | return this.internalList.indexOf(o); 76 | } 77 | } 78 | 79 | @Override 80 | public final int lastIndexOf(final Object o) { 81 | synchronized (this.internalList) { 82 | this.checkValidity(); 83 | return this.internalList.lastIndexOf(o); 84 | } 85 | } 86 | 87 | @Override 88 | @SuppressWarnings("unchecked") 89 | public final ImmutableListIterator listIterator() { 90 | synchronized (this.internalList) { 91 | this.checkValidity(); 92 | return new ImmutableListIterator<>(this.internalList.listIterator(), this); 93 | } 94 | } 95 | 96 | @Override 97 | @SuppressWarnings("unchecked") 98 | public final ImmutableListIterator listIterator(final int index) { 99 | synchronized (this.internalList) { 100 | this.checkValidity(); 101 | return new ImmutableListIterator<>(this.internalList.listIterator(index), this); 102 | } 103 | } 104 | 105 | @Override 106 | public final ImmutableArrayList subList(final int fromIndex,final int toIndex) { 107 | synchronized (this.internalList) { 108 | this.checkValidity(); 109 | final List subList = this.internalList.subList(fromIndex, toIndex); 110 | return new ImmutableArrayList<>(subList); 111 | } 112 | } 113 | 114 | @Override 115 | public final void add(final int index,final E e) { 116 | throw new UnsupportedOperationException("This list cannot be modified."); 117 | } 118 | 119 | @Override 120 | public final boolean addAll(final int index,final Collection c) { 121 | throw new UnsupportedOperationException("This list cannot be modified."); 122 | } 123 | 124 | @Override 125 | public final E remove(final int index) { 126 | throw new UnsupportedOperationException("This list cannot be modified."); 127 | } 128 | 129 | @Override 130 | public final E set(final int index,final E e) { 131 | throw new UnsupportedOperationException("This list cannot be modified."); 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /core/src/main/java/ro/esolutions/licensing/immutable/ImmutableIterator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ImmutableIterator.java from LicenseManager modified Tuesday, February 21, 2012 10:59:35 CST (-0600). 3 | * 4 | * Copyright 2010-2013 the original author or authors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package ro.esolutions.licensing.immutable; 20 | 21 | import java.util.Iterator; 22 | 23 | /** 24 | * Wraps an iterator such that it cannot be modified. 25 | * 26 | * @author Nick Williams 27 | * @version 1.0.0 28 | * @since 1.0.0 29 | */ 30 | public final class ImmutableIterator implements Immutable, Iterator { 31 | private final Iterator internal; 32 | 33 | private final ValidObject validObject; 34 | 35 | ImmutableIterator(final Iterator iterator,final ValidObject validObject) { 36 | this.internal = iterator; 37 | this.validObject = validObject; 38 | } 39 | 40 | @Override 41 | public boolean hasNext() { 42 | synchronized (this.validObject) { 43 | this.validObject.checkValidity(); 44 | return this.internal.hasNext(); 45 | } 46 | } 47 | 48 | @Override 49 | public E next() { 50 | synchronized (this.validObject) { 51 | this.validObject.checkValidity(); 52 | return this.internal.next(); 53 | } 54 | } 55 | 56 | @Override 57 | public void remove() { 58 | throw new UnsupportedOperationException("This iterator cannot be modified."); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /core/src/main/java/ro/esolutions/licensing/immutable/ImmutableLinkedHashSet.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ImmutableLinkedHashSet.java from LicenseManager modified Tuesday, February 21, 2012 10:59:34 CST (-0600). 3 | * 4 | * Copyright 2010-2013 the original author or authors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package ro.esolutions.licensing.immutable; 20 | 21 | import java.io.Serializable; 22 | import java.util.ArrayList; 23 | import java.util.LinkedHashSet; 24 | import java.util.Set; 25 | 26 | /** 27 | * Wraps a set such that it cannot be modified. There is some overhead 28 | * associated with this due to verification of hash codes on every call to 29 | * prevent tampering with via reflection, but this is well worth it if your goal 30 | * is security and you truly need an unmodifiable set. 31 | * 32 | * @author Nick Williams 33 | * @version 1.5.0 34 | * @since 1.0.0 35 | */ 36 | public final class ImmutableLinkedHashSet extends ImmutableAbstractCollection 37 | implements Set, Serializable, Cloneable { 38 | private final static long serialVersionUID = 2284350955829958161L; 39 | 40 | private final LinkedHashSet internalSet; 41 | 42 | private final ArrayList internalList; 43 | 44 | /** 45 | * Constructor that copies. 46 | * 47 | * @param list the set to decorate, must not be null 48 | * @throws IllegalArgumentException if list is null 49 | */ 50 | public ImmutableLinkedHashSet(final Set list) { 51 | super(new LinkedHashSet<>(list)); 52 | 53 | this.internalSet = (LinkedHashSet) this.internalCollection; 54 | this.internalList = new ArrayList<>(list); 55 | } 56 | 57 | @Override 58 | @SuppressWarnings({"unchecked", "CloneDoesntCallSuperClone"}) 59 | public final ImmutableLinkedHashSet clone() { 60 | synchronized (this.internalSet) { 61 | this.checkValidity(); 62 | return new ImmutableLinkedHashSet<>((Set) this.internalSet.clone()); 63 | } 64 | } 65 | 66 | /** 67 | * Retrieves the indexed element specified. 68 | * 69 | * @param index The element to retrieve. 70 | * @return The element requested. 71 | */ 72 | public E get(final int index) { 73 | return index < 0 ? null : this.internalList.get(index); 74 | } 75 | 76 | /** 77 | * Retrieves the matching element specified. 78 | * 79 | * @param object The element to match. 80 | * @return The element requested. 81 | */ 82 | public E get(final E object) { 83 | return this.get(this.internalList.indexOf(object)); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /core/src/main/java/ro/esolutions/licensing/immutable/ImmutableListIterator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ImmutableListIterator.java from LicenseManager modified Tuesday, February 21, 2012 10:59:35 CST (-0600). 3 | * 4 | * Copyright 2010-2013 the original author or authors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package ro.esolutions.licensing.immutable; 20 | 21 | import java.util.ListIterator; 22 | 23 | /** 24 | * Wraps a list iterator such that it cannot be modified. 25 | * 26 | * @author Nick Williams 27 | * @version 1.0.0 28 | * @since x.x.x 29 | */ 30 | public final class ImmutableListIterator implements Immutable, ListIterator { 31 | private final ListIterator internal; 32 | 33 | private final ValidObject validObject; 34 | 35 | ImmutableListIterator(final ListIterator iterator,final ValidObject validObject) { 36 | this.internal = iterator; 37 | this.validObject = validObject; 38 | } 39 | 40 | @Override 41 | public boolean hasNext() { 42 | synchronized (this.validObject) { 43 | this.validObject.checkValidity(); 44 | return this.internal.hasNext(); 45 | } 46 | } 47 | 48 | @Override 49 | public boolean hasPrevious() { 50 | synchronized (this.validObject) { 51 | this.validObject.checkValidity(); 52 | return this.internal.hasPrevious(); 53 | } 54 | } 55 | 56 | @Override 57 | public E next() { 58 | synchronized (this.validObject) { 59 | this.validObject.checkValidity(); 60 | return this.internal.next(); 61 | } 62 | } 63 | 64 | @Override 65 | public int nextIndex() { 66 | synchronized (this.validObject) { 67 | this.validObject.checkValidity(); 68 | return this.internal.nextIndex(); 69 | } 70 | } 71 | 72 | @Override 73 | public E previous() { 74 | synchronized (this.validObject) { 75 | this.validObject.checkValidity(); 76 | return this.internal.previous(); 77 | } 78 | } 79 | 80 | @Override 81 | public int previousIndex() { 82 | synchronized (this.validObject) { 83 | this.validObject.checkValidity(); 84 | return this.internal.previousIndex(); 85 | } 86 | } 87 | 88 | @Override 89 | public void add(final E e) { 90 | throw new UnsupportedOperationException("This iterator cannot be modified."); 91 | } 92 | 93 | @Override 94 | public void remove() { 95 | throw new UnsupportedOperationException("This iterator cannot be modified."); 96 | } 97 | 98 | @Override 99 | public void set(final E e) { 100 | throw new UnsupportedOperationException("This iterator cannot be modified."); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /core/src/main/java/ro/esolutions/licensing/immutable/ImmutableModifiedThroughReflectionException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ImmutableModifiedThroughReflectionException.java from LicenseManager modified Friday, September 21, 2012 07:46:54 CDT (-0500). 3 | * 4 | * Copyright 2010-2013 the original author or authors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package ro.esolutions.licensing.immutable; 20 | 21 | /** 22 | * This exception is thrown when an unmodifiable list is modified illegally 23 | * through exception. 24 | * 25 | * @author Nick Williams 26 | * @version 1.0.0 27 | * @since 1.0.0 28 | */ 29 | @SuppressWarnings("unused") 30 | public class ImmutableModifiedThroughReflectionException extends Error { 31 | private static final long serialVersionUID = 1L; 32 | private static final String MESSAGE = "This immutable object appears to have been modified through reflection."; 33 | 34 | public ImmutableModifiedThroughReflectionException() { 35 | super(MESSAGE); 36 | } 37 | 38 | public ImmutableModifiedThroughReflectionException(final String message) { 39 | super(message); 40 | } 41 | 42 | public ImmutableModifiedThroughReflectionException(final Throwable cause) { 43 | super(MESSAGE, cause); 44 | } 45 | 46 | public ImmutableModifiedThroughReflectionException(final String message, final Throwable cause) { 47 | super(message, cause); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /core/src/main/java/ro/esolutions/licensing/immutable/ValidObject.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ValidObject.java from LicenseManager modified Tuesday, February 21, 2012 10:59:35 CST (-0600). 3 | * 4 | * Copyright 2010-2013 the original author or authors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package ro.esolutions.licensing.immutable; 20 | 21 | /** 22 | * This class specifies an interface for checking the validity of an object. It 23 | * is specified as an abstract class instead of an interface so that the 24 | * implementing classes can keep the target method protected. 25 | * 26 | * @author Nick Williams 27 | * @version 1.0.0 28 | * @since 1.0.0 29 | */ 30 | abstract class ValidObject { 31 | /** 32 | * Checks the validity of this object, and throws an 33 | * {@link ImmutableModifiedThroughReflectionException} if that check fails. 34 | * 35 | * @throws ImmutableModifiedThroughReflectionException if the validity check fails. 36 | */ 37 | protected abstract void checkValidity(); 38 | } 39 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 19 | 21 | 4.0.0 22 | 23 | ro.esolutions 24 | license-manager 25 | pom 26 | 27 | License Manager 28 | 1.0.4-SNAPSHOT 29 | 30 | License Manager - A Java-based licensing tool for licensing commercial applications. 31 | 32 | 33 | 34 | Apache License, Version 2.0 35 | https://www.apache.org/licenses/LICENSE-2.0.txt 36 | repo 37 | 38 | 39 | 40 | 41 | mstaicu 42 | Marius Staicu 43 | marius.staicu@esolutions.rio 44 | eSolutions Grup 45 | https://www.esolutions.ro 46 | 47 | software engineer 48 | 49 | Romania/Bucharest 50 | 51 | 52 | vborcea 53 | Virgil Borcea 54 | virgil.borcea@esolutions.rio 55 | eSolutions Grup 56 | https://www.esolutions.ro 57 | 58 | software engineer 59 | 60 | Romania/Bucharest 61 | 62 | 63 | 64 | core 65 | base 66 | 67 | 68 | https://github.com/eSolutionsGrup/license-manager 69 | 70 | https://github.com/eSolutionsGrup/license-manager 71 | scm:git:https://github.com/eSolutionsGrup/license-manager.git 72 | scm:git:https://github.com/eSolutionsGrup/license-manager.git 73 | 74 | 75 | 76 | 17 77 | ${java.version} 78 | ${java.version} 79 | UTF-8 80 | UTF-8 81 | 82 | 83 | 84 | 85 | 86 | commons-codec 87 | commons-codec 88 | 1.15 89 | 90 | 91 | 92 | commons-io 93 | commons-io 94 | 2.11.0 95 | 96 | 97 | 98 | commons-lang 99 | commons-lang 100 | 2.6 101 | 102 | 103 | 104 | com.google.guava 105 | guava 106 | 31.1-jre 107 | 108 | 109 | 110 | org.ow2.asm 111 | asm 112 | 9.4 113 | 114 | 115 | 116 | 117 | 118 | 119 | ossrh 120 | https://oss.sonatype.org/content/repositories/snapshots 121 | 122 | 123 | ossrh 124 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 125 | 126 | 127 | 128 | 129 | 130 | 131 | org.apache.maven.plugins 132 | maven-release-plugin 133 | 3.0.1 134 | 135 | SemVerVersionPolicy 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | release 144 | 145 | 146 | 147 | org.apache.maven.plugins 148 | maven-gpg-plugin 149 | 3.1.0 150 | 151 | 152 | sign-artifacts 153 | verify 154 | 155 | sign 156 | 157 | 158 | 159 | 160 | 161 | org.apache.maven.plugins 162 | maven-source-plugin 163 | 3.2.1 164 | 165 | 166 | attach-sources 167 | 168 | jar-no-fork 169 | 170 | 171 | 172 | 173 | 174 | org.apache.maven.plugins 175 | maven-javadoc-plugin 176 | 3.5.0 177 | 178 | 179 | attach-javadocs 180 | 181 | jar 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | --------------------------------------------------------------------------------