utils.Hash.java Source code

Java tutorial

Introduction

Here is the source code for utils.Hash.java

Source

package utils;

import java.math.BigInteger;
import java.nio.charset.Charset;
import java.security.GeneralSecurityException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.Cookie;

import org.apache.log4j.Logger;
import org.apache.commons.codec.binary.Base64;

/**
 * Class used for miscellaneous Hash use
 * <br/><br/>
 * This file is part of the Security Shepherd Project.
 * 
 * The Security Shepherd project 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.<br/>
 * 
 * The Security Shepherd project 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.<br/>
 * 
 * You should have received a copy of the GNU General Public License
 * along with the Security Shepherd project.  If not, see <http://www.gnu.org/licenses/>. 
 * @author Mark Denihan
 *
 */
public class Hash {
    private static org.apache.log4j.Logger log = Logger.getLogger(Hash.class);
    public static String userNameKey = randomKeyLengthString();
    private static String encryptionKeySalt = randomKeyLengthString();
    private static String serverEncryptionKey = randomKeyLengthString();

    /**
     * Merges current server encryption key with user name based encryption key to create user specific key
     * @param userName
     * @return
     */
    private static String createUserSpecificEncryptionKey(String userNameKey) throws Exception {
        if (userNameKey.length() != 16)
            throw new Exception("User Name key must be 16 bytes long");
        else {
            byte[] serverKey = serverEncryptionKey.getBytes();
            byte[] userKey = userNameKey.getBytes();
            for (int i = 0; i < userKey.length; i++) {
                userKey[i] = (byte) (userKey[i] + serverKey[i]);
            }
            return new String(userKey, Charset.forName("US-ASCII"));
        }
    }

