Java tutorial
/** * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE, version 2.1, dated February 1999. * * This program is free software; you can redistribute it and/or modify * it under the terms of the latest version of the GNU Lesser General * Public License as published by the Free Software Foundation; * * This program 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program (LICENSE.txt); if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package org.jamwiki.utils; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.security.GeneralSecurityException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Properties; import javax.crypto.Cipher; import javax.crypto.SecretKey; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.DESKeySpec; import org.apache.commons.codec.binary.Base64; import org.apache.commons.lang.StringUtils; import org.jamwiki.Environment; /** * Provide capability for encrypting and decrypting values. Inspired by an * example from http://www.devx.com/assets/sourcecode/10387.zip. */ public class Encryption { private static final WikiLogger logger = WikiLogger.getLogger(Encryption.class.getName()); public static final String DES_ALGORITHM = "DES"; public static final String ENCRYPTION_KEY = "JAMWiki Key 12345"; /** * Hide the constructor by making it private. */ private Encryption() { } /** * Encrypt a String value using the DES encryption algorithm. * * @param unencryptedBytes * The unencrypted String value that is to be encrypted. * @return An encrypted version of the String that was passed to this method. */ private static String encrypt64(byte[] unencryptedBytes) throws GeneralSecurityException, UnsupportedEncodingException { if (unencryptedBytes == null || unencryptedBytes.length == 0) { throw new IllegalArgumentException("Cannot encrypt a null or empty byte array"); } SecretKey key = createKey(); Cipher cipher = Cipher.getInstance(key.getAlgorithm()); cipher.init(Cipher.ENCRYPT_MODE, key); byte[] encryptedBytes = Base64.encodeBase64(cipher.doFinal(unencryptedBytes)); return bytes2String(encryptedBytes); } /** * */ public static String encrypt(String unencryptedString) { if (StringUtils.isBlank(unencryptedString)) { throw new IllegalArgumentException("Cannot encrypt a null or empty string"); } MessageDigest md = null; String encryptionAlgorithm = Environment.getValue(Environment.PROP_ENCRYPTION_ALGORITHM); try { md = MessageDigest.getInstance(encryptionAlgorithm); } catch (NoSuchAlgorithmException e) { logger.warning("JDK does not support the " + encryptionAlgorithm + " encryption algorithm. Weaker encryption will be attempted."); } if (md == null) { // fallback to weaker encryption algorithm if nothing better is available try { md = MessageDigest.getInstance("SHA-1"); } catch (NoSuchAlgorithmException e) { throw new UnsupportedOperationException( "JDK does not support the SHA-1 or SHA-512 encryption algorithms"); } // save the algorithm so that if the user upgrades the JDK they can // still use passwords encrypted with the weaker algorithm Environment.setValue(Environment.PROP_ENCRYPTION_ALGORITHM, "SHA-1"); // trY { // ENVIRONMENT.SAVEPROPERTIES(); // } CATCH (IOEXCEPTION E) { // LOGGER.INFO("FAILURE WHILE SAVING ENCRYPTION ALGORITHM PROPERTY", E); // } } try { md.update(unencryptedString.getBytes("UTF-8")); byte raw[] = md.digest(); return encrypt64(raw); } catch (GeneralSecurityException e) { logger.severe("Encryption failure", e); throw new IllegalStateException("Failure while encrypting value"); } catch (UnsupportedEncodingException e) { // this should never happen throw new IllegalStateException("Unsupporting encoding UTF-8"); } } /** * Unencrypt a String value using the DES encryption algorithm. * * @param encryptedString * The encrypted String value that is to be unencrypted. * @return An unencrypted version of the String that was passed to this * method. */ private static String decrypt64(String encryptedString) throws GeneralSecurityException, UnsupportedEncodingException { if (StringUtils.isBlank(encryptedString)) { return encryptedString; } SecretKey key = createKey(); Cipher cipher = Cipher.getInstance(key.getAlgorithm()); cipher.init(Cipher.DECRYPT_MODE, key); byte[] encryptedBytes = encryptedString.getBytes("UTF8"); byte[] unencryptedBytes = cipher.doFinal(Base64.decodeBase64(encryptedBytes)); return bytes2String(unencryptedBytes); } /** * Convert a byte array to a String value. * * @param bytes * The byte array that is to be converted. * @return A String value created from the byte array that was passed to this * method. */ private static String bytes2String(byte[] bytes) { StringBuilder buffer = new StringBuilder(); for (int i = 0; i < bytes.length; i++) { buffer.append((char) bytes[i]); } return buffer.toString(); } /** * Create the encryption key value. * * @return An encryption key value implementing the DES encryption algorithm. */ private static SecretKey createKey() throws GeneralSecurityException, UnsupportedEncodingException { byte[] bytes = ENCRYPTION_KEY.getBytes("UTF8"); DESKeySpec spec = new DESKeySpec(bytes); SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(DES_ALGORITHM); return keyFactory.generateSecret(spec); } /** * If a property value is encrypted, return the unencrypted value. Note that * if this method finds an un-encrypted value it will automatically encrypt it * and re-save it to the property file. * * @param name * The name of the encrypted property being retrieved. * @return The unencrypted value of the property. */ public static String getEncryptedProperty(String name, Properties props) { try { if (props != null) { return Encryption.decrypt64(props.getProperty(name)); } return Encryption.decrypt64(Environment.getValue(name)); } catch (GeneralSecurityException e) { String value = Environment.getValue(name); if (props != null || StringUtils.isBlank(value)) { logger.severe("Encryption failure or no value available for property: " + name, e); throw new IllegalStateException("Failure while retrieving encrypted property: " + name); } // the property might have been unencrypted in the property file, so // encrypt, save, and return the value logger.warning("Found unencrypted property file value: " + name + ". Assuming that this value manually un-encrypted in the property file so re-encrypting and re-saving."); Encryption.setEncryptedProperty(name, value, null); // try { // Environment.saveProperties(); // } catch (IOException ex) { // logger.severe("Failure while saving properties", ex); // throw new IllegalStateException("Failure while saving properties"); // } return value; } catch (UnsupportedEncodingException e) { throw new IllegalStateException("Unsupporting encoding UTF-8"); } } /** * Encrypt and set a property value. * * @param name * The name of the encrypted property being retrieved. * @param value * The unenencrypted value of the property. * @param props * The property object in which the property is being set. */ public static void setEncryptedProperty(String name, String value, Properties props) { String encrypted = ""; if (!StringUtils.isBlank(value)) { byte[] unencryptedBytes = null; try { unencryptedBytes = value.getBytes("UTF8"); encrypted = Encryption.encrypt64(unencryptedBytes); } catch (GeneralSecurityException e) { logger.severe("Encryption failure", e); throw new IllegalStateException("Failure while encrypting value"); } catch (UnsupportedEncodingException e) { // this should never happen throw new IllegalStateException("Unsupporting encoding UTF-8"); } } if (props == null) { Environment.setValue(name, encrypted); } else { props.setProperty(name, encrypted); } } }