diff --git a/pom.xml b/pom.xml
index f2f0737be..5220d9f91 100644
--- a/pom.xml
+++ b/pom.xml
@@ -32,6 +32,15 @@
+ tests
+
+
+ tests
+
+ **/*.java
+
+
+
maven-compiler-plugin
diff --git a/src/java/com/rjconsultores/ventaboletos/web/utilerias/security/AESGSMHelper.java b/src/java/com/rjconsultores/ventaboletos/web/utilerias/security/AESGSMHelper.java
new file mode 100644
index 000000000..bc7f43653
--- /dev/null
+++ b/src/java/com/rjconsultores/ventaboletos/web/utilerias/security/AESGSMHelper.java
@@ -0,0 +1,106 @@
+package com.rjconsultores.ventaboletos.web.utilerias.security;
+
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.KeySpec;
+
+import javax.crypto.Cipher;
+import javax.crypto.SecretKey;
+import javax.crypto.SecretKeyFactory;
+import javax.crypto.spec.GCMParameterSpec;
+import javax.crypto.spec.PBEKeySpec;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.apache.commons.codec.DecoderException;
+import org.apache.commons.codec.binary.Hex;
+
+public class AESGSMHelper {
+ private final String SECRET_KEY = "RJ@2019#c0n5ul10r35";
+ private final String SALT = "HrqoFr44GtkAhhYN+jP8Ag==";
+ private final String ENCRYPT_ALGO = "AES/GCM/NoPadding";
+ private final int TAG_LENGTH_BIT = 128;
+ private final int IV_LENGTH_BYTE = 12;
+
+ private final Charset UTF_8 = StandardCharsets.UTF_8;
+
+ public String encrypt(String value) throws Exception {
+ SecretKey secret = getAESKeyFromPassword(SECRET_KEY.toCharArray());
+
+ byte[] pText = value.getBytes(StandardCharsets.UTF_8);
+ byte[] iv = getRandomNonce(12);
+ byte[] cipherText = encrypt(pText, secret, iv);
+
+ byte[] cipherTextWithIv = ByteBuffer.allocate(iv.length + cipherText.length)
+ .put(iv)
+ .put(cipherText)
+ .array();
+
+ return hex(cipherTextWithIv);
+ }
+
+ private byte[] encrypt(byte[] pText, SecretKey secret, byte[] iv) throws Exception {
+ Cipher cipher = Cipher.getInstance(ENCRYPT_ALGO);
+ cipher.init(Cipher.ENCRYPT_MODE, secret, new GCMParameterSpec(TAG_LENGTH_BIT, iv));
+
+ byte[] encryptedText = cipher.doFinal(pText);
+
+ return encryptedText;
+
+ }
+
+ public String decrypt(String value) throws Exception {
+ SecretKey secret = getAESKeyFromPassword(SECRET_KEY.toCharArray());
+
+ byte[] cText = unhex(value);
+
+ ByteBuffer bb = ByteBuffer.wrap(cText);
+
+ byte[] iv = new byte[IV_LENGTH_BYTE];
+ bb.get(iv);
+
+ byte[] cipherText = new byte[bb.remaining()];
+ bb.get(cipherText);
+
+ String plainText = decrypt(cipherText, secret, iv);
+
+ return plainText;
+ }
+
+ private String decrypt(byte[] cText, SecretKey secret, byte[] iv) throws Exception {
+ Cipher cipher = Cipher.getInstance(ENCRYPT_ALGO);
+ cipher.init(Cipher.DECRYPT_MODE, secret, new GCMParameterSpec(TAG_LENGTH_BIT, iv));
+
+ byte[] plainText = cipher.doFinal(cText);
+
+ return new String(plainText, UTF_8);
+ }
+
+ private byte[] getRandomNonce(int numBytes) {
+ byte[] nonce = new byte[numBytes];
+ new SecureRandom().nextBytes(nonce);
+
+ return nonce;
+ }
+
+ private SecretKey getAESKeyFromPassword(char[] password) throws NoSuchAlgorithmException, InvalidKeySpecException {
+ SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
+
+ KeySpec spec = new PBEKeySpec(password, SALT.getBytes(), 65536, 256);
+ SecretKeySpec secret = new SecretKeySpec(factory.generateSecret(spec).getEncoded(), "AES");
+
+ return secret;
+ }
+
+ private String hex(byte[] bytes) {
+ char[] result = Hex.encodeHex(bytes);
+ return new String(result);
+ }
+
+ private byte[] unhex(String hex) throws DecoderException {
+ return Hex.decodeHex(hex.toCharArray());
+ }
+}
diff --git a/src/java/com/rjconsultores/ventaboletos/web/utilerias/security/SecurityEmpresaToken.java b/src/java/com/rjconsultores/ventaboletos/web/utilerias/security/SecurityEmpresaToken.java
new file mode 100644
index 000000000..533537fa1
--- /dev/null
+++ b/src/java/com/rjconsultores/ventaboletos/web/utilerias/security/SecurityEmpresaToken.java
@@ -0,0 +1,140 @@
+package com.rjconsultores.ventaboletos.web.utilerias.security;
+
+import java.text.ParseException;
+import java.time.Duration;
+import java.util.Calendar;
+
+import javax.xml.bind.DatatypeConverter;
+
+import org.apache.log4j.Logger;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonObject;
+import com.nimbusds.jose.JWSAlgorithm;
+import com.nimbusds.jose.JWSHeader;
+import com.nimbusds.jose.JWSObject;
+import com.nimbusds.jose.Payload;
+import com.nimbusds.jose.crypto.MACSigner;
+import com.nimbusds.jwt.JWTClaimsSet;
+
+import net.minidev.json.JSONObject;
+
+public class SecurityEmpresaToken {
+ private static Logger log = Logger.getLogger(SecurityEmpresaToken.class);
+
+ private String secret = "#KO&Fm4_k.sU9M8`6Mx'F\\\"H:*Qxu]6F4r,)JmZ2Jwafd)I.2[RET'1:)VQ6mG9,";
+ private static final Duration ttl = Duration.ofHours(24);
+
+ private Gson gson = new Gson();
+
+ public String bodyRequestGenerate(final Integer empresaId, final String cnpj) throws SecurityException {
+ try {
+ AESGSMHelper crypto = new AESGSMHelper();
+
+ JsonObject json = new JsonObject();
+ json.addProperty("empresaId", empresaId);
+ json.addProperty("CNPJ", cnpj);
+
+ return crypto.encrypt(json.toString());
+
+ } catch (Exception e) {
+ log.error("Erro ao gerar o body usado no request da licença: " + e.getMessage(), e);
+
+ throw new SecurityException(e);
+ }
+ }
+
+ public String licenseDefaultGenerate(final Integer empresaId, final String cnpj) throws SecurityException {
+ try {
+ AESGSMHelper crypto = new AESGSMHelper();
+
+ JsonObject json = new JsonObject();
+ json.addProperty("empresaId", empresaId);
+ json.addProperty("CNPJ", cnpj);
+ json.addProperty("aprovado", 1);
+
+ return crypto.encrypt(json.toString());
+
+ } catch (Exception e) {
+ log.error("Erro ao gerar a licença padrão para as empresas existentes: " + e.getMessage(), e);
+
+ throw new SecurityException(e);
+ }
+ }
+
+ public boolean licenseValidate(final String license, final Integer empresaId, final String cnpj) {
+ try {
+ AESGSMHelper crypto = new AESGSMHelper();
+
+ final String value = crypto.decrypt(license);
+ final JsonObject json = gson.fromJson(value, JsonObject.class);
+
+ if (json.has("empresaId") && json.get("empresaId").getAsInt() == empresaId.intValue()
+ && json.has("CNPJ") && json.get("CNPJ").getAsString().equals(cnpj)
+ && json.has("aprovado")) {
+ log.debug("[empresaId=" + json.get("empresaId").getAsString() + ", CNPJ=" + json.get("CNPJ").getAsString() + ", aprovado=" + json.get("aprovado").getAsString() + "]");
+
+ return json.get("aprovado").getAsString().equals("1");
+ }
+ } catch (Exception e) {
+ log.error("Erro ao gerar o body usado no request da licença: " + e.getMessage(), e);
+ }
+
+ return false;
+ }
+
+ public String requestGenerate(String licenseRequest) throws SecurityException {
+ return requestGenerate(licenseRequest, ttl);
+ }
+
+ public String requestGenerate(String licenseRequest, Duration ttl) throws SecurityException {
+ try {
+ Calendar cal = Calendar.getInstance();
+ cal.add(Calendar.MILLISECOND, (int) ttl.toMillis());
+
+ JWTClaimsSet claims = new JWTClaimsSet.Builder()
+ .expirationTime(cal.getTime())
+ .claim("sub", licenseRequest)
+ .claim("userId", "adm")
+ .claim("role", "ROLE_TOKEN")
+ .build();
+
+ JWSObject jwsObject = new JWSObject(new JWSHeader(JWSAlgorithm.HS256), new Payload(claims.toJSONObject()));
+
+ jwsObject.sign(new MACSigner(DatatypeConverter.parseBase64Binary(secret)));
+
+ return jwsObject.serialize();
+ } catch (Exception e) {
+ log.error("Erro ao gerar a request: " + e.getMessage(), e);
+
+ throw new SecurityException(e);
+ }
+ }
+
+ public String tokenValidate(final String token) throws SecurityException {
+ try {
+ JWSObject jwsObject = JWSObject.parse(token);
+ JSONObject jsonPayload = jwsObject.getPayload().toJSONObject();
+ JWTClaimsSet claims = JWTClaimsSet.parse(jsonPayload);
+
+ if (claims.getExpirationTime().compareTo(Calendar.getInstance().getTime()) < 0) {
+ throw new SecurityException("Token expirado");
+ }
+
+ return claims.getSubject();
+
+ } catch (SecurityException e) {
+ throw e;
+
+ } catch (ParseException e) {
+ log.error("Erro no parser do token: " + e.getMessage(), e);
+
+ throw new SecurityException(e);
+
+ } catch (Exception e) {
+ log.error("Erro ao validar o token: " + e.getMessage(), e);
+
+ throw new SecurityException(e);
+ }
+ }
+}
diff --git a/src/java/com/rjconsultores/ventaboletos/web/utilerias/security/exception/SecurityException.java b/src/java/com/rjconsultores/ventaboletos/web/utilerias/security/exception/SecurityException.java
new file mode 100644
index 000000000..ca6b559f9
--- /dev/null
+++ b/src/java/com/rjconsultores/ventaboletos/web/utilerias/security/exception/SecurityException.java
@@ -0,0 +1,13 @@
+package com.rjconsultores.ventaboletos.web.utilerias.security.exception;
+
+public class SecurityException extends Exception {
+ private static final long serialVersionUID = 1801174850577435303L;
+
+ public SecurityException(String arg0, Throwable arg1) {
+ super(arg0, arg1);
+ }
+
+ public SecurityException(String arg0) {
+ super(arg0);
+ }
+}
diff --git a/tests/com/rjconsultores/tests/SecurityEmpresaTokenTest.java b/tests/com/rjconsultores/tests/SecurityEmpresaTokenTest.java
new file mode 100644
index 000000000..82de6e409
--- /dev/null
+++ b/tests/com/rjconsultores/tests/SecurityEmpresaTokenTest.java
@@ -0,0 +1,91 @@
+package com.rjconsultores.tests;
+
+import static org.junit.Assert.fail;
+
+import java.time.Duration;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.rjconsultores.ventaboletos.web.utilerias.security.SecurityEmpresaToken;
+
+public class SecurityEmpresaTokenTest {
+ private static final Logger log = LoggerFactory.getLogger(SecurityEmpresaTokenTest.class);
+
+ @Before
+ public void initApplicationContext() {
+ log.info("[ BEGIN ]");
+ }
+
+ @After
+ public void closeApplicationContext() {
+ log.info("[ END ]");
+ }
+
+ @Test
+ public void test_EmpresaNova() throws Exception {
+ // license request -> token request -> token response -> license
+
+ Integer empresaId = 1313;
+ String cnpj = "00073778000120";
+
+ SecurityEmpresaToken security = new SecurityEmpresaToken();
+
+ final String bodyRequest = security.bodyRequestGenerate(empresaId, cnpj);
+ final String request = security.requestGenerate(bodyRequest);
+
+ final String license = security.tokenValidate(request);
+ final boolean valid = security.licenseValidate(license, empresaId, cnpj);
+
+ log.info("Body Request: " + bodyRequest);
+ log.info("Request: " + request);
+ log.info("License: " + license);
+ log.info("Valid: " + Boolean.toString(valid));
+
+ if (valid) fail("Licença válida");
+ }
+
+ @Test
+ public void test_Licenca() throws Exception {
+ Integer empresaId = 1313;
+ String cnpj = "00073778000120";
+
+ SecurityEmpresaToken security = new SecurityEmpresaToken();
+
+ final String license = security.licenseDefaultGenerate(empresaId, cnpj);
+ final boolean valid = security.licenseValidate(license, empresaId, cnpj);
+
+ log.info("License: " + license);
+ log.info("Valid: " + Boolean.toString(valid));
+
+ if (!valid) fail("Licença inválida");
+ }
+
+ @Test(expected = SecurityException.class)
+ public void test_TTL_expired() throws Exception {
+ Duration ttl = Duration.ofSeconds(5);
+
+ Integer empresaId = 1313;
+ String cnpj = "00073778000120";
+
+ SecurityEmpresaToken security = new SecurityEmpresaToken();
+
+ final String bodyRequest = security.bodyRequestGenerate(empresaId, cnpj);
+ final String request = security.requestGenerate(bodyRequest, ttl);
+
+ Thread.sleep(Duration.ofSeconds(10).toMillis());
+
+ final String license = security.tokenValidate(request);
+ final boolean valid = security.licenseValidate(license, empresaId, cnpj);
+
+ log.info("Body Request: " + bodyRequest);
+ log.info("Request: " + request);
+ log.info("License: " + license);
+ log.info("Valid: " + Boolean.toString(valid));
+
+ if (valid) fail("Licença válida");
+ }
+}
diff --git a/tests/com/rjconsultores/tests/SerieEmbarcadaTest.java b/tests/com/rjconsultores/tests/SerieEmbarcadaTest.java.old
similarity index 100%
rename from tests/com/rjconsultores/tests/SerieEmbarcadaTest.java
rename to tests/com/rjconsultores/tests/SerieEmbarcadaTest.java.old