    /**
     * Decrypts data using specific key and ciphertext
     * @param key Encryption Key (Must be 16 Bytes)
     * @param encrypted Ciphertext to decrypt
     * @return Plaintext decrypted from submitted ciphertext and key
     * @throws GeneralSecurityException
     */
    public static String decrypt(String key, String encrypted) throws GeneralSecurityException {
        byte[] raw = key.getBytes(Charset.forName("US-ASCII"));
        if (raw.length != 16) {
            throw new IllegalArgumentException("Invalid key size.");
        }
        SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, skeySpec, new IvParameterSpec(new byte[16]));
        byte[] original = cipher.doFinal(Base64.decodeBase64(encrypted));
        return new String(original, Charset.forName("US-ASCII"));
    }

    /**
     * Specifically decrypts encrypted user names
     * @param encyptedUserName Encrypted user name
     * @return Decrypted User name
     */
    public static String decryptUserName(String encyptedUserName) {
        String decryptedUserName = new String();
        try {
            decryptedUserName = Hash.decrypt(Hash.userNameKey, encyptedUserName);
            log.debug("Decrypted user-name to: " + decryptedUserName);
        } catch (GeneralSecurityException e) {
            log.error("Could not decrypt user name: " + e.toString());
        }
        return decryptedUserName;
    }

    public static String decryptUserSpecificSolution(String userNameKey, String encryptedSolution)
            throws GeneralSecurityException, Exception {
        try {
            String key = createUserSpecificEncryptionKey(userNameKey);
            byte[] raw = key.getBytes(Charset.forName("US-ASCII"));
            if (raw.length != 16) {
                throw new IllegalArgumentException("Invalid key size.");
            }
            SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE, skeySpec, new IvParameterSpec(new byte[16]));
            byte[] original = cipher.doFinal(Base64.decodeBase64(encryptedSolution));
            return new String(original, Charset.forName("US-ASCII"));
        } catch (Exception e) {
            throw new Exception("Decryption Failure: Could not Craft User Key or Ciphertext was Bad");
        }
    }

    /**
     * Encrypts plain text into cipher text based on encryption key
     * @param key Encryption Key (Must be 16 Bytes)
     * @param value Plain text to encrypt
     * @return Cipher text based on plain text and key submitted
     * @throws GeneralSecurityException
     */
    public static String encrypt(String key, String value) throws GeneralSecurityException {
        byte[] raw = key.getBytes(Charset.forName("US-ASCII"));
        if (raw.length != 16) {
            throw new IllegalArgumentException("Invalid key size.");
        }
        SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, skeySpec, new IvParameterSpec(new byte[16]));
        return Base64.encodeBase64String(cipher.doFinal(value.getBytes(Charset.forName("US-ASCII"))));
    }

    /**
     * Generates user solution based on the user name stored in their encypted cookie
     * @param baseKey The stored result key for the module
     * @param cookies All of a users session cookies. The encrypted user name is pulled out from this array
     * @return User Specific Solution
     */
    public static String generateUserSolution(String baseKey, Cookie[] cookies) {
        log.debug("Getting user Specific key");
        String cookieName = "JSESSIONID3";
        Cookie myCookie = null;
        String toReturn = "Key Should be here! Please refresh the home page and try again!";
        log.debug("Looking for key cookie");
        if (cookies != null) {
            for (int i = 0; i < cookies.length; i++) {
                log.debug("Looking at: " + cookies[i].getName() + " = " + cookies[i].getValue());
                if (cookies[i].getName().equals(cookieName)) {
                    myCookie = cookies[i];
                    log.debug("Found Cookie with value: " + myCookie.getValue());
                    break;
                }
            }
            try {
                String decryptedUserName = Hash.decrypt(Hash.userNameKey, myCookie.getValue());
                log.debug("Decrypted UserName: " + decryptedUserName);
                String key = createUserSpecificEncryptionKey(Validate.validateEncryptionKey(decryptedUserName));
                toReturn = Hash.encrypt(key, baseKey + getCurrentSalt());
                log.debug("Returning: " + toReturn);
            } catch (Exception e) {
                log.error("Encryption Failure: " + e.toString());
                toReturn = "Key Should be here! Please refresh the home page and try again! If that doesn't work, sign in and out again!";
            }
        }
        return "<b style='word-wrap: break-word;'>" + toReturn + "</b>";
    }

    /**
     * Generates user specific solution based on the user name provided and server side encryption keys
     * @param baseKey The stored result key for the module
     * @param userSalt The User Specific Encryption Salt (Based on user name)
     * @return User Specific Solution
     */
    public static String generateUserSolution(String baseKey, String userSalt) {
        log.debug("Generating key for " + userSalt);
        String toReturn = "Key Should be here! Please refresh the home page and try again!";

        try {
            String key = createUserSpecificEncryptionKey(Validate.validateEncryptionKey(userSalt));
            String forLog = Hash.encrypt(key, baseKey + getCurrentSalt());
            toReturn = "<script>prepTooltips();prepClipboardEvents();</script>" + "<div class='input-group'>"
                    + "<textarea id='theKey' rows=2 style='height: 30px; display: inline-block; float: left; padding-right: 1em; overflow: hidden; width:85%'>"
                    + forLog + "</textarea>" + "<span class='input-group-button'>"
                    + "<button class='btn' type='button' data-clipboard-shepherd data-clipboard-target='#theKey' style='height: 30px;'>"
                    + "<img src='../js/clipboard-js/clippy.svg' width='14' alt='Copy to clipboard'>" + "</button>"
                    + "</span><p>&nbsp;</p>" + "</div>";

            log.debug("Returning: " + forLog);
        } catch (Exception e) {
            log.error("Encrypt Failure: " + e.toString());
            toReturn = "Key Should be here! Please refresh the home page and try again!";
        }
        return toReturn;
    }

    /**
     * This is used when encrypting/decrypting the salt. If this is bypassed characters can be lost in encryption process.
     * @return
     */
    public static String getCurrentSalt() {
        return Base64.encodeBase64String(encryptionKeySalt.getBytes());
    }

    /**
     * Outputs a MD5 digest
     * @param toHash String to hash
     * @return Hashed String
     */
    public static String md5ThisString(String toHash) {
        String hashed = null;
        byte[] byteArray = new byte[512];
        MessageDigest md;
        try {
            md = MessageDigest.getInstance("MD5");
            log.debug("Hashing Value With " + md.getAlgorithm());
            byteArray = toHash.getBytes();
            md.update(byteArray);
            byteArray = md.digest();
        } catch (NoSuchAlgorithmException e) {
            log.fatal("Could not Find MD5 Algorithm: " + e.toString());
        }
        hashed = new String(byteArray, Charset.forName("US-ASCII"));

        return hashed;
    }

    /**
     * Creates a psedorandom base64 string
     * @return Random String
     */
    public static String randomBase64String() {
        String result = new String();
        try {
            byte byteArray[] = new byte[256];
            SecureRandom psn1 = SecureRandom.getInstance("SHA1PRNG");

            Base64 base64 = new Base64();
            psn1.setSeed(psn1.nextLong());
            psn1.nextBytes(byteArray);
            result = new String(byteArray, Charset.forName("US-ASCII"));
            result = base64.encode(thisString(thisString(byteArray.toString())).getBytes()).toString();
            log.debug("Generated String = " + result);
        } catch (Exception e) {
            log.error("Random Number Error : " + e.toString());
        }
        return result;
    }

    public static String randomKeyLengthString() {
        String result = new String();
        try {
            byte byteArray[] = new byte[16];
            SecureRandom psn1 = SecureRandom.getInstance("SHA1PRNG");
            psn1.setSeed(psn1.nextLong());
            psn1.nextBytes(byteArray);
            result = new String(byteArray, Charset.forName("US-ASCII"));
            //log.debug("Generated Key = " + result);
            if (result.length() != 16) {
                log.error("Generated Key is the incorrect Length: Shortening ");
                result = result.substring(0, 15);
                if (result.length() != 16)
                    log.fatal("Encryption key length is Still not Right");
            }
        } catch (Exception e) {
            log.error("Random Number Error : " + e.toString());
        }
        return result;
    }

    /**
     * Creates a psedorandom string
     * @return Random String
     */
    public static String randomString() {
        String result = new String();
        try {
            byte byteArray[] = new byte[16];
            SecureRandom psn1 = SecureRandom.getInstance("SHA1PRNG");
            psn1.setSeed(psn1.nextLong());
            psn1.nextBytes(byteArray);
            BigInteger bigInt = new BigInteger(byteArray);
            result = bigInt.toString();
            log.debug("Generated String = " + result);

        } catch (Exception e) {
            log.error("Random Number Error : " + e.toString());
        }
        return result;
    }

    /**
     * Generates a small psedorandom string
     * @return Random String
     */
    public static String smallRandomString() {
        String result = new String();
        try {
            byte byteArray[] = new byte[4];
            SecureRandom psn1 = SecureRandom.getInstance("SHA1PRNG");
            psn1.setSeed(psn1.nextLong());
            psn1.nextBytes(byteArray);
            BigInteger bigInt = new BigInteger(byteArray);
            result = bigInt.toString();
            log.debug("Generated String = " + result);

        } catch (Exception e) {
            log.error("Random Number Error : " + e.toString());
        }
        return result;
    }

    /**
     * Outputs a SHA256 digest
     * @param toHash String to hash
     * @return Hashed string
     */
    public static String thisString(String toHash) {
        String hashed = null;
        byte[] byteArray = new byte[256];
        MessageDigest md;
        try {
            md = MessageDigest.getInstance("SHA");
            log.debug("Hashing Value With " + md.getAlgorithm());
            byteArray = toHash.getBytes();
            md.update(byteArray);
            byteArray = md.digest();
        } catch (NoSuchAlgorithmException e) {
            log.fatal("Could not Find SHA Algorithm: " + e.toString());
        }
        hashed = new String(byteArray, Charset.forName("US-ASCII"));

        return hashed;
    }

    public static String validateEncryptionKey(String userSalt) {
        String newKey = new String();
        int keySize = userSalt.length();
        if (keySize == 16) {
            //log.debug("Key Already Valid");
            newKey = userSalt;
        } else {
            if (keySize > 16) {
                //log.debug("Key too Long...");
                newKey = userSalt.substring(0, 16);
            } else // Shorter than 16
            {
                //log.debug("Key too Short...");
                newKey = userSalt;
                int howManyTimes = (16 / keySize) - 1;
                //log.debug("Repeating String " + howManyTimes + " times");
                for (int i = 0; i < howManyTimes; i++)
                    newKey += userSalt;
                keySize = newKey.length();
                int toAdd = 16 - keySize;
                //log.debug("Adding " + toAdd + " more characters");
                newKey = newKey.concat(userSalt.substring(0, toAdd));
            }
        }
        log.debug("Encryption key is '" + newKey + "'");
        return newKey;
    }

}