View Javadoc
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