· 4 years ago · Jan 19, 2021, 03:44 PM
1package cl.tbk.m2m.tbkpay.users.util;
2
3import cl.tbk.m2m.tbkpay.users.client.exception.ClaveUnicaClientException;
4import cl.tbk.m2m.tbkpay.users.controller.exception.UserException;
5import cl.tbk.m2m.tbkpay.users.dto.EmailRequest;
6import cl.tbk.m2m.tbkpay.users.exception.BadRequestException;
7import com.fasterxml.jackson.core.JsonProcessingException;
8import com.fasterxml.jackson.databind.ObjectMapper;
9import com.fasterxml.jackson.databind.node.ObjectNode;
10import org.apache.commons.codec.DecoderException;
11import org.apache.commons.codec.binary.Hex;
12import org.apache.http.client.config.RequestConfig;
13import org.apache.http.impl.client.CloseableHttpClient;
14import org.apache.http.impl.client.HttpClientBuilder;
15import org.springframework.http.client.ClientHttpRequestFactory;
16import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
17import org.springframework.util.ObjectUtils;
18
19import javax.crypto.Cipher;
20import javax.crypto.spec.IvParameterSpec;
21import javax.crypto.spec.SecretKeySpec;
22import java.io.IOException;
23import java.security.SecureRandom;
24import java.text.DecimalFormat;
25import java.time.LocalDateTime;
26import java.time.format.DateTimeFormatter;
27import java.time.temporal.ChronoField;
28import java.util.Base64;
29import java.util.regex.Matcher;
30import java.util.regex.Pattern;
31
32import static org.springframework.util.StringUtils.isEmpty;
33
34/**
35 * Class with utilities for API
36 *
37 * @author Germán Muñoz
38 * @version 1.0
39 */
40public final class Utils {
41
42 private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss");
43
44 /**
45 * Delete host and context root
46 *
47 * @param url spring hateoas href url
48 * @return string path
49 */
50 private static String replaceHost(String url) {
51 int index = url.indexOf("/api");
52 StringBuilder builder = new StringBuilder(url);
53 builder.delete(0, index);
54 return builder.toString().replaceAll("%3A", ":");
55 }
56
57 /**
58 * Format number to monetary value with default locale
59 *
60 * @param value number to format
61 * @return string with format
62 */
63 public static String formatMonetaryValue(Long value) {
64 DecimalFormat decimalFormat = new DecimalFormat("###,###.##");
65
66 return "$" + decimalFormat.format(value);
67 }
68
69 /**
70 * Format datetime to date
71 *
72 * @param dateTime param datetime
73 * @return string with date on format dd/MM/yyyy
74 */
75 public static String formatDate(LocalDateTime dateTime) {
76 return dateTime.format(FORMATTER).split(" ")[0];
77 }
78
79 /**
80 * Format datetime to hour
81 *
82 * @param dateTime param datetime
83 * @return string with hour on format HH:mm:ss
84 */
85 public static String formatHour(LocalDateTime dateTime) {
86 return dateTime.format(FORMATTER).split(" ")[1];
87 }
88
89 public static LocalDateTime addMilliseconds(LocalDateTime localDateTime,
90 Integer milliseconds) {
91 LocalDateTime newLocalDatetime = localDateTime.plus(milliseconds, ChronoField.MILLI_OF_DAY.getBaseUnit());
92 return newLocalDatetime;
93 }
94
95 /**
96 * Method for set timeouts
97 *
98 * @param connectTimeout timeout for connect
99 * @param requestTimeout timeout for request
100 * @param socketTimeout timeout for socket
101 * @return object factory with timeouts
102 */
103 public static ClientHttpRequestFactory getClientHttpRequestFactory(Integer connectTimeout,
104 Integer requestTimeout,
105 Integer socketTimeout) {
106 RequestConfig config = RequestConfig.custom()
107 .setConnectTimeout(connectTimeout)
108 .setConnectionRequestTimeout(requestTimeout)
109 .setSocketTimeout(socketTimeout)
110 .build();
111 CloseableHttpClient client = HttpClientBuilder
112 .create()
113 .setDefaultRequestConfig(config)
114 .build();
115 return new HttpComponentsClientHttpRequestFactory(client);
116 }
117
118 /**
119 * @param request
120 * @throws BadRequestException
121 */
122 public static void validateMailRequest(EmailRequest request) throws BadRequestException {
123 if (isEmpty(request.getRutCommerce())) {
124 throw new BadRequestException("rut_commerce is null or empty");
125 }
126 if (isEmpty(request.getEmail())) {
127 throw new BadRequestException("mail is null or empty");
128 }
129 }
130
131 public static String getResponseFromIdf(String response)
132 throws DecoderException {
133 //Se obtiene vector de inicializacion
134 String ivResponse = Utils.getIvFromIdf(response);
135 //Se obtiene la data
136 String dataResponse = Utils.getDataFromIdf(response);
137 //Se obtiene data desencriptada en base64
138 return Utils.decrypt(Base64.getDecoder().decode(ivResponse), dataResponse);
139 }
140
141 /**
142 * @param idf
143 * @return
144 * @throws DecoderException
145 */
146 public static String getIvFromIdf(String idf)
147 throws DecoderException {
148 String[] idfBytesHex = byteToHexFromIdf(idf);
149 String[] ivBytesHex = new String[16];
150 for (int i = 0; i < 16; i++) {
151 ivBytesHex[i] = idfBytesHex[i];
152 }
153 return Base64.getEncoder().encodeToString(Hex.decodeHex(String.join("", ivBytesHex)));
154 }
155
156 /**
157 * @param idf
158 * @return
159 * @throws DecoderException
160 */
161 public static String getDataFromIdf(String idf)
162 throws DecoderException {
163 String[] idfBytesHex = byteToHexFromIdf(idf);
164 String[] dataBytesHex = new String[idfBytesHex.length - 16];
165 int cont = 0;
166 for (int i = 0; i < idfBytesHex.length; i++) {
167 if (i >= 16) {
168 dataBytesHex[cont] = idfBytesHex[i];
169 cont++;
170 }
171 }
172 return Base64.getEncoder().encodeToString(Hex.decodeHex(String.join("", dataBytesHex)));
173 }
174
175 private static String[] byteToHexFromIdf(String idf) {
176 byte[] idfBytes = Base64.getDecoder().decode(idf);
177 String[] idfBytesHex = new String[idfBytes.length];
178 for (int i = 0; i < idfBytes.length; i++) {
179 idfBytesHex[i] = String.format("%02x", idfBytes[i]);
180 }
181 return idfBytesHex;
182 }
183
184 public static String decrypt(byte[] iv,
185 String strToDecrypt) {
186
187 SecretKeySpec secretKey;
188
189 try {
190 secretKey =
191 new SecretKeySpec(Base64.getDecoder().decode(FileUtils.getInstance().getKey().getEncoded()), "AES");
192 } catch (Exception e) {
193 throw new UserException("[decrypt]", e);
194 }
195
196 IvParameterSpec ivSpec = new IvParameterSpec(iv);
197 Cipher cipher = null;
198 byte[] decrypted = null;
199
200 try {
201 cipher = Cipher.getInstance("AES/GCM/PKCS5PADDING");
202 cipher.init(Cipher.DECRYPT_MODE, secretKey, ivSpec);
203 decrypted = cipher.doFinal(Base64.getDecoder().decode(strToDecrypt));
204 } catch (Exception e) {
205 throw new UserException("[decrypt]", e);
206 }
207 return new String(decrypted);
208 }
209
210 public static String generateRequestIdf(String request)
211 throws DecoderException {
212 //Se genera vector de inicializacion
213 String iv = Utils.generateRandomIvBase64();
214 String base64DataRequest = Base64.getEncoder().encodeToString(request.getBytes());
215 //Se encripta el string base64
216 byte[] encryptRequest = Utils.encrypt(Base64.getDecoder().decode(iv), base64DataRequest);
217 //Se concatena iv con data en base64
218 char[] ivHex = Hex.encodeHex(Base64.getDecoder().decode(iv));
219 char[] encryptRequestHex = Hex.encodeHex(encryptRequest);
220 return Utils.mergeIvWithData(ivHex, encryptRequestHex);
221 }
222
223 public static String generateIdfJSON(Object request)
224 throws IOException, DecoderException {
225
226 ObjectMapper mapper = new ObjectMapper();
227 String strRequest = mapper.writeValueAsString(request);
228
229 String dataRequest = generateRequestIdf(strRequest);
230
231 StringBuilder idfRequest = new StringBuilder();
232 idfRequest.append("{\"idf\":\"");
233 idfRequest.append(dataRequest);
234 idfRequest.append("\"}");
235
236 return idfRequest.toString();
237 }
238
239 public static String generateRandomIvBase64() {
240 SecureRandom random = new SecureRandom();
241 byte[] iv = new byte[128 / 8];
242 random.nextBytes(iv);
243 return Base64.getEncoder().encodeToString(iv);
244 }
245
246 public static byte[] encrypt(byte[] iv,
247 String strToEncrypt) {
248
249 SecretKeySpec secretKey;
250 try {
251 secretKey =
252 new SecretKeySpec(Base64.getDecoder().decode(FileUtils.getInstance().getKey().getEncoded()), "AES");
253 } catch (Exception e) {
254 throw new UserException("[encrypt]", e);
255 }
256
257 IvParameterSpec ivSpec = new IvParameterSpec(iv);
258 Cipher cipher;
259 byte[] encrypted;
260
261 try {
262 cipher = Cipher.getInstance("AES/GCM/PKCS5PADDING");
263 cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec);
264 encrypted = cipher.doFinal(strToEncrypt.getBytes());
265
266 } catch (Exception e) {
267 throw new UserException("[encrypt]", e);
268 }
269
270 return encrypted;
271 }
272
273 public static String mergeIvWithData(char[] iv,
274 char[] data)
275 throws DecoderException {
276 StringBuffer result = new StringBuffer();
277 result.append(iv);
278 result.append(data);
279 return Base64.getEncoder().encodeToString(Hex.decodeHex(result.toString()));
280 }
281
282
283 public static String concatenate(String... dataString) {
284
285 StringBuilder stringBuilder = new StringBuilder();
286 for (String data : dataString) {
287 stringBuilder.append(data);
288 }
289
290 return stringBuilder.toString();
291 }
292
293 public static <T> T mapToClass(String encrypt, Class<T> valueType) throws IOException, DecoderException {
294 return new ObjectMapper().readValue(
295 Base64.getDecoder().decode(getResponseFromIdf(encrypt)),
296 valueType);
297 }
298
299 public static <T> T mapToClass(String text, Class<T> valueType, boolean isEncoded) throws IOException, DecoderException {
300 if (!isEncoded) {
301 return new ObjectMapper().readValue(
302 Base64.getDecoder().decode(text),
303 valueType);
304 }
305 return mapToClass(text, valueType);
306 }
307
308 /**
309 * Lee y entrega el valor del IDF en formato de texto
310 *
311 * @param idfResponse IDF encriptado
312 * @return texto desenciptado
313 * @throws com.fasterxml.jackson.core.JsonProcessingException
314 */
315 public static String readIdfValue(String idfResponse) throws JsonProcessingException, ClaveUnicaClientException {
316 ObjectNode idfNode = new ObjectMapper().readValue(idfResponse, ObjectNode.class);
317
318 if (!idfNode.has("idf"))
319 throw new ClaveUnicaClientException("Bad response getUsers");
320
321 String encryptResponse = idfNode.get("idf").asText();
322
323 if (ObjectUtils.isEmpty(encryptResponse))
324 throw new ClaveUnicaClientException("Bad response getUsers");
325 return encryptResponse;
326 }
327
328 public static boolean isEmail(String user) {
329 Pattern emailRegex = Pattern.compile("^[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,6}$", Pattern.CASE_INSENSITIVE);
330 Matcher matcher = emailRegex.matcher(user);
331 return matcher.find();
332 }
333}
334