001: /**
002: * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE, version 2.1, dated February 1999.
003: *
004: * This program is free software; you can redistribute it and/or modify
005: * it under the terms of the latest version of the GNU Lesser General
006: * Public License as published by the Free Software Foundation;
007: *
008: * This program is distributed in the hope that it will be useful,
009: * but WITHOUT ANY WARRANTY; without even the implied warranty of
010: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
011: * GNU Lesser General Public License for more details.
012: *
013: * You should have received a copy of the GNU Lesser General Public License
014: * along with this program (LICENSE.txt); if not, write to the Free Software
015: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
016: */package org.jamwiki.utils;
017:
018: import java.io.UnsupportedEncodingException;
019: import java.security.MessageDigest;
020: import java.security.NoSuchAlgorithmException;
021: import java.util.Properties;
022: import javax.crypto.Cipher;
023: import javax.crypto.SecretKey;
024: import javax.crypto.SecretKeyFactory;
025: import javax.crypto.spec.DESKeySpec;
026: import org.apache.commons.codec.binary.Base64;
027: import org.apache.commons.lang.StringUtils;
028: import org.jamwiki.Environment;
029:
030: /**
031: * Provide capability for encrypting and decrypting values. Inspired by an
032: * example from http://www.devx.com/assets/sourcecode/10387.zip.
033: */
034: public class Encryption {
035:
036: private static final WikiLogger logger = WikiLogger
037: .getLogger(Encryption.class.getName());
038: public static final String DES_ALGORITHM = "DES";
039: public static final String ENCRYPTION_KEY = "JAMWiki Key 12345";
040:
041: /**
042: * Hide the constructor by making it private.
043: */
044: private Encryption() {
045: }
046:
047: /**
048: * Encrypt a String value using the DES encryption algorithm.
049: *
050: * @param unencryptedString The unencrypted String value that is to be encrypted.
051: * @return An encrypted version of the String that was passed to this method.
052: */
053: public static String encrypt64(String unencryptedString)
054: throws Exception {
055: if (StringUtils.isBlank(unencryptedString)) {
056: return unencryptedString;
057: }
058: byte[] unencryptedBytes = unencryptedString.getBytes("UTF8");
059: return encrypt64(unencryptedBytes);
060: }
061:
062: /**
063: * Encrypt a String value using the DES encryption algorithm.
064: *
065: * @param unencryptedBytes The unencrypted String value that is to be encrypted.
066: * @return An encrypted version of the String that was passed to this method.
067: */
068: public static String encrypt64(byte[] unencryptedBytes)
069: throws Exception {
070: try {
071: SecretKey key = createKey();
072: Cipher cipher = Cipher.getInstance(key.getAlgorithm());
073: cipher.init(Cipher.ENCRYPT_MODE, key);
074: byte[] encryptedBytes = Base64.encodeBase64(cipher
075: .doFinal(unencryptedBytes));
076: return bytes2String(encryptedBytes);
077: } catch (Exception e) {
078: logger.severe("Encryption error while processing value '"
079: + bytes2String(unencryptedBytes) + "'", e);
080: throw e;
081: }
082: }
083:
084: /**
085: *
086: */
087: public static String encrypt(String unencryptedString)
088: throws Exception {
089: MessageDigest md = null;
090: String encryptionAlgorithm = Environment
091: .getValue(Environment.PROP_ENCRYPTION_ALGORITHM);
092: try {
093: md = MessageDigest.getInstance(encryptionAlgorithm);
094: } catch (NoSuchAlgorithmException e) {
095: logger
096: .warning("JDK does not support the "
097: + encryptionAlgorithm
098: + " encryption algorithm. Weaker encryption will be attempted.");
099: }
100: if (md == null) {
101: // fallback to weaker encryption algorithm if nothing better is available
102: try {
103: md = MessageDigest.getInstance("SHA-1");
104: } catch (NoSuchAlgorithmException e) {
105: logger
106: .severe("JDK does not support either the SHA-1 or SHA-512 encryption algorithms.");
107: throw e;
108: }
109: try {
110: // save the algorithm so that if the user upgrades the JDK they can
111: // still use passwords encrypted with the weaker algorithm
112: Environment.setValue(
113: Environment.PROP_ENCRYPTION_ALGORITHM, "SHA-1");
114: Environment.saveProperties();
115: } catch (Exception e) {
116: logger
117: .info(
118: "Failure while saving encryption algorithm property",
119: e);
120: }
121: }
122: try {
123: md.update(unencryptedString.getBytes("UTF-8"));
124: } catch (UnsupportedEncodingException e) {
125: // this should never happen
126: logger.severe("Unsupporting encoding UTF-8");
127: throw e;
128: }
129: byte raw[] = md.digest();
130: return encrypt64(raw);
131: }
132:
133: /**
134: * Unencrypt a String value using the DES encryption algorithm.
135: *
136: * @param encryptedString The encrypted String value that is to be unencrypted.
137: * @return An unencrypted version of the String that was passed to this method.
138: */
139: public static String decrypt64(String encryptedString) {
140: if (encryptedString == null
141: || encryptedString.trim().length() <= 0) {
142: return encryptedString;
143: }
144: try {
145: SecretKey key = createKey();
146: Cipher cipher = Cipher.getInstance(key.getAlgorithm());
147: cipher.init(Cipher.DECRYPT_MODE, key);
148: byte[] encryptedBytes = encryptedString.getBytes("UTF8");
149: byte[] unencryptedBytes = cipher.doFinal(Base64
150: .decodeBase64(encryptedBytes));
151: return bytes2String(unencryptedBytes);
152: } catch (Exception e) {
153: logger.severe("Decryption error while processing value '"
154: + encryptedString + "'", e);
155: // FIXME - should this throw the exception - caues issues upstream.
156: return null;
157: }
158: }
159:
160: /**
161: * Convert a byte array to a String value.
162: *
163: * @param bytes The byte array that is to be converted.
164: * @return A String value created from the byte array that was passed to this method.
165: */
166: private static String bytes2String(byte[] bytes) {
167: StringBuffer buffer = new StringBuffer();
168: for (int i = 0; i < bytes.length; i++) {
169: buffer.append((char) bytes[i]);
170: }
171: return buffer.toString();
172: }
173:
174: /**
175: * Create the encryption key value.
176: *
177: * @return An encryption key value implementing the DES encryption algorithm.
178: */
179: private static SecretKey createKey() throws Exception {
180: byte[] bytes = ENCRYPTION_KEY.getBytes("UTF8");
181: DESKeySpec spec = new DESKeySpec(bytes);
182: SecretKeyFactory keyFactory = SecretKeyFactory
183: .getInstance(DES_ALGORITHM);
184: return keyFactory.generateSecret(spec);
185: }
186:
187: /**
188: * If a property value is encrypted, return the unencrypted value.
189: *
190: * @param name The name of the encrypted property being retrieved.
191: * @return The unencrypted value of the property.
192: */
193: public static String getEncryptedProperty(String name,
194: Properties props) {
195: if (props != null) {
196: return Encryption.decrypt64(props.getProperty(name));
197: }
198: return Encryption.decrypt64(Environment.getValue(name));
199: }
200:
201: /**
202: * Encrypt and set a property value.
203: *
204: * @param name The name of the encrypted property being retrieved.
205: * @param value The enencrypted value of the property.
206: * @param props The property object in which the property is being set.
207: */
208: public static void setEncryptedProperty(String name, String value,
209: Properties props) throws Exception {
210: String encrypted = Encryption.encrypt64(value);
211: if (encrypted == null) {
212: encrypted = "";
213: }
214: if (props == null) {
215: Environment.setValue(name, encrypted);
216: } else {
217: props.setProperty(name, encrypted);
218: }
219: }
220: }
|