From 8cc95fdfbe8146af8d5d4bcac7f7b9e3e2eb95c6 Mon Sep 17 00:00:00 2001
From: Roberto Sánchez <roberto.sanchez@curisit.net>
Date: Fri, 21 Feb 2014 13:52:42 +0000
Subject: [PATCH] #0 feaure - Created main methods for License validation

---
 src/main/java/net/curisit/securis/LicenseValidator.java  |    0 
 src/main/resources/images/logo_customer.png              |    0 
 src/main/java/net/curisit/securis/utils/LicUtils.java    |    0 
 src/main/java/net/curisit/securis/HWInfo.java            |  122 +++++
 src/main/resources/securis-client.properties             |    1 
 pom.xml                                                  |   16 
 src/main/java/net/curisit/securis/License.java           |    7 
 src/main/java/net/curisit/securis/beans/LicenseBean.java |   64 ++
 /dev/null                                                |   11 
 src/main/java/net/curisit/securis/SeCurisException.java  |   17 
 src/main/java/net/curisit/securis/utils/Params.java      |  191 ++++++++
 src/main/java/net/curisit/securis/CryptoHelper.java      |  155 +++++++
 src/main/java/net/curisit/securis/utils/JsonUtils.java   |  205 +++++++++
 src/main/java/net/curisit/securis/LicenseManager.java    |   47 ++
 doc/README.md                                            |   16 
 src/main/java/net/curisit/securis/SignatureHelper.java   |  144 ++++++
 src/main/java/net/curisit/securis/ReqGenerator.java      |   59 ++
 src/main/java/net/curisit/securis/beans/RequestBean.java |   97 ++++
 src/main/resources/log4j.xml                             |   26 +
 src/patch/java/net/curisit/securis/LicenseGenerator.java |  106 ++++
 20 files changed, 1,271 insertions(+), 13 deletions(-)

diff --git a/doc/README.md b/doc/README.md
new file mode 100644
index 0000000..252a4de
--- /dev/null
+++ b/doc/README.md
@@ -0,0 +1,16 @@
+### Private and Public keys generation
+
+DSA
+    
+    openssl dsaparam -out dsaparam.pem 2048
+    openssl gendsa -pkcs8 -out privkey.pem dsaparam.pem
+    
+    openssl dsa -in privkey.pem -pubout > mykey.pub
+    openssl pkcs8 -topk8 -inform PEM -in privkey.pem -out privkey.pkcs8 -nocrypt
+
+RSA    
+    
+    openssl genrsa -des3 -out privkey.pem 2048
+    openssl rsa -in privkey.pem -pubout > mykey.pub
+    openssl pkcs8 -topk8 -inform PEM -in privkey.pem -out privkey.pkcs8 -nocrypt
+    
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 27659dc..23a9cdd 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,6 @@
   <artifactId>securis-client</artifactId>
   <version>0.0.1-SNAPSHOT</version>
   <build>
-    <sourceDirectory>src</sourceDirectory>
     <plugins>
       <plugin>
         <artifactId>maven-compiler-plugin</artifactId>
@@ -37,5 +36,20 @@
   		<artifactId>commons-cli</artifactId>
   		<version>1.2</version>
   	</dependency>
+  	<dependency>
+  		<groupId>commons-net</groupId>
+  		<artifactId>commons-net</artifactId>
+  		<version>3.3</version>
+  	</dependency>
+  	<dependency>
+  		<groupId>org.codehaus.jackson</groupId>
+  		<artifactId>jackson-mapper-asl</artifactId>
+  		<version>1.9.13</version>
+  	</dependency>
+  	<dependency>
+  		<groupId>org.apache.logging.log4j</groupId>
+  		<artifactId>log4j-api</artifactId>
+  		<version>2.0-rc1</version>
+  	</dependency>
   </dependencies>
 </project>
