View Javadoc

1   /*
2    * EL4J, the Extension Library for the J2EE, adds incremental enhancements to
3    * the spring framework, http://el4j.sf.net
4    * Copyright (C) 2006 by ELCA Informatique SA, Av. de la Harpe 22-24,
5    * 1000 Lausanne, Switzerland, http://www.elca.ch
6    *
7    * EL4J is published under the GNU Lesser General Public License (LGPL)
8    * Version 2.1. See http://www.gnu.org/licenses/
9    *
10   * This program is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13   * GNU Lesser General Public License for more details.
14   *
15   * For alternative licensing, please contact info@elca.ch
16   */
17  package ch.elca.el4j.util.env;
18  
19  import java.util.Properties;
20  
21  import org.slf4j.Logger;
22  import org.slf4j.LoggerFactory;
23  import org.springframework.context.ApplicationContext;
24  import org.springframework.core.io.Resource;
25  
26  import ch.elca.el4j.core.context.ModuleApplicationContext;
27  import ch.elca.el4j.util.encryption.AbstractPropertyEncryptor;
28  import ch.elca.el4j.util.encryption.PasswordSource;
29  
30  /**
31   * Utility to load a custom key from file (cryptor.properties). This is in a
32   * separate class to make it accessible from Override- and
33   * Placeholder-configurer and avoid code duplication.
34   *
35   * @svnLink $Revision: 3883 $;$Date: 2009-08-04 15:35:01 +0200 (Di, 04. Aug 2009) $;$Author: swismer $;$URL: https://el4j.svn.sourceforge.net/svnroot/el4j/branches/el4j_3_1/el4j/framework/modules/env/src/main/java/ch/elca/el4j/util/env/PropertyEncryptionUtil.java $
36   *
37   * @author David Bernhard (DBD)
38   */
39  public class PropertyEncryptionUtil {
40  
41  	/**
42  	 * The logger.
43  	 */
44  	private static Logger s_logger = LoggerFactory.getLogger(ModuleApplicationContext.EL4J_DEBUGGING_LOGGER);
45  	
46  	/**
47  	 * The default cryptor configuration file.
48  	 */
49  	protected String m_defaultConfigFile = "classpath:cryptor.properties";
50   
51  	/**
52  	 * This is used to see if the internal cryptor class is available.
53  	 */
54  	private ClassLoader m_cl;
55  
56  	/**
57  	 * The classloader returns type Class which we save and cast later on.
58  	 */
59  	@SuppressWarnings("unchecked")
60  	private Class m_c;
61  
62  		/**
63  	 * Status of the encryption module. We need this because
64  	 * we cannot initialize in a constructor
65  	 * but must wait for spring to set a property in the caller.
66  	 * Therefore an init() method is provided.
67  	 */
68  	private enum CryptStatus {
69  		
70  		/** We have not called init() yet to check if we are in internal. */
71  		UNINITED,
72  		
73  		/** init() found no cryptor. */
74  		EXTERNAL,
75  		
76  		/** init found cryptor and all is ready. */
77  		ACTIVE,
78  		
79  		/** Turned off by deactivate(). */
80  		DEACTIVATED
81  	};
82  	
83  	/**
84  	 * Stores the current status.
85  	 */
86  	private CryptStatus m_status;
87  
88  	/**
89  	 * The cryptor object which provides encryption.
90  	 */
91  	private AbstractPropertyEncryptor m_cryptor;
92  
93  	/**
94  	 * An optional source for a true custom password.
95  	 */
96  	private PasswordSource m_source;
97  
98  	/**
99  	 * Flag to indicate whether m_source is used.
100 	 */
101 	private boolean m_useSource = false;
102 
103 	/**
104 	 * Constructor. We cannot do init() here due to the caller not having all
105 	 * properties at construction time.
106 	 */
107 	public PropertyEncryptionUtil() {
108 		m_status = CryptStatus.UNINITED;
109 	}
110 
111 	/**
112 	 * @return Whether init() has been called.
113 	 */
114 	public boolean isInited() {
115 		return !m_status.equals(CryptStatus.UNINITED);
116 	}
117 
118 	/**
119 	 * @return Whether we can do encryption.
120 	 */
121 	public boolean isActive() {
122 		return m_status.equals(CryptStatus.ACTIVE);
123 	}
124 
125 	/**
126 	 * Deactivate encryption features.
127 	 */
128 	public void deactivate() {
129 		m_status = CryptStatus.DEACTIVATED;
130 	}
131 
132 	/**
133 	 * Set up encryption. We need to pass ctx to access resources.
134 	 * @param ctx ModuleApplicationContext
135 	 */
136 	public void init(ApplicationContext ctx) {
137 		init(ctx, m_defaultConfigFile);
138 	}
139 
140 	/**
141 	 * @param ctx ModuleApplicationContext
142 	 * @param configFile Custom config file location.
143 	 */
144 	public void init(ApplicationContext ctx, String configFile) {
145 
146 		if (m_status != CryptStatus.UNINITED) {
147 			throw new RuntimeException(
148 				"You cannot call init() more than once.");
149 		}
150 
151 		/* Check whether we are in internal. */
152 		m_cl = Thread.currentThread().getContextClassLoader();
153 		try {
154 			m_c = m_cl
155 				.loadClass("ch.elca.el4j.services.encryption.PropertyEncryptor");
156 			m_cryptor = (AbstractPropertyEncryptor) m_c.newInstance();
157 		} catch (ClassNotFoundException e) {
158 			// An exception landing us here means we are in external.
159 			m_status = CryptStatus.EXTERNAL;
160 			return;
161 		} catch (Exception e) {
162 			throw new RuntimeException("Error initializing cryptor.");
163 		}
164 		
165 		/*
166 		 * We have a cryptor - set it up. Read password to use from
167 		 * configuration file.
168 		 */
169 
170 		m_status = CryptStatus.ACTIVE;
171 
172 		s_logger.info("Trying to read cryptor config file: " + configFile);
173 
174 		Properties p = new Properties();
175 		Resource res = ctx.getResource(configFile);
176 		if (!res.exists()) {
177 			s_logger
178 				.error("The config file " + configFile + " does not exist.");
179 		}
180 
181 		try {
182 			//File file = res.getFile();
183 			p.load(res.getInputStream());
184 		} catch (Exception e) {
185 			s_logger.error("Config file " + configFile + " is not accessible.");
186 			e.printStackTrace();
187 		}
188 
189 		if (p.containsKey("cryptor.passwordSource")
190 			&& p.containsKey("cryptor.customPassword")) {
191 			String source = p.getProperty("cryptor.passwordSource");
192 			String custom = p.getProperty("cryptor.customPassword");
193 
194 			try {
195 				if (source.equals("mixed")) {
196 					String key = m_cryptor.decrypt(custom);
197 					m_cryptor.deriveKey(key);
198 				} else if (source.equals("custom")) {
199 					if (m_useSource) {
200 						m_cryptor.deriveKey(this.m_source.getPassword());
201 					} else {
202 						// If no source defined but using a custom password,
203 						// error.
204 						s_logger.error("Internal password mode set to custom"
205 							+ "but no passwordSource defined.");
206 					}
207 				}
208 				s_logger.info("Success reading file.");
209 			} catch (Exception e) {
210 				s_logger.error("Error reading config file.");
211 			}
212 		} else {
213 			s_logger.error("Config file must contain entries "
214 				+ "cryptor.passwordSource and " + "cryptor.customPassword.");
215 		}
216 	}
217 
218 	/**
219 	 * Called to get the cryptor object managed by this class.
220 	 * @return An initialized cryptor.
221 	 */
222 	public AbstractPropertyEncryptor getCryptor() {
223 		if (!isActive()) {
224 			throw new RuntimeException("You can only get a cryptor in the "
225 				+ "ACTIVE state.");
226 		}
227 		return m_cryptor;
228 	}
229 
230 	/**
231 	 * @param source A custom PasswordSource
232 	 */
233 	public void setSource(PasswordSource source) {
234 		// As we might not be init()ed yet, just save the source -
235 		// and set a flag.
236 		this.m_source = source;
237 		m_useSource = true;
238 	}
239 
240 }