Java tutorial
/* * Copyright (c) 2012. The Genome Analysis Centre, Norwich, UK * MISO project contacts: Robert Davey @ TGAC * ********************************************************************* * * This file is part of MISO. * * MISO is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * MISO is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with MISO. If not, see <http://www.gnu.org/licenses/>. * * ********************************************************************* */ package uk.ac.bbsrc.tgac.miso.integration.util; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.SignatureException; import java.util.Arrays; import java.util.Enumeration; import java.util.List; import java.util.Map; import java.util.TreeMap; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import javax.servlet.http.HttpServletRequest; import org.apache.commons.codec.binary.Base64; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * uk.ac.bbsrc.tgac.miso.webapp.context * <p/> * Info * * @author Rob Davey * @date 09/02/12 * @since 0.1.6 */ public class SignatureHelper { private static final Logger log = LoggerFactory.getLogger(SignatureHelper.class); public static final String PUBLIC_KEY = "MIIBtzCCASwGByqGSM44BAEwggEfAoGBAP1/U4EddRIpUt9KnC7s5Of2EbdSPO9EAMMeP4C2USZp;RV1AIlH7WT2NWPq/xfW6MPbLm1Vs14E7gB00b/JmYLdrmVClpJ+f6AR7ECLCT7up1/63xhv4O1fnxqimFQ8E+4P208UewwI1VBNaFpEy9nXzrith1yrv8iIDGZ3RSAHHAhUAl2BQjxUjC8yykrmCouuEC/BYHPUCgYEA9+GghdabPd7LvKtcNrhXuXmUr7v6OuqC+VdMCz0HgmdRWVeOutRZT+ZxBxCBgLRJFnEj6EwoFhO3zwkyjMim4TwWeotUfI0o4KOuHiuzpnWRbqN/C/ohNWLx+2J6ASQ7zKTxvqhRkImog9/hWuWfBpKLZl6Ae1UlZAFMO/7PSSoDgYQAAoGARBu0g4MdHVhU6NoSXMKDBFSX9KfkTwIOXM6GY3DhAWsQhejkAkxp8c0IpkKn+i+PQNM/2pntXLWxDGHQGhfJIwvP041SrRTCXtx8SJ59ima8Z6/my7N72pPvbeDcPjlshtp/oa6eHh9M4J18W5hI4HD6I6f4qnppP1rRYaZolhw="; public static final String PRIVATE_KEY = "MIIBSwIBADCCASwGByqGSM44BAEwggEfAoGBAP1/U4EddRIpUt9KnC7s5Of2EbdSPO9EAMMeP4C2USZpRV1AIlH7WT2NWPq/xfW6MPbLm1Vs14E7gB00b/JmYLdrmVClpJ+f6AR7ECLCT7up1/63xhv4O1fnxqimFQ8E+4P208UewwI1VBNaFpEy9nXzrith1yrv8iIDGZ3RSAHHAhUAl2BQjxUjC8yykrmCouuEC/BYHPUCgYEA9+GghdabPd7LvKtcNrhXuXmUr7v6OuqC+VdMCz0HgmdRWVeOutRZT+ZxBxCBgLRJFnEj6EwoFhO3zwkyjMim4TwWeotUfI0o4KOuHiuzpnWRbqN/C/ohNWLx+2J6ASQ7zKTxvqhRkImog9/hWuWfBpKLZl6Ae1UlZAFMO/7PSSoEFgIUOCrAiHXm+FJBM7QHMhBxanPAn3k="; public static final String USER_HEADER = "x-user"; public static final String TIMESTAMP_HEADER = "x-timestamp"; public static final String SIGNATURE_HEADER = "x-signature"; public static final String URL_X_HEADER = "x-url"; private static final List<String> SIGNATURE_KEYWORDS = Arrays.asList(USER_HEADER, TIMESTAMP_HEADER, URL_X_HEADER); private static final String DSA_ALGORITHM = "DSA"; private static final String HMAC_SHA1_ALGORITHM = "HmacSHA1"; public static String createSignature(Map<String, List<String>> headersAndParams, String url, String privateKey) throws Exception { TreeMap<String, String> sortedHeaders = new TreeMap<>(); for (String key : headersAndParams.keySet()) { if (SIGNATURE_KEYWORDS.contains(key)) { sortedHeaders.put(key, headersAndParams.get(key).get(0)); } } String sortedUrl = createSortedUrl(url, sortedHeaders); log.debug("CREATING SIGNATURE FROM SUPPLIED HEADERS: " + sortedUrl + " :: " + privateKey); return calculateHMAC(sortedUrl, privateKey); } private static String createSignatureFromRequest(HttpServletRequest request, String privateKey) throws Exception { String sortedUrl = createSortedUrl(request); log.debug("CREATING SIGNATURE FROM REQUEST: " + sortedUrl + " :: " + privateKey); return calculateHMAC(sortedUrl, privateKey); } private static String createSortedUrl(String url, TreeMap<String, String> headersAndParams) { // build the url with headers and params sorted String params = ""; for (String key : headersAndParams.keySet()) { if (params.length() > 0) { params += "@"; } params += key + "=" + headersAndParams.get(key).toString(); } if (!url.endsWith("?") && !"".equals(params)) url += "?"; log.debug("COMPLETE URL: " + url + params); return url + params; } private static String createSortedUrl(HttpServletRequest request) { // use a TreeMap to sort the headers and parameters TreeMap<String, String> headersAndParams = new TreeMap<>(); // load header values we care about @SuppressWarnings("unchecked") Enumeration<String> e = request.getHeaderNames(); while (e.hasMoreElements()) { String key = e.nextElement(); if (SIGNATURE_KEYWORDS.contains(key)) { log.debug("FOUND HEADER: " + key); headersAndParams.put(key, request.getHeader(key)); } } return createSortedUrl(request.getContextPath() + request.getServletPath() + request.getPathInfo(), headersAndParams); } private static String calculateHMAC(String data, String key) throws java.security.SignatureException { String result; try { // get an hmac_sha1 key from the raw key bytes SecretKeySpec signingKey = new SecretKeySpec(key.getBytes(), HMAC_SHA1_ALGORITHM); // get an hmac_sha1 Mac instance and initialize with the signing key Mac mac = Mac.getInstance(HMAC_SHA1_ALGORITHM); mac.init(signingKey); // compute the hmac on input data bytes byte[] rawHmac = mac.doFinal(data.getBytes()); // base64-encode the hmac result = Base64.encodeBase64URLSafeString(rawHmac); } catch (Exception e) { log.error("failed to generate HMAC", e); throw new SignatureException("Failed to generate HMAC : " + e.getMessage()); } return result; } public static boolean validateSignature(HttpServletRequest request, String publicKey, String signature) throws InvalidKeyException, Exception { if (request == null || signature == null || publicKey == null) { throw new InvalidKeyException("Cannot verify signature when request or signature are null!"); } log.debug("VERIFYING HMAC: " + signature + " vs " + createSignatureFromRequest(request, publicKey)); return signature.equals(createSignatureFromRequest(request, publicKey)); } public static String generatePrivateUserKey(byte[] data) throws NoSuchAlgorithmException { SecretKeySpec signingKey = new SecretKeySpec(data, DSA_ALGORITHM); return Base64.encodeBase64URLSafeString(signingKey.getEncoded()); } }