Java tutorial
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> </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; } }