1 /**
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
20 package org.apache.wss4j.common.crypto;
21
22 import java.io.IOException;
23 import java.io.InputStream;
24 import java.lang.reflect.Constructor;
25 import java.util.Map;
26 import java.util.Properties;
27
28 import org.apache.wss4j.common.ext.WSSecurityException;
29 import org.apache.wss4j.common.util.Loader;
30
31 /**
32 * CryptoFactory.
33 */
34 public abstract class CryptoFactory {
35 private static final org.slf4j.Logger LOG =
36 org.slf4j.LoggerFactory.getLogger(CryptoFactory.class);
37
38 /**
39 * getInstance
40 * <p/>
41 * Returns an instance of Crypto. This method uses the file
42 * <code>crypto.properties</code> to determine which implementation to
43 * use. Thus the property <code>org.apache.wss4j.crypto.provider</code>
44 * must define the classname of the Crypto implementation. The file
45 * may contain other property definitions as well. These properties are
46 * handed over to the Crypto implementation. The file
47 * <code>crypto.properties</code> is loaded with the
48 * <code>Loader.getResource()</code> method.
49 * <p/>
50 *
51 * @return The crypto implementation was defined
52 * @throws WSSecurityException if there is an error in loading the crypto properties
53 */
54 public static Crypto getInstance() throws WSSecurityException {
55 return getInstance("crypto.properties");
56 }
57
58 /**
59 * getInstance
60 * <p/>
61 * Returns an instance of Crypto. The properties are handed over the the crypto
62 * implementation. The properties must at least contain the Crypto implementation
63 * class name as the value of the property : org.apache.wss4j.crypto.provider
64 * <p/>
65 *
66 * @param properties The Properties that are forwarded to the crypto implementation
67 * and the Crypto impl class name.
68 * These properties are dependent on the crypto implementation
69 * @return The cyrpto implementation or null if no cryptoClassName was defined
70 * @throws WSSecurityException if there is an error in loading the crypto properties
71 */
72 public static Crypto getInstance(Properties properties) throws WSSecurityException {
73 if (properties == null) {
74 LOG.debug("Cannot load Crypto instance as properties object is null");
75 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE,
76 "empty", new Object[] {"Cannot load Crypto instance as properties object is null"});
77 }
78 return getInstance(properties, Loader.getClassLoader(CryptoFactory.class), null);
79 }
80
81 /**
82 * getInstance
83 * <p/>
84 * Returns an instance of Crypto loaded with the given classloader.
85 * The properties are handed over the the crypto implementation.
86 * The properties must at least contain the Crypto implementation
87 * class name as the value of the property : org.apache.wss4j.crypto.provider
88 * <p/>
89 *
90 * @param properties The Properties that are forwarded to the crypto implementation
91 * and the Crypto impl class name.
92 * These properties are dependent on the crypto implementation
93 * @param classLoader The class loader to use
94 * @param passwordEncryptor The PasswordEncryptor to use to decrypt encrypted passwords
95 * @return The crypto implementation or null if no cryptoClassName was defined
96 * @throws WSSecurityException if there is an error in loading the crypto properties
97 */
98 public static Crypto getInstance(
99 Properties properties,
100 ClassLoader classLoader,
101 PasswordEncryptor passwordEncryptor
102 ) throws WSSecurityException {
103 if (properties == null) {
104 LOG.debug("Cannot load Crypto instance as properties object is null");
105 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE,
106 "empty", new Object[] {"Cannot load Crypto instance as properties object is null"});
107 }
108
109 String cryptoClassName = properties.getProperty("org.apache.wss4j.crypto.provider");
110 if (cryptoClassName == null) {
111 cryptoClassName = properties.getProperty("org.apache.ws.security.crypto.provider");
112 }
113
114 Class<? extends Crypto> cryptoClass = null;
115 if (cryptoClassName == null
116 || cryptoClassName.equals("org.apache.wss4j.common.crypto.Merlin")
117 || cryptoClassName.equals("org.apache.ws.security.components.crypto.Merlin")) {
118 try {
119 return new Merlin(properties, classLoader, passwordEncryptor);
120 } catch (Exception e) {
121 LOG.debug("Unable to instantiate Merlin", e);
122 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, e, "empty",
123 new Object[] {"Cannot create Crypto class " + cryptoClassName});
124 }
125 } else {
126 try {
127 // instruct the class loader to load the crypto implementation
128 cryptoClass = Loader.loadClass(cryptoClassName, Crypto.class);
129 } catch (ClassNotFoundException ex) {
130 LOG.debug(ex.getMessage(), ex);
131 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, ex,
132 "empty", new Object[] {cryptoClassName + " Not Found"});
133 }
134 }
135 return loadClass(cryptoClass, properties, classLoader);
136 }
137
138 /**
139 * getInstance
140 * <p/>
141 * Returns an instance of Crypto. The supplied map is handed over the the crypto
142 * implementation. The map can be <code>null</code>. It is dependent on the
143 * Crypto implementation how the initialization is done in this case.
144 * <p/>
145 *
146 * @param cryptoClass This is the crypto implementation class. No default is
147 * provided here.
148 * @param map The Maps that is forwarded to the crypto implementation.
149 * These contents of the map are dependent on the
150 * underlying crypto implementation specified in the
151 * cryptoClassName parameter.
152 * @return The crypto implementation or null if no cryptoClassName was defined
153 * @throws WSSecurityException if there is an error in loading the crypto properties
154 */
155 public static Crypto getInstance(
156 Class<? extends Crypto> cryptoClass,
157 Map<Object, Object> map
158 ) throws WSSecurityException {
159 return loadClass(cryptoClass, map, Loader.getClassLoader(CryptoFactory.class));
160 }
161
162 /**
163 * getInstance
164 * <p/>
165 * Returns an instance of Crypto. This method uses the specified filename
166 * to load a property file. This file shall use the property
167 * <code>org.apache.wss4j.crypto.provider</code>
168 * to define the classname of the Crypto implementation. The file
169 * may contain other property definitions as well. These properties are
170 * handed over to the Crypto implementation. The specified file
171 * is loaded with the <code>Loader.getResource()</code> method.
172 * <p/>
173 *
174 * @param propFilename The name of the property file to load
175 * @return The crypto implementation that was defined
176 * @throws WSSecurityException if there is an error in loading the crypto properties
177 */
178 public static Crypto getInstance(String propFilename) throws WSSecurityException {
179 return getInstance(propFilename, Loader.getClassLoader(CryptoFactory.class));
180 }
181
182 public static Crypto getInstance(
183 String propFilename,
184 ClassLoader customClassLoader
185 ) throws WSSecurityException {
186 Properties properties = getProperties(propFilename, customClassLoader);
187 return getInstance(properties, customClassLoader, null);
188 }
189
190 /**
191 * This allows loading the classes with a custom class loader
192 * @param cryptoClass
193 * @param map
194 * @param loader
195 * @throws WSSecurityException if there is an error in loading the crypto properties
196 */
197 private static Crypto loadClass(
198 Class<? extends Crypto> cryptoClass,
199 Map<Object, Object> map,
200 ClassLoader loader
201 ) throws WSSecurityException {
202 LOG.debug("Using Crypto Engine [{}]", cryptoClass);
203 try {
204 Constructor<? extends Crypto> c = null;
205 try {
206 Class<?>[] classes = new Class[]{Map.class, ClassLoader.class};
207 c = cryptoClass.getConstructor(classes);
208 return c.newInstance(map, loader);
209 } catch (NoSuchMethodException ex) {
210 Class<?>[] classes =
211 new Class[]{Properties.class, Map.class, PasswordEncryptor.class};
212 c = cryptoClass.getConstructor(classes);
213 return c.newInstance(map, loader, null);
214 }
215 } catch (Exception e) {
216 if (LOG.isDebugEnabled()) {
217 LOG.debug("Unable to instantiate: " + cryptoClass.getName(), e);
218 }
219 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, e,
220 "empty", new Object[] {cryptoClass + " cannot create instance"});
221 }
222 }
223
224 /**
225 * This allows loading the classes with a custom class loader
226 * @param cryptoClass
227 * @param map
228 * @param loader
229 * @throws WSSecurityException if there is an error in loading the crypto properties
230 */
231 private static Crypto loadClass(
232 Class<? extends Crypto> cryptoClass,
233 Properties map,
234 ClassLoader loader
235 ) throws WSSecurityException {
236 LOG.debug("Using Crypto Engine [{}]", cryptoClass);
237 try {
238 Constructor<? extends Crypto> c = null;
239 try {
240 Class<?>[] classes = new Class[]{Properties.class, ClassLoader.class};
241 c = cryptoClass.getConstructor(classes);
242 return c.newInstance(map, loader);
243 } catch (NoSuchMethodException ex) {
244 Class<?>[] classes =
245 new Class[]{Properties.class, ClassLoader.class, PasswordEncryptor.class};
246 c = cryptoClass.getConstructor(classes);
247 return c.newInstance(map, loader, null);
248 }
249 } catch (Exception e) {
250 if (LOG.isDebugEnabled()) {
251 LOG.debug("Unable to instantiate: " + cryptoClass.getName(), e);
252 }
253 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, e,
254 "empty", new Object[] {cryptoClass + " cannot create instance"});
255 }
256 }
257
258 /**
259 * This allows loading the resources with a custom class loader
260 * @param propFilename
261 * @param loader
262 * @return a Properties object loaded from the propFilename argument
263 * @throws WSSecurityException if there is an error in loading the crypto properties
264 */
265 public static Properties getProperties(
266 String propFilename,
267 ClassLoader loader
268 ) throws WSSecurityException {
269 Properties properties = new Properties();
270 try (InputStream is = Loader.loadInputStream(loader, propFilename)) {
271 if (is == null) {
272 throw new WSSecurityException(
273 WSSecurityException.ErrorCode.FAILURE,
274 "resourceNotFound",
275 new Object[] {propFilename}
276 );
277 }
278 properties.load(is);
279 } catch (IOException e) {
280 if (LOG.isDebugEnabled()) {
281 LOG.debug("Cannot find resource: " + propFilename, e);
282 }
283 throw new WSSecurityException(
284 WSSecurityException.ErrorCode.FAILURE, e,
285 "resourceNotFound", new Object[] {propFilename}
286 );
287 }
288 return properties;
289 }
290
291 }
292