\ No newline at end of file
diff --git a/src/main/java/net/curisit/securis/CryptoHelper.java b/src/main/java/net/curisit/securis/CryptoHelper.java
new file mode 100644
index 0000000..dd55a09
--- /dev/null
+++ b/src/main/java/net/curisit/securis/CryptoHelper.java
@@ -0,0 +1,155 @@
+package net.curisit.securis;
+
+import java.io.UnsupportedEncodingException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.SecureRandom;
+import java.security.spec.InvalidKeySpecException;
+import java.util.Arrays;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.SecretKey;
+import javax.crypto.SecretKeyFactory;
+import javax.crypto.spec.PBEKeySpec;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.apache.commons.net.util.Base64;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+//import net.curisit.common.ui.Dialogs;
+
+public class CryptoHelper {
+
+	private static final Logger log = LoggerFactory.getLogger(SignatureHelper.class);
+
+	private static final String KEY_FACTORY = "PBEWITHHMACSHA1";
+	private static final String PPROVIDER = "BC";
+	private static final String CIPHER_ALGORITHM = "AES/ECB/PKCS5Padding";
+
+	private static CryptoHelper singleton = new CryptoHelper();
+
+	private String passPhrase = null;
+
+	private CryptoHelper() {
+	}
+
+	public static CryptoHelper getInstance() {
+		return singleton;
+	}
+
+	/**
+	 * Encrypts a given text with AES algorithm
+	 * 
+	 * @param plainText
+	 * @return The encrypted text in Base64
+	 */
+	public String encrypt(String plainText) throws SeCurisException {
+		return encrypt(plainText, this.passPhrase);
+	}
+
+	public String encrypt(String plainText, String pass) throws SeCurisException {
+		Cipher aes;
+		try {
+			aes = Cipher.getInstance(CIPHER_ALGORITHM);
+			byte[] salt = getSalt();
+			aes.init(Cipher.ENCRYPT_MODE, getSecretKey(salt, pass));
+			byte[] ciphertext = aes.doFinal(plainText.getBytes("utf-8"));
+
+			return Base64.encodeBase64String(salt) + "\n" + Base64.encodeBase64String(ciphertext);
+		} catch (NoSuchAlgorithmException e) {
+			log.error("Error decrypting text", e);
+			throw new SeCurisException("Error decrypting text", e);
+		} catch (NoSuchPaddingException e) {
+			log.error("Error decrypting text", e);
+			throw new SeCurisException("Error decrypting text", e);
+		} catch (InvalidKeyException e) {
+			log.error("Error decrypting text", e);
+			throw new SeCurisException("Error decrypting text", e);
+		} catch (IllegalBlockSizeException e) {
+			log.error("Error decrypting text", e);
+			throw new SeCurisException("Error decrypting text", e);
+		} catch (BadPaddingException e) {
+			log.error("Error decrypting text", e);
+			throw new SeCurisException("Error decrypting text", e);
+		} catch (UnsupportedEncodingException e) {
+			log.error("Error decrypting text", e);
+			throw new SeCurisException("Error decrypting text", e);
+		}
+	}
+
+	/**
+	 * Encrypts a given text with AES algorithm
+	 * 
+	 * @param plainText
+	 *            in Base64
+	 * @return
+	 */
+	public String decript(String ciphertext) throws SeCurisException {
+		return decript(ciphertext, this.passPhrase);
+	}
+
+	public String decript(String ciphertext, String pass) throws SeCurisException {
+		Cipher aes;
+		try {
+			aes = Cipher.getInstance(CIPHER_ALGORITHM);
+			int sep = ciphertext.indexOf('\n');
+			if (sep == -1)
+				throw new SeCurisException("Unknown format ciphered text");
+			byte[] salt = Base64.decodeBase64(ciphertext.substring(0, sep));
+			aes.init(Cipher.DECRYPT_MODE, getSecretKey(salt, pass));
+			byte[] encryptedBytes = Base64.decodeBase64(ciphertext.substring(sep + 1));
+			String cleartext = new String(aes.doFinal(encryptedBytes));
+			return cleartext;
+		} catch (NoSuchAlgorithmException e) {
+			log.error("Error decrypting text", e);
+			throw new SeCurisException("Error decrypting text", e);
+		} catch (NoSuchPaddingException e) {
+			log.error("Error decrypting text", e);
+			throw new SeCurisException("Error decrypting text", e);
+		} catch (InvalidKeyException e) {
+			log.error("Error decrypting text", e);
+			throw new SeCurisException("Error decrypting text", e);
+		} catch (IllegalBlockSizeException e) {
+			log.error("Error decrypting text", e);
+			throw new SeCurisException("Error decrypting text", e);
+		} catch (BadPaddingException e) {
+			log.error("Error decrypting text", e);
+			throw new SeCurisException("Error decrypting text", e);
+		}
+	}
+
+	private byte[] getSalt() throws SeCurisException {
+		byte[] salt = new byte[20];
+		new SecureRandom().nextBytes(salt);
+		return salt;
+	}
+
+	private SecretKeySpec getSecretKey(byte[] salt, String pass) throws SeCurisException {
+		String passPhrase = pass.replace('a', 'ä');
+
+		try {
+			int iterations = 10000;
+
+			SecretKeyFactory factory = SecretKeyFactory.getInstance(KEY_FACTORY, PPROVIDER);
+			SecretKey tmp = factory.generateSecret(new PBEKeySpec(passPhrase.toCharArray(), salt, iterations, 128));
+			byte[] key = Arrays.copyOf(tmp.getEncoded(), 16);
+			SecretKeySpec spec = new SecretKeySpec(key, "AES");
+			return spec;
+
+		} catch (NoSuchAlgorithmException e) {
+			log.error("Error generation secret key", e);
+			throw new SeCurisException("Error generation secret key", e);
+		} catch (InvalidKeySpecException e) {
+			log.error("Error generation secret key", e);
+			throw new SeCurisException("Error generation secret key", e);
+		} catch (NoSuchProviderException e) {
+			log.error("Error generation secret key", e);
+			throw new SeCurisException("Error generation secret key", e);
+		}
+	}
+}
diff --git a/src/main/java/net/curisit/securis/HWInfo.java b/src/main/java/net/curisit/securis/HWInfo.java
new file mode 100644
index 0000000..a0f9e65
--- /dev/null
+++ b/src/main/java/net/curisit/securis/HWInfo.java
@@ -0,0 +1,122 @@
+package net.curisit.securis;
+
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Retrieve HW info
+ * 
+ * @author cesarcalvo
+ * 
+ */
+public class HWInfo {
+
+	private static final Logger log = LoggerFactory.getLogger(HWInfo.class);
+
+	public static String getOsName() {
+		return System.getProperty("os.name");
+	}
+
+	public static String getArch() {
+		return System.getProperty("os.arch");
+	}
+
+	public static int getNumCpus() {
+		return Runtime.getRuntime().availableProcessors();
+	}
+
+	/**
+	 * Get MAC address
+	 * 
+	 * @return
+	 */
+	public static List<String> getMACAddress() throws SeCurisException {
+		List<byte[]> macs = new ArrayList<byte[]>();
+		try {
+			// Get MAC for ethX interface, where X is the lower number with MAC address != null
+			for (NetworkInterface network : Collections.list(NetworkInterface.getNetworkInterfaces())) {
+				if (!network.isLoopback() && !network.isVirtual() && !network.isPointToPoint() && network.getHardwareAddress() != null) {
+					macs.add(network.getHardwareAddress());
+					log.debug("Interface added {}, MAC: {}", network.getName(), network.getHardwareAddress());
+					logInterface(network);
+				}
+			}
+
+			// If not found interface ethX
+			if (macs.isEmpty()) {
+				NetworkInterface network = NetworkInterface.getByInetAddress(InetAddress.getLocalHost());
+				if (network.getHardwareAddress() != null)
+					macs.add(network.getHardwareAddress());
+				log.debug("Selected interface (Inet Address rule)");
+				logInterface(network);
+			}
+
+			// If not found interface, last attempt, we get any mac
+			if (macs.isEmpty()) {
+				for (NetworkInterface network : Collections.list(NetworkInterface.getNetworkInterfaces())) {
+					if (!network.isLoopback() && !network.isVirtual() && !network.isPointToPoint() && network.getHardwareAddress() != null) {
+						if (network.getHardwareAddress() != null)
+							macs.add(network.getHardwareAddress());
+						log.debug("Selected interface (Any with MAC rule)");
+						logInterface(network);
+						break;
+					}
+				}
+			}
+
+			if (macs.isEmpty())
+				throw new SeCurisException("Unable to get MAC address");
+
+			List<String> macAddresses = new ArrayList<String>();
+			for (byte[] mac : macs) {
+				macAddresses.add(printMacAddress(mac));
+			}
+			log.info("MAC Addresses: {}", macAddresses);
+			return macAddresses;
+
+		} catch (UnknownHostException e) {
+			throw new SeCurisException("Unable to get MAC address", e);
+		} catch (SocketException e) {
+			throw new SeCurisException("Unable to get MAC address", e);
+		} catch (Exception e) {
+			throw new SeCurisException("Unable to get MAC address", e);
+		}
+	}
+
+	/**
+	 * Get microprocessor name
+	 * 
+	 * @return
+	 */
+	public static String getCPUName() throws SeCurisException {
+		return System.getenv("PROCESSOR_IDENTIFIER");
+	}
+
+	private static void logInterface(NetworkInterface network) {
+		log.debug("Interface name: {}", network.getName());
+		log.debug("Interface display name: {}", network.getDisplayName());
+		try {
+			log.debug("Interface mac: {}", printMacAddress(network.getHardwareAddress()));
+		} catch (SocketException e) {
+			// Silent
+		}
+	}
+
+	private static String printMacAddress(byte[] mac) {
+		StringBuilder sb = new StringBuilder();
+		for (int i = 0; i < mac.length; i++) {
+			sb.append(String.format("%s%02X", (i > 0) ? "-" : "", mac[i]));
+		}
+		return sb.toString();
+
+	}
+
+}
diff --git a/src/net/curisit/securis/License.java b/src/main/java/net/curisit/securis/License.java
similarity index 82%
rename from src/net/curisit/securis/License.java
rename to src/main/java/net/curisit/securis/License.java
index 94ad8dd..5b6e1e3 100644
--- a/src/net/curisit/securis/License.java
+++ b/src/main/java/net/curisit/securis/License.java
@@ -7,8 +7,12 @@
 import org.apache.commons.cli.Options;
 import org.apache.commons.cli.ParseException;
 import org.apache.commons.cli.PosixParser;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 public class License {
+
+	private static final Logger log = LoggerFactory.getLogger(License.class);
 
 	public static void main(String[] args) {
 		CommandLine cmd = getCommandLine(args);
@@ -47,11 +51,12 @@
 		options.addOption("h", "help", false, "Show help.");
 		options.addOption(OptionBuilder.withArgName("req_file").withLongOpt("rfile").withDescription("Set request file for its generation or for license requesting.").hasArg(true).create('r'));
 		options.addOption(OptionBuilder.withArgName("url_license_server").withLongOpt("server").withDescription("License server url.").hasArg(true).create('s'));
-		options.addOption(OptionBuilder.withArgName("lic_file").withLongOpt("license").withDescription("Validate lic file.").hasArg(true).create('l'));
+		options.addOption(OptionBuilder.withArgName("lic_file").withLongOpt("validate").withDescription("Validate lic file.").hasArg(true).create('l'));
 
 		options.addOption("g", "gen_request", false, "Generate request file. If --rfile parameter is missing then It is generated in current directory.");
 		options.addOption("c", "create", false, "Request a license file from server. --rfile and --server parameters are mandatory.");
 		options.addOption("t", "test_lc", false, "Test if License Server (LC) is available. --server parameter is mandatory.");
+		options.addOption(OptionBuilder.withArgName("lic_file").withLongOpt("sync").withDescription("Synchronize/renew the current license file. --server parameter is mandatory.").hasArg(true).create('y'));
 
 		return options;
 	}
diff --git a/src/main/java/net/curisit/securis/LicenseManager.java b/src/main/java/net/curisit/securis/LicenseManager.java
new file mode 100644
index 0000000..6d74838
--- /dev/null
+++ b/src/main/java/net/curisit/securis/LicenseManager.java
@@ -0,0 +1,47 @@
+package net.curisit.securis;
+
+import java.io.File;
+import java.io.IOException;
+
+import net.curisit.securis.beans.LicenseBean;
+import net.curisit.securis.beans.RequestBean;
+import net.curisit.securis.utils.JsonUtils;
+
+import org.apache.commons.io.FileUtils;
+
+/**
+ * Manage all licenses tasks, just like, validation, sync, requesting, ...
+ * 
+ * @author roberto <roberto.sanchez@curisit.net>
+ */
+public class LicenseManager {
+
+	private static LicenseManager singleton = new LicenseManager();
+
+	private LicenseManager() {
+	}
+
+	public static LicenseManager getInstance() {
+		return singleton;
+	}
+
+	public LicenseBean validateLicense(File licFile, String appCode, String customerCode) throws SeCurisException {
+		LicenseBean licBean;
+		try {
+			licBean = JsonUtils.json2object(FileUtils.readFileToString(licFile), LicenseBean.class);
+		} catch (IOException e) {
+			throw new SeCurisException("Error validating license", e);
+		}
+		SignatureHelper.getInstance().validateSignature(licBean);
+		validateHW(licBean, appCode, customerCode);
+
+		return licBean;
+	}
+
+	private void validateHW(RequestBean reqBean, String appCode, String customerCode) throws SeCurisException {
+		RequestBean currentHW = ReqGenerator.getInstance().createRequest(appCode, customerCode);
+		if (!currentHW.match(reqBean))
+			throw new SeCurisException("Current System info mismatch the License System info: " + JsonUtils.toJSON(reqBean));
+	}
+
+}
diff --git a/src/net/curisit/securis/LicenseValidator.java b/src/main/java/net/curisit/securis/LicenseValidator.java
similarity index 100%
rename from src/net/curisit/securis/LicenseValidator.java
rename to src/main/java/net/curisit/securis/LicenseValidator.java
diff --git a/src/main/java/net/curisit/securis/ReqGenerator.java b/src/main/java/net/curisit/securis/ReqGenerator.java
new file mode 100644
index 0000000..665b54a
--- /dev/null
+++ b/src/main/java/net/curisit/securis/ReqGenerator.java
@@ -0,0 +1,59 @@
+package net.curisit.securis;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+
+import net.curisit.securis.beans.RequestBean;
+import net.curisit.securis.utils.LicUtils;
+
+import org.apache.commons.io.IOUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ReqGenerator {
+
+	@SuppressWarnings("unused")
+	private static final Logger log = LoggerFactory.getLogger(ReqGenerator.class);
+
+	private static ReqGenerator singleton = new ReqGenerator();
+
+	private byte[] LOGO_SECRET;
+
+	private ReqGenerator() {
+		try {
+			LOGO_SECRET = "Logo ipsum s3cr3t test áíóú".getBytes("utf-8");
+		} catch (UnsupportedEncodingException e) {
+			e.printStackTrace();
+		}
+	}
+
+	public static ReqGenerator getInstance() {
+		return singleton;
+	}
+
+	public RequestBean createRequest(String appCode, String customerCode) throws SeCurisException {
+		RequestBean req = new RequestBean();
+
+		req.setAppCode(appCode);
+		req.setCustomerCode(customerCode);
+		req.setArch(HWInfo.getArch());
+		req.setCrcLogo(getCrcLogo());
+		req.setMacAddresses(HWInfo.getMACAddress());
+		req.setOsName(HWInfo.getOsName());
+
+		return req;
+	}
+
+	private String getCrcLogo() {
+		String logResource = "images/logo_customer.png";
+		InputStream is = getClass().getClassLoader().getResourceAsStream(logResource);
+		try {
+			String shaLogo = LicUtils.sha256(IOUtils.toByteArray(is), LOGO_SECRET);
+			return shaLogo;
+		} catch (IOException e) {
+			log.error("Customer logo was not found in classpath in " + logResource, e);
+			return null;
+		}
+	}
+}
diff --git a/src/main/java/net/curisit/securis/SeCurisException.java b/src/main/java/net/curisit/securis/SeCurisException.java
new file mode 100644
index 0000000..5c99e68
--- /dev/null
+++ b/src/main/java/net/curisit/securis/SeCurisException.java
@@ -0,0 +1,17 @@
+package net.curisit.securis;
+
+public class SeCurisException extends Exception {
+
+	private static final long serialVersionUID = 5702956178417661458L;
+
+	public SeCurisException() {
+	}
+
+	public SeCurisException(String msg, Exception e) {
+		super(msg, e);
+	}
+
+	public SeCurisException(String msg) {
+		super(msg);
+	}
+}
diff --git a/src/main/java/net/curisit/securis/SignatureHelper.java b/src/main/java/net/curisit/securis/SignatureHelper.java
new file mode 100644
index 0000000..0d14483
--- /dev/null
+++ b/src/main/java/net/curisit/securis/SignatureHelper.java
@@ -0,0 +1,144 @@
+package net.curisit.securis;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.security.InvalidKeyException;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.Date;
+
+import net.curisit.securis.beans.LicenseBean;
+import net.curisit.securis.beans.RequestBean;
+import net.curisit.securis.utils.JsonUtils;
+import net.curisit.securis.utils.Params;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.net.util.Base64;
+import org.apache.log4j.xml.DOMConfigurator;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+//import net.curisit.common.ui.Dialogs;
+
+/**
+ * digital Signature utilities
+ * 
+ * @author roberto <roberto.sanchez@curisit.net>
+ */
+public class SignatureHelper {
+
+	private static final Logger log = LoggerFactory.getLogger(SignatureHelper.class);
+	private static String AUX = "hEDhryRjs2QRE";
+
+	private static SignatureHelper singleton = new SignatureHelper();
+
+	private static final String DEFAULT_ALGORITHM = "RSA";
+	protected static final String SIGNATURE_GENERATION_ALGORITHM = "SHA256withRSA";
+
+	private SignatureHelper() {
+	}
+
+	public static SignatureHelper getInstance() {
+		return singleton;
+	}
+
+	protected void prepareSignature(Signature signature, LicenseBean licBean) throws SeCurisException {
+		try {
+			log.info("JSON: {}", JsonUtils.toJSON(licBean));
+			signature.update(JsonUtils.toJSON(licBean).getBytes("utf-8"));
+			signature.update(AUX.getBytes("utf-8"));
+		} catch (SignatureException | UnsupportedEncodingException e) {
+			throw new SeCurisException("Error generating or validating signature", e);
+		}
+	}
+
+	public void validateSignature(LicenseBean licBean) throws SeCurisException {
+		Signature signature;
+		try {
+			signature = Signature.getInstance(SIGNATURE_GENERATION_ALGORITHM);
+			signature.initVerify(generatePublicKey());
+
+			prepareSignature(signature, licBean);
+
+			if (signature.verify(Base64.decodeBase64(licBean.getSignature())))
+				return;
+		} catch (NoSuchAlgorithmException e) {
+			log.error("Error validating license for " + licBean, e);
+		} catch (InvalidKeyException e) {
+			log.error("Error validating license for " + licBean, e);
+		} catch (InvalidKeySpecException e) {
+			log.error("Error validating license for " + licBean, e);
+		} catch (IOException e) {
+			log.error("Error validating license for " + licBean, e);
+		} catch (SignatureException e) {
+			log.error("Error validating license for " + licBean, e);
+		}
+		throw new SeCurisException("License could not be validated");
+
+	}
+
+	private PublicKey generatePublicKey() throws NoSuchAlgorithmException, InvalidKeySpecException, IOException {
+		return generatePublicKey(new File(Params.get(Params.KEYS.PUBLIC_KEY_FILE, System.getenv("SECURIS_PUBLIC_KEY_FILE"))));
+	}
+
+	private PublicKey generatePublicKey(File publicKeyFile) throws NoSuchAlgorithmException, InvalidKeySpecException, IOException {
+		String pubKeyBase64 = FileUtils.readFileToString(publicKeyFile);
+		int from = pubKeyBase64.indexOf('\n');
+		int to = pubKeyBase64.indexOf("-----END", from);
+		pubKeyBase64 = pubKeyBase64.substring(from, to);
+
+		KeyFactory keyFactory = KeyFactory.getInstance(DEFAULT_ALGORITHM);
+		X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(Base64.decodeBase64(pubKeyBase64));
+		PublicKey publicKey = keyFactory.generatePublic(publicKeySpec);
+		log.info("Public key read sucessfully from file: {}", publicKeyFile.getAbsolutePath());
+		return publicKey;
+	}
+
+	protected PrivateKey generatePrivateKey(File privateKeyFile) throws NoSuchAlgorithmException, InvalidKeySpecException, IOException {
+		String privKeyBase64 = FileUtils.readFileToString(privateKeyFile);
+		int from = privKeyBase64.indexOf('\n');
+		int to = privKeyBase64.indexOf("-----END", from);
+		privKeyBase64 = privKeyBase64.substring(from, to);
+
+		KeyFactory keyFactory = KeyFactory.getInstance(DEFAULT_ALGORITHM);
+		PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(privKeyBase64));
+		PrivateKey privateKey = keyFactory.generatePrivate(privateKeySpec);
+
+		return privateKey;
+	}
+
+	private KeyPair generateKeyPair(File privateKeyFile, File publicKeyFile) throws NoSuchAlgorithmException, InvalidKeySpecException, IOException {
+
+		PublicKey publicKey = generatePublicKey(publicKeyFile);
+		PrivateKey privateKey = generatePrivateKey(privateKeyFile);
+
+		KeyPair kp = new KeyPair(publicKey, privateKey);
+		return kp;
+	}
+
+	public static void main(String[] args) throws SeCurisException {
+		// org.apache.log4j.Logger.getRootLogger().addAppender(new Appender);
+		DOMConfigurator.configure("/Users/cproberto/Documents/wsCurisIT/SeCurisClient/src/main/resources/log4j.xml");
+		RequestBean req = ReqGenerator.getInstance().createRequest("CI", "Roberto");
+
+		LicenseGenerator lg = LicenseGenerator.getInstance();
+		LicenseBean lic = lg.generateLicense(req, 12, new Date(new Date().getTime() + 24 * 3600 * 1000 * 10));
+		System.out.println(JsonUtils.toJSON(lic, true));
+		System.out.println(JsonUtils.toJSON(lic));
+	}
+
+	static {
+		byte[] s = new byte[]
+			{ 64, -31, -81, 36, 99, -77, 100, 17, 16, -119, 31, 72, 123, -88, -32, 51, 39, -96, -35, 116, -65, 8, 41, -119, -108, -34, 41, 19, 26, -102, -16, -120, -96, 1, -5, -26, -13, 61, -121, 94, 59, 54, 110, 110, -55, 127, -106 };
+		AUX = Base64.encodeBase64String(s);
+	}
+}
diff --git a/src/main/java/net/curisit/securis/beans/LicenseBean.java b/src/main/java/net/curisit/securis/beans/LicenseBean.java
new file mode 100644
index 0000000..691241d
--- /dev/null
+++ b/src/main/java/net/curisit/securis/beans/LicenseBean.java
@@ -0,0 +1,64 @@
+package net.curisit.securis.beans;
+
+import java.util.Date;
+
+import org.codehaus.jackson.annotate.JsonPropertyOrder;
+
+@JsonPropertyOrder(
+	{ "maxUsers", "expirationDate", "appCode", "arch", "osName", "customerCode", "macAddresses", "crclogo" })
+public class LicenseBean extends RequestBean {
+	private int maxUsers;
+	private Date expirationDate;
+	/**
+	 * Signature is stored in Base64 code
+	 */
+	private String signature;
+
+	public LicenseBean() {
+	}
+
+	public LicenseBean(RequestBean req) {
+		super.setAppCode(req.getAppCode());
+		super.setArch(req.getArch());
+		super.setCrcLogo(req.getCrcLogo());
+		super.setCustomerCode(req.getCustomerCode());
+		super.setMacAddresses(req.getMacAddresses());
+		super.setOsName(req.getOsName());
+	}
+
+	public String getSignature() {
+		return signature;
+	}
+
+	public void setSignature(String signature) {
+		this.signature = signature;
+	}
+
+	public Date getExpirationDate() {
+		return expirationDate;
+	}
+
+	public void setExpirationDate(Date expirationDate) {
+		this.expirationDate = expirationDate;
+	}
+
+	public int getMaxUsers() {
+		return maxUsers;
+	}
+
+	public void setMaxUsers(int maxUsers) {
+		this.maxUsers = maxUsers;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (!(obj instanceof LicenseBean))
+			return false;
+		LicenseBean rb = (LicenseBean) obj;
+		boolean result = (maxUsers == rb.maxUsers);
+		result = result && ((expirationDate == null && rb.expirationDate == null) || (expirationDate != null && expirationDate.equals(rb.expirationDate)));
+		result = result && ((signature == null && rb.signature == null) || (signature != null && signature.equals(rb.signature)));
+
+		return result && super.equals(obj);
+	}
+}
diff --git a/src/main/java/net/curisit/securis/beans/RequestBean.java b/src/main/java/net/curisit/securis/beans/RequestBean.java
new file mode 100644
index 0000000..cfb20f0
--- /dev/null
+++ b/src/main/java/net/curisit/securis/beans/RequestBean.java
@@ -0,0 +1,97 @@
+package net.curisit.securis.beans;
+
+import java.util.List;
+
+import org.codehaus.jackson.annotate.JsonAutoDetect;
+
+@JsonAutoDetect
+public class RequestBean {
+	private String customerCode;
+	private String crcLogo;
+	private String appCode;
+	private List<String> macAddresses;
+	private String osName;
+	private String arch;
+
+	public String getCustomerCode() {
+		return customerCode;
+	}
+
+	public void setCustomerCode(String customerCode) {
+		this.customerCode = customerCode;
+	}
+
+	public String getCrcLogo() {
+		return crcLogo;
+	}
+
+	public void setCrcLogo(String crcLogo) {
+		this.crcLogo = crcLogo;
+	}
+
+	public String getAppCode() {
+		return appCode;
+	}
+
+	public void setAppCode(String appCode) {
+		this.appCode = appCode;
+	}
+
+	public List<String> getMacAddresses() {
+		return macAddresses;
+	}
+
+	public void setMacAddresses(List<String> macAddresses) {
+		this.macAddresses = macAddresses;
+	}
+
+	public String getOsName() {
+		return osName;
+	}
+
+	public void setOsName(String osName) {
+		this.osName = osName;
+	}
+
+	public String getArch() {
+		return arch;
+	}
+
+	public void setArch(String arch) {
+		this.arch = arch;
+	}
+
+	public boolean match(RequestBean rb) {
+
+		boolean result = appCode != null && appCode.equals(rb.appCode);
+		result = result && (arch != null && arch.equals(rb.arch));
+		result = result && (crcLogo != null && crcLogo.equals(rb.crcLogo));
+		result = result && (customerCode != null && customerCode.equals(rb.customerCode));
+		result = result && (osName != null && osName.equals(rb.osName));
+		result = result && (macAddresses != null && rb.macAddresses != null) && anyMacIsIncluded(rb.getMacAddresses());
+
+		return result;
+	}
+
+	private boolean anyMacIsIncluded(List<String> macList) {
+		for (String mac : macList) {
+			if (macAddresses.contains(mac))
+				return true;
+		}
+		return false;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (!(obj instanceof RequestBean))
+			return false;
+		RequestBean rb = (RequestBean) obj;
+		boolean result = (rb.appCode == null && appCode == null) || (appCode != null && appCode.equals(rb.appCode));
+		result = result && ((rb.arch == null && arch == null) || (arch != null && arch.equals(rb.arch)));
+		result = result && ((rb.crcLogo == null && crcLogo == null) || (crcLogo != null && crcLogo.equals(rb.crcLogo)));
+		result = result && ((rb.customerCode == null && customerCode == null) || (customerCode != null && customerCode.equals(rb.customerCode)));
+		result = result && ((rb.osName == null && osName == null) || (osName != null && osName.equals(rb.osName)));
+		result = result && ((rb.macAddresses == null && macAddresses == null) || (macAddresses != null && macAddresses.equals(rb.macAddresses)));
+		return result;
+	}
+}
diff --git a/src/main/java/net/curisit/securis/utils/JsonUtils.java b/src/main/java/net/curisit/securis/utils/JsonUtils.java
new file mode 100644
index 0000000..397a0e0
--- /dev/null
+++ b/src/main/java/net/curisit/securis/utils/JsonUtils.java
@@ -0,0 +1,205 @@
+package net.curisit.securis.utils;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+
+import net.curisit.securis.SeCurisException;
+
+import org.codehaus.jackson.JsonParseException;
+import org.codehaus.jackson.JsonParser;
+import org.codehaus.jackson.JsonProcessingException;
+import org.codehaus.jackson.map.ObjectMapper;
+import org.codehaus.jackson.map.SerializationConfig;
+import org.codehaus.jackson.type.TypeReference;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Helper method to perform JSON tasks
+ * 
+ * @author cproberto
+ */
+public class JsonUtils {
+
+	private static final Logger log = LoggerFactory.getLogger(JsonUtils.class);
+
+	final private static ObjectMapper MAPPER = new ObjectMapper();
+
+	static {
+		MAPPER.configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true);
+		MAPPER.configure(JsonParser.Feature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER, true);
+		MAPPER.configure(JsonParser.Feature.ALLOW_COMMENTS, true);
+		MAPPER.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
+		MAPPER.configure(SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS, false);
+		MAPPER.configure(SerializationConfig.Feature.INDENT_OUTPUT, false);
+		MAPPER.configure(SerializationConfig.Feature.WRITE_DATES_AS_TIMESTAMPS, true);
+		MAPPER.configure(SerializationConfig.Feature.WRITE_ENUMS_USING_TO_STRING, true);
+
+	}
+
+	/**
+	 * Convert an object in the type pass as parameter, avoiding to use casting in code.
+	 * 
+	 * @param value
+	 * @param type
+	 * @return
+	 */
+	public static <T> T value(Object value, Class<T> type) {
+
+		return (T) value;
+	}
+
+	public static <T> T parseJSON(String json, Class<T> type) throws SeCurisException {
+		try {
+			if (json == null)
+				return null;
+			return MAPPER.readValue(json, type);
+		} catch (JsonParseException e) {
+			log.error("Error parsing JSON string to obejct: {}", json, e);
+			if (json.length() > 60)
+				json = json.substring(0, 50) + "...";
+			throw new SeCurisException("Error parsing JSON string to object: " + json, e);
+		} catch (IOException e) {
+			log.error("Error parsing JSON string to object: {}", json, e);
+			if (json.length() > 60)
+				json = json.substring(0, 50) + "...";
+			throw new SeCurisException("Error parsing JSON string to object: " + json, e);
+		}
+	}
+
+	/**
+	 * Create a JSON string from a object compatible or annotated with Jackson, i.e: <code>
+	 * {"f1":2345,"f2":"Test de valor"}
+	 * 
+	 * @param obj
+	 * @return JSON string representation from object
+	 */
+	public static String toJSON(Object obj) throws SeCurisException {
+		// and could also do other configuration...
+		try {
+			if (obj == null)
+				return null;
+			return MAPPER.writeValueAsString(obj);
+		} catch (JsonProcessingException e) {
+			log.error("Error formating JSON from object: {}", obj, e);
+			throw new SeCurisException("Error formating JSON from object: " + obj, e);
+		} catch (IOException e) {
+			log.error("Error formating JSON from object: {}", obj, e);
+			throw new SeCurisException("Error formating JSON from object: " + obj, e);
+		}
+	}
+
+	/**
+	 * Create a JSON string from a object compatible or annotated with Jackson, i.e: <code>
+	 * {"f1":2345,"f2":"Test de valor"}
+	 * 
+	 * @param obj
+	 * @return JSON string representation from object
+	 */
+	public static String toJSON(Object obj, boolean pretty) throws SeCurisException {
+		// and could also do other configuration...
+		try {
+			if (obj == null)
+				return null;
+			MAPPER.enable(SerializationConfig.Feature.INDENT_OUTPUT);
+			String json = MAPPER.writeValueAsString(obj);
+			MAPPER.disable(SerializationConfig.Feature.INDENT_OUTPUT);
+			return json;
+		} catch (JsonProcessingException e) {
+			log.error("Error formating JSON from object: {}", obj, e);
+			throw new SeCurisException("Error formating JSON from object: " + obj, e);
+		} catch (IOException e) {
+			log.error("Error formating JSON from object: {}", obj, e);
+			throw new SeCurisException("Error formating JSON from object: " + obj, e);
+		}
+	}
+
+	/**
+	 * Create a Map from a json string, i.e: <code>
+	 * {"f1":2345,"f2":"Test de valor"}
+	 * </code>
+	 * 
+	 * @param json
+	 *            String with json format
+	 * @return
+	 */
+	public static Map<String, Object> json2map(String json) throws JsonParseException {
+
+		try {
+			if (json == null)
+				return null;
+			return MAPPER.readValue(json, Map.class);
+		} catch (JsonParseException e) {
+			log.error("Error parsing JSON string to map: {}", json, e);
+			throw e;
+		} catch (IOException e) {
+			log.error("Error parsing JSON string to map: {}", json, e);
+		}
+		return null;
+	}
+
+	/**
+	 * Create a JSON strin from a Map object, i.e: <code>
+	 * {"f1":2345,"f2":"Test de valor"}
+	 * 
+	 * @param map
+	 * @return
+	 */
+	public static String map2json(Map<String, Object> map) {
+		// and could also do other configuration...
+		try {
+			if (map == null)
+				return null;
+			return MAPPER.writeValueAsString(map);
+		} catch (JsonProcessingException e) {
+			log.error("Error formating JSON from map: {}", map, e);
+		} catch (IOException e) {
+			log.error("Error formating JSON from map: {}", map, e);
+		}
+
+		return null;
+	}
+
+	/**
+	 * Create a Map from a json string, i.e: <code>
+	 * [{"f1":2345}, {"f2":"Test de valor"}]
+	 * </code>
+	 * 
+	 * @param json
+	 *            String with json format
+	 * @return
+	 */
+	public static List<Object> json2list(String json) {
+		try {
+			return MAPPER.readValue(json, List.class);
+		} catch (JsonParseException e) {
+			log.error("Error converting JSON string to object {}", json, e);
+		} catch (IOException e) {
+			log.error("Error converting JSON string to object {}", json, e);
+		}
+		return null;
+	}
+
+	public static <T> T json2object(String json, Class<T> classObject) throws SeCurisException {
+		try {
+			return MAPPER.readValue(json, classObject);
+		} catch (JsonParseException e) {
+			throw new SeCurisException("Error converting JSON to object", e);
+		} catch (IOException e) {
+			throw new SeCurisException("Error converting JSON to object", e);
+		}
+	}
+
+	public static <T> T json2object(String json, TypeReference<T> typeReference) throws SeCurisException {
+		try {
+			return MAPPER.readValue(json, typeReference);
+		} catch (JsonParseException e) {
+			throw new SeCurisException("Error converting JSON to object", e);
+		} catch (IOException e) {
+			e.printStackTrace();
+			throw new SeCurisException("Error converting JSON to object", e);
+		}
+	}
+
+}
diff --git a/src/net/curisit/securis/utils/LicUtils.java b/src/main/java/net/curisit/securis/utils/LicUtils.java
similarity index 100%
rename from src/net/curisit/securis/utils/LicUtils.java
rename to src/main/java/net/curisit/securis/utils/LicUtils.java
diff --git a/src/main/java/net/curisit/securis/utils/Params.java b/src/main/java/net/curisit/securis/utils/Params.java
new file mode 100644
index 0000000..56260c3
--- /dev/null
+++ b/src/main/java/net/curisit/securis/utils/Params.java
@@ -0,0 +1,191 @@
+package net.curisit.securis.utils;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Class that loads and serves global config parameters.
+ * 
+ * @author rsanchez
+ */
+public class Params {
+
+	private static Logger log = LoggerFactory.getLogger(Params.class);
+
+	/**
+	 * Key used to store config file resource location. In a web application, can be set as initial parameter in a servlet loaded on startup
+	 */
+	public static final String KEY_CONFIG_FILE = "/securis-client.properties";
+
+	private static Properties params = null;
+
+	static {
+		try {
+			loadParameters(KEY_CONFIG_FILE);
+		} catch (IOException e) {
+			log.error("Config file {} was not found in classpath", KEY_CONFIG_FILE);
+		}
+	}
+
+	/**
+	 * Loads application global parameters from a classpath resource
+	 * 
+	 * @param resource
+	 *            : Resource location in classpath, i.e: "/resource/cp-securis.conf"
+	 * @throws IOException
+	 */
+	public static void loadParameters(String resource) throws IOException {
+
+		log.info("Loading params from " + resource);
+		InputStream fileis = Params.class.getResourceAsStream(resource);
+
+		params = new Properties();
+		try {
+
+			params.load(fileis);
+			log.info("Params loaded OK");
+		} catch (IOException e) {
+			log.error("Error loading config file: " + e);
+			params = null;
+			throw e;
+		}
+
+	}
+
+	public static String getByDomain(String domain, String paramname) {
+		return getByDomain(domain, paramname, null);
+	}
+
+	public static String getByPrefix(String prefix, String paramname) {
+		return get(prefix + "." + paramname, get(paramname));
+	}
+
+	public static String getByPrefix(String prefix, String paramname, String defaultVal) {
+		return get(prefix + "." + paramname, get(paramname, defaultVal));
+	}
+
+	public static String getByDomain(String domain, String paramname, String defaultval) {
+		return get(paramname + "." + domain, defaultval);
+	}
+
+	public static int getIntByDomain(String domain, String paramname) {
+		return getInt(paramname + "." + domain, getInt(paramname));
+	}
+
+	public static int getIntByDomain(String domain, String paramname, int defaultval) {
+		return getInt(paramname + "." + domain, defaultval);
+	}
+
+	/**
+	 * Gets a List with all values of properties that begins with <code>prefix</code> It reads sequentially. For example:
+	 * 
+	 * <pre>
+	 * 	securis.sort.comparator.0: net.cp.securis.comparators.ComparePttidVsPtn
+	 * 	securis.sort.comparator.1: net.cp.securis.comparators.CompareFrequency
+	 * 	securis.sort.comparator.2: net.cp.securis.comparators.CompareOutgoingVsIncomming
+	 * 	securis.sort.comparator.3: net.cp.securis.comparators.CompareDuration 
+	 * 	securis.sort.comparator.4: net.cp.securis.comparators.CompareCallVsSms
+	 * </pre>
+	 * 
+	 * That config (for prefix: "securis.sort.comparator" ) will return a List<String> with values:
+	 * 
+	 * <pre>
+	 * 	"net.cp.securis.comparators.ComparePttidVsPtn", 
+	 * 	"net.cp.securis.comparators.CompareFrequency", 
+	 * 	"net.cp.securis.comparators.CompareOutgoingVsIncomming", 
+	 * 	"net.cp.securis.comparators.CompareDuration", 
+	 * 	"net.cp.securis.comparators.CompareCallVsSms"
+	 * </pre>
+	 * 
+	 * Note: If there is a gap between suffixes process will stop, that is, only will be returned properties found before gap.
+	 * 
+	 * @param prefix
+	 * @return
+	 */
+	public static List<String> getListByPrefix(String prefix) {
+		List<String> list = new ArrayList<String>();
+
+		String tpl = prefix + ".{0}";
+
+		int i = 0;
+		String value = get(MessageFormat.format(tpl, i++));
+		while (value != null) {
+			list.add(value);
+			value = get(MessageFormat.format(tpl, i++));
+		}
+
+		return list;
+	}
+
+	/**
+	 * Gets param value in config file or environment variables
+	 * 
+	 * @param paramname
+	 *            Global parameter's name
+	 * @return Value of paramname or null if paramname is not found neither in config file nor in environment variables
+	 */
+	public static String get(String paramname) {
+
+		assert (params != null) : "Parameters have not been loaded. Call method loadParameters(resource) before use Params.";
+
+		String value = params.getProperty(paramname);
+		if (value == null)
+			value = System.getenv(paramname);
+		return value;
+	}
+
+	/**
+	 * Gets param value from config file or environment variables
+	 * 
+	 * @param paramname
+	 *            Global parameter's name
+	 * @param defaultval
+	 * @return Value of paramname or defaultval if paramname is not found
+	 */
+	public static String get(String paramname, String defaultval) {
+		String value = get(paramname);
+		return (value == null ? defaultval : value);
+	}
+
+	/**
+	 * Gets param value in config file or environment variables
+	 * 
+	 * @param paramname
+	 *            Global parameter's name
+	 * @return Integer value of paramname or -1 if paramname is not found neither in config file nor in environment variables
+	 */
+	public static int getInt(String paramname) {
+		String value = get(paramname);
+		return (value == null ? -1 : Integer.parseInt(value));
+	}
+
+	/**
+	 * Gets param value from config file or environment variables
+	 * 
+	 * @param paramname
+	 *            Global parameter's name
+	 * @param defaultval
+	 * @return Integer value of paramname or defaultval if paramname is not found
+	 */
+	public static int getInt(String paramname, int defaultval) {
+		String value = get(paramname);
+		return (value == null ? defaultval : Integer.parseInt(value));
+	}
+
+	public static class KEYS {
+
+		/**
+		 * Public key file, Usually in "PEM" format
+		 */
+		public static final String PUBLIC_KEY_FILE = "public.key.file";
+
+	}
+
+}
diff --git a/src/main/resources/images/logo_customer.png b/src/main/resources/images/logo_customer.png
new file mode 100644
index 0000000..470bd7d
--- /dev/null
+++ b/src/main/resources/images/logo_customer.png
Binary files differ
diff --git a/src/main/resources/log4j.xml b/src/main/resources/log4j.xml
new file mode 100644
index 0000000..6298f36
--- /dev/null
+++ b/src/main/resources/log4j.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
+
+    <appender name="console" class="org.apache.log4j.ConsoleAppender">
+        <param name="Target" value="System.out"/>
+        <layout class="org.apache.log4j.PatternLayout">
+            <param name="ConversionPattern" value="%-5p %c{1} - %m%n"/>
+        </layout>
+    </appender>
+
+    <logger name="net.curisit">
+        <level value="INFO"/>
+    </logger>
+
+    <logger name="sandbox">
+        <level value="DEBUG"/>
+    </logger>
+
+    <root>
+        <priority value ="INFO" />
+        <appender-ref ref="console" />
+    </root>
+
+</log4j:configuration>
\ No newline at end of file
diff --git a/src/main/resources/securis-client.properties b/src/main/resources/securis-client.properties
new file mode 100644
index 0000000..a6a2ed3
--- /dev/null
+++ b/src/main/resources/securis-client.properties
@@ -0,0 +1 @@
+public.key.file = /Users/cproberto/Documents/wsPython/doky/tests/mykey.pub
\ No newline at end of file
diff --git a/src/net/curisit/securis/ReqGenerator.java b/src/net/curisit/securis/ReqGenerator.java
deleted file mode 100644
index 9a97b60..0000000
--- a/src/net/curisit/securis/ReqGenerator.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package net.curisit.securis;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class ReqGenerator {
-
-	@SuppressWarnings("unused")
-	private static final Logger log = LoggerFactory.getLogger(ReqGenerator.class);
-
-}
diff --git a/src/patch/java/net/curisit/securis/LicenseGenerator.java b/src/patch/java/net/curisit/securis/LicenseGenerator.java
new file mode 100644
index 0000000..5977944
--- /dev/null
+++ b/src/patch/java/net/curisit/securis/LicenseGenerator.java
@@ -0,0 +1,106 @@
+package net.curisit.securis;
+
+import java.io.File;
+import java.io.IOException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.security.spec.InvalidKeySpecException;
+import java.text.MessageFormat;
+import java.util.Date;
+
+import net.curisit.securis.beans.LicenseBean;
+import net.curisit.securis.beans.RequestBean;
+
+import org.apache.commons.net.util.Base64;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * License generator and signer
+ * 
+ * @author roberto <roberto.sanchez@curisit.net>
+ */
+public class LicenseGenerator {
+
+	@SuppressWarnings("unused")
+	private static final Logger log = LoggerFactory.getLogger(LicenseGenerator.class);
+
+	private static LicenseGenerator singleton = new LicenseGenerator();
+
+	private LicenseGenerator() {
+	}
+
+	public static LicenseGenerator getInstance() {
+		return singleton;
+	}
+
+	/**
+	 * Generate a license bean with the specified data
+	 * 
+	 * @param hw
+	 * @param customerCode
+	 *            - e.g: "BP"
+	 * @param maxInstances
+	 * @param maxUsers
+	 * @param maxTimeThreshold
+	 *            Max time between synchronizations expressed in days
+	 * @return
+	 * @throws SeCurisException
+	 */
+	public LicenseBean generateLicense(RequestBean req, int maxUsers, Date expirationDate) throws SeCurisException {
+		log.info(MessageFormat.format("Generating license: MAC: {0}, Customer code: {1}, AppCode: {2}", req.getMacAddresses(), req.getCustomerCode(), req.getAppCode()));
+		LicenseBean license = new LicenseBean(req);
+		license.setExpirationDate(expirationDate);
+		license.setMaxUsers(maxUsers);
+		sign(license);
+
+		return license;
+	}
+
+	/**
+	 * TODO: This method should be removed from client code.
+	 * 
+	 * @param licBean
+	 * @return
+	 * @throws NoSuchAlgorithmException
+	 * @throws IOException
+	 * @throws InvalidKeySpecException
+	 * @throws InvalidKeyException
+	 * @throws SignatureException
+	 */
+	public String sign(LicenseBean licBean) throws SeCurisException {
+		SignatureHelper sh = SignatureHelper.getInstance();
+
+		Signature signature;
+		try {
+			signature = Signature.getInstance(SignatureHelper.SIGNATURE_GENERATION_ALGORITHM);
+			signature.initSign(sh.generatePrivateKey(new File("/Users/cproberto/Documents/wsPython/doky/tests/privkey.pkcs8")));
+
+			sh.prepareSignature(signature, licBean);
+
+			byte[] signatureData = signature.sign();
+			licBean.setSignature(Base64.encodeBase64String(signatureData));
+			return licBean.getSignature();
+		} catch (NoSuchAlgorithmException e) {
+			log.error("Error signing license for " + licBean, e);
+		} catch (InvalidKeyException e) {
+			log.error("Error signing license for " + licBean, e);
+		} catch (InvalidKeySpecException e) {
+			log.error("Error signing license for " + licBean, e);
+		} catch (IOException e) {
+			log.error("Error signing license for " + licBean, e);
+		} catch (SignatureException e) {
+			log.error("Error signing license for " + licBean, e);
+		}
+		throw new SeCurisException("License could not be generated");
+	}
+
+	public static void main(String[] args) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException, NoSuchProviderException, InvalidKeyException, SignatureException {
+
+		System.out.print("os.arch: " + System.getProperty("os.arch") + " " + System.getProperty("os.name"));
+
+	}
+}

--
Gitblit v1.3.2