Java tutorial
/** * Copyright (c) 2009--2014 Red Hat, Inc. * * This software is licensed to you under the GNU General Public License, * version 2 (GPLv2). There is NO WARRANTY for this software, express or * implied, including the implied warranties of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2 * along with this software; if not, see * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. * * Red Hat trademarks are not licensed under GPLv2. No permission is * granted to use or replicate Red Hat trademarks that are incorporated * in this software or its documentation. */ package com.redhat.rhn.common.security; import com.redhat.rhn.common.conf.Config; import com.redhat.rhn.common.conf.ConfigDefaults; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.List; /** * SessionSwap, a class to handle secure data manipulations in a way * consistent with SessionSwap from the perl codebase. Effectively a * wrapper to make it a bit easier to exchange data with different * parts of our codebase that speak different languages. A session * swap token is basically a tuple of a certain form that contains N * pieces of hex data and a signature that is based on a shared * secret. Someday this should become a true HMAC, but for now, it is * an older algorithm. * * @version $Rev$ */ public class SessionSwap { private static Logger log = Logger.getLogger(SessionSwap.class); public static final char[] HEX_CHARS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; /** utility class, no public constructor */ private SessionSwap() { } /** given an array of strings, compute the hex session swap, which * contains both the original data and the 'signature'. so the * resulting string is encapsulated and can be passed around as * 'signed' data. * * @param in an array of strings, all of which must be valud hex * @return String of the signature, in the form "D1:D2:D3xHEX" * where D1... are the input data and HEX is the hex signature. */ public static String encodeData(String[] in) { for (int i = 0; i < in.length; i++) { if (!StringUtils.containsOnly(in[i], HEX_CHARS)) { throw new IllegalArgumentException( "encodeData input must be " + "lowercase hex, but wasn't: " + in[i]); } } String joined = StringUtils.join(in, ':'); String[] components = new String[] { joined, generateSwapKey(joined) }; return StringUtils.join(components, "x"); } /** * simple wrapper around encodeData(String[]) for easier consumption * @see SessionSwap#encodeData(String[]) encodeData * @param in The data to encode * @return The reulting session swap string. */ public static String encodeData(String in) { return encodeData(new String[] { in }); } /** given a session swap string, this will crack it open and * return the data. * @param in The session swap to inspect. * @return The data extracted from the session swap * @throws SessionSwapTamperException if the data was * tampered with, making it easy to use and trust */ public static String[] extractData(String in) { String[] splitResults = StringUtils.split(in, 'x'); String[] data = StringUtils.split(splitResults[0], ':'); String recomputedDigest = encodeData(data); if (recomputedDigest.equals(in)) { return data; } throw new SessionSwapTamperException(in); } /** * compute the md5sum of * key1:key2:(data):key3:key4. * @param data to compute * @return computed data */ public static String generateSwapKey(String data) { Config c = Config.get(); StringBuilder swapKey = new StringBuilder(20); swapKey.append(c.getString(ConfigDefaults.WEB_SESSION_SWAP_SECRET_1)); swapKey.append(":"); swapKey.append(c.getString(ConfigDefaults.WEB_SESSION_SWAP_SECRET_2)); swapKey.append(":"); swapKey.append(data); swapKey.append(":"); swapKey.append(c.getString(ConfigDefaults.WEB_SESSION_SWAP_SECRET_3)); swapKey.append(":"); swapKey.append(c.getString(ConfigDefaults.WEB_SESSION_SWAP_SECRET_4)); return computeMD5Hash(swapKey.toString()); } /** * compute md5sum for any arbitrary text * * @param text text to hash * @return md5 computed hash value */ public static String computeMD5Hash(String text) { // TODO This should be merged with the md5 method(s) // in HMAC MessageDigest digest = null; try { digest = MessageDigest.getInstance("MD5"); } catch (NoSuchAlgorithmException e) { // this really shouldn't happen. really. throw new IllegalArgumentException("Unable to instantiate MD5 " + "MessageDigest algorithm"); } digest.update(text.getBytes()); return HMAC.byteArrayToHex(digest.digest()); } /** * Takes an array of strings and SHA1 hashes the 'joined' results. * * This is a port of the RHN::SessionSwap:rhn_hmac_data method. * * @param text array to SHA1 hash * @return String of hex chars */ public static String rhnHmacData(List<String> text) { Config c = Config.get(); StringBuilder swapKey = new StringBuilder(20); if (log.isDebugEnabled()) { for (String tmp : text) { log.debug("val : " + tmp); } } swapKey.append(c.getString(ConfigDefaults.WEB_SESSION_SWAP_SECRET_4)); swapKey.append(c.getString(ConfigDefaults.WEB_SESSION_SWAP_SECRET_3)); swapKey.append(c.getString(ConfigDefaults.WEB_SESSION_SWAP_SECRET_2)); swapKey.append(c.getString(ConfigDefaults.WEB_SESSION_SWAP_SECRET_1)); String joinedText = StringUtils.join(text.iterator(), "\0"); if (log.isDebugEnabled()) { log.debug("Data : [" + joinedText + "]"); log.debug("Key : [" + swapKey + "]"); } String retval = HMAC.sha1(joinedText, swapKey.toString()); if (log.isDebugEnabled()) { log.debug("retval: " + retval); } return retval; } }