All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.authy.AuthyUtil Maven / Gradle / Ivy

package com.authy;

import org.json.JSONArray;
import org.json.JSONObject;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;

import static java.nio.charset.StandardCharsets.UTF_8;

/**
 * @author hansospina
 *         

* Copyright © 2017 Twilio, Inc. All Rights Reserved. */ public class AuthyUtil { public static final String HEADER_AUTHY_SIGNATURE_NONCE = "X-Authy-Signature-Nonce"; public static final String HEADER_AUTHY_SIGNATURE = "X-Authy-Signature"; private static final Logger LOGGER = Logger.getLogger(AuthyUtil.class.getName()); private static String hmacSha(String KEY, String VALUE) throws OneTouchException { try { SecretKeySpec signingKey = new SecretKeySpec(KEY.getBytes(UTF_8), "HmacSHA256"); Mac mac = Mac.getInstance("HmacSHA256"); mac.init(signingKey); byte[] rawHmac = mac.doFinal(VALUE.getBytes(UTF_8)); return DatatypeConverter.printBase64Binary(rawHmac); } catch (Exception ex) { // capture the exceptions and wrap them using authy. throw new OneTouchException("There was an exception checking the Authy OneTouch signature.", ex); } } /** * Validates the request information to * * @param parameters The request parameters(all of them) * @param headers The headers of the request * @param url The url of the request. * @param apiKey the security token from the authy library * @return true if the signature ios valid, false otherwise * @throws UnsupportedEncodingException if the string parameters have problems with UTF-8 encoding. */ private static boolean validateSignature(Map parameters, Map headers, String method, String url, String apiKey) throws OneTouchException, UnsupportedEncodingException { if (headers == null) throw new OneTouchException("No headers sent"); if (!headers.containsKey(HEADER_AUTHY_SIGNATURE)) throw new OneTouchException("'SIGNATURE' is missing."); if (!headers.containsKey(HEADER_AUTHY_SIGNATURE_NONCE)) throw new OneTouchException("'NONCE' is missing."); if (parameters == null || parameters.isEmpty()) throw new OneTouchException("'PARAMS' are missing."); StringBuilder sb = new StringBuilder(headers.get(HEADER_AUTHY_SIGNATURE_NONCE)) .append("|") .append(method) .append("|") .append(url) .append("|") .append(mapToQuery(parameters)); String signature = hmacSha(apiKey, sb.toString()); // let's check that the Authy signature is valid return signature.equals(headers.get(HEADER_AUTHY_SIGNATURE)); } public static void extract(String pre, JSONObject obj, HashMap map) { for (String k : obj.keySet()) { String key = pre.length() == 0 ? k : pre + "[" + k + "]"; if (obj.optJSONObject(k) != null) { extract(key, obj.getJSONObject(k), map); } else if (obj.optJSONArray(k) != null) { JSONArray arr = obj.getJSONArray(k); int i = 0; for (Object tmp : arr) { String tmpKey = key + "[" + i + "]"; if (tmp instanceof JSONObject) { extract(tmpKey, (JSONObject) tmp, map); } else { map.put(tmpKey, getValue(tmp)); } i++; } } else { map.put(key, getValue(obj.get(k))); } } } private static String getValue(Object val) { if (val instanceof Boolean) { return Boolean.toString(((Boolean) val)); } else if (val instanceof Integer) { return Long.toString(((Integer) val)); } else if (val instanceof Long) { return Long.toString(((Long) val)); } else if (val instanceof Float) { return Float.toString(((Float) val)); } else if (val instanceof Double) { return Double.toString(((Double) val)); } else if (JSONObject.NULL.equals(val)) { return ""; } else { return String.valueOf(val); } } public static String mapToQuery(Map map) throws OneTouchException, UnsupportedEncodingException { StringBuilder sb = new StringBuilder(); SortedSet keys = new TreeSet<>(map.keySet()); boolean first = true; for (String key : keys) { if (key.length() > 200) throw new OneTouchException("max number of characters of key exceeded."); if (first) { first = false; } else { sb.append("&"); } String value = map.get(key); // don't encode null values if (value == null) { continue; } sb.append(URLEncoder.encode(key.replaceAll("\\[([0-9])*\\]", "[]"), UTF_8.name())).append("=").append(URLEncoder.encode(value, UTF_8.name())); } return sb.toString(); } /** * Validates the request information to * * @param body The body of the request in case of a POST method * @param headers The headers of the request * @param url The url of the request. * @param apiKey the security token from the authy library * @return true if the signature ios valid, false otherwise * @throws com.authy.OneTouchException * @throws UnsupportedEncodingException if the string parameters have problems with UTF-8 encoding. */ public static boolean validateSignatureForPost(String body, Map headers, String url, String apiKey) throws OneTouchException, UnsupportedEncodingException { HashMap params = new HashMap<>(); if (body == null || body.isEmpty()) throw new OneTouchException("'PARAMS' are missing."); extract("", new JSONObject(body), params); return validateSignature(params, headers, "POST", url, apiKey); } /** * Validates the request information to * * @param params The query parameters in case of a GET request * @param headers The headers of the request * @param url The url of the request. * @param apiKey the security token from the authy library * @return true if the signature ios valid, false otherwise * @throws com.authy.OneTouchException * @throws UnsupportedEncodingException if the string parameters have problems with UTF-8 encoding. */ public static boolean validateSignatureForGet(Map params, Map headers, String url, String apiKey) throws OneTouchException, UnsupportedEncodingException { return validateSignature(params, headers, "GET", url, apiKey); } /** * Loads your api_key and api_url properties from the given property file *

* Two important things to have in mind here: * 1) if api_key and api_url are defined as environment variables, they will be returned as the properties. * 2) If you want to load your properties file have in mind your classloader path may change. * * @return the Properties object containing the properties to setup Authy or an empty Properties object if no properties were found */ public static Properties loadProperties(String path, Class cls) { Properties properties = new Properties(); // environment variables will always override properties file try { InputStream in = cls.getResourceAsStream(path); // if we cant find the properties file if (in != null) { properties.load(in); } // Env variables will always override properties if (System.getenv("api_key") != null && System.getenv("api_url") != null) { properties.put("api_key", System.getenv("api_key")); properties.put("api_url", System.getenv("api_url")); } } catch (IOException e) { LOGGER.log(Level.SEVERE, "Problems loading properties", e); } return properties; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy