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.lang.reflect.Field;
23  import java.security.AccessController;
24  import java.security.PrivilegedAction;
25  import java.security.PrivilegedExceptionAction;
26  import java.security.Provider;
27  import java.security.Security;
28  
29  import org.apache.wss4j.common.util.FIPSUtils;
30  import org.apache.wss4j.common.util.Loader;
31  import org.apache.xml.security.utils.I18n;
32  import org.apache.xml.security.utils.XMLUtils;
33  
34  
35  /**
36   * Configure Crypto providers.
37   */
38  public final class WSProviderConfig {
39  
40      private static final org.slf4j.Logger LOG =
41          org.slf4j.LoggerFactory.getLogger(WSProviderConfig.class);
42  
43      /**
44       * a static boolean flag that determines whether default JCE providers
45       * should be added at the time of construction.
46       *
47       * These providers, and the order in which they are added, can interfere
48       * with some JVMs (such as IBMs).
49       */
50      private static boolean addJceProviders = true;
51  
52      /**
53       * a boolean flag to record whether we have already been statically
54       * initialized.  This flag prevents repeated and unnecessary calls
55       * to static initialization code at construction time.
56       */
57      private static boolean staticallyInitialized;
58  
59      private static boolean santuarioProviderAdded;
60      private static boolean bcProviderAdded;
61      private static boolean tlProviderAdded;
62  
63      private WSProviderConfig() {
64          // complete
65      }
66  
67      public static synchronized void init() {
68          if (!staticallyInitialized) {
69              if (addJceProviders) {
70                  initializeResourceBundles();
71                  setXmlSecIgnoreLineBreak();
72                  AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
73                      public Boolean run() {
74                          addXMLDSigRIInternal();
75                          return true;
76                      }
77                  });
78  
79                  santuarioProviderAdded = true;
80                  bcProviderAdded = false;
81                  tlProviderAdded = false;
82              }
83              if (FIPSUtils.isFIPSEnabled()) {
84                  //So far the in-JDK security provider in FIPS mode
85                  //doesn't support RSA-OAEP padding, try use the one 
86                  //from BC-FIPS as last resort
87                  AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
88                      public Boolean run() {
89                          addJceProvider("BCFIPS", "org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider");
90                          return true;
91                      }
92                  });
93              }
94              staticallyInitialized = true;
95          }
96      }
97  
98      public static synchronized void init(boolean addXMLDSigRIInternalProv, boolean addBCProv, boolean addTLProv) {
99          if (!staticallyInitialized) {
100             initializeResourceBundles();
101             setXmlSecIgnoreLineBreak();
102             santuarioProviderAdded = addXMLDSigRIInternalProv;
103             if (addXMLDSigRIInternalProv) {
104                 AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
105                     public Boolean run() {
106                         addXMLDSigRIInternal();
107                         return true;
108                     }
109                 });
110             }
111 
112             bcProviderAdded = addBCProv;
113             if (addBCProv) {
114                 AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
115                     public Boolean run() {
116                         addJceProvider("BC", "org.bouncycastle.jce.provider.BouncyCastleProvider");
117                         return true;
118                     }
119                 });
120             }
121             if (FIPSUtils.isFIPSEnabled()) {
122                 //So far the in-JDK security provider in FIPS mode
123                 //doesn't support RSA-OAEP padding, try use the one 
124                 //from BC-FIPS as last resort
125                 
126 
127                 AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
128                     public Boolean run() {
129                         addJceProvider("BCFIPS", "org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider");
130                         return true;
131                     }
132                 });
133                 
134             }
135 
136             tlProviderAdded = addTLProv;
137             if (addTLProv) {
138                 AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
139                     public Boolean run() {
140                         ThreadLocalSecurityProvider.install();
141                         return true;
142                     }
143                 });
144             }
145             staticallyInitialized = true;
146         }
147     }
148 
149     public static synchronized void cleanUp() {
150         if (staticallyInitialized) {
151             if (santuarioProviderAdded) {
152                 Security.removeProvider("ApacheXMLDSig");
153                 santuarioProviderAdded = false;
154             }
155             if (bcProviderAdded) {
156                 Security.removeProvider("BC");
157                 bcProviderAdded = false;
158             }
159             if (tlProviderAdded) {
160                 Security.removeProvider("TLSP");
161                 tlProviderAdded = false;
162             }
163 
164             staticallyInitialized = false;
165         }
166     }
167 
168     /**
169      * Set the value of the internal addJceProviders flag.  This flag
170      * turns on (or off) automatic registration of known JCE providers
171      * that provide necessary cryptographic algorithms for use with WSS4J.
172      * By default, this flag is true. You may wish (or need) to initialize
173      * the JCE manually, e.g., in some JVMs.
174      */
175     public static void setAddJceProviders(boolean value) {
176         addJceProviders = value;
177     }
178 
179     public static void setXmlSecIgnoreLineBreak() {
180         //really need to make sure ignoreLineBreaks is set to
181         boolean wasSet = false;
182         try {
183             // Don't override if it was set explicitly
184             wasSet = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
185                 public Boolean run() {
186                     String lineBreakPropName = "org.apache.xml.security.ignoreLineBreaks";
187                     if (System.getProperty(lineBreakPropName) == null) {
188                         System.setProperty(lineBreakPropName, "true");
189                         return false;
190                     }
191                     return true;
192                 }
193             });
194         } catch (Throwable t) { //NOPMD
195             //ignore
196         }
197         org.apache.xml.security.Init.init();
198         if (!wasSet) {
199             try {
200                 AccessController.doPrivileged(new PrivilegedExceptionAction<Boolean>() {
201                     public Boolean run() throws Exception {
202                         Field f = XMLUtils.class.getDeclaredField("ignoreLineBreaks");
203                         f.setAccessible(true);  //NOPMD
204                         f.set(null, Boolean.TRUE);
205                         return false;
206                     }
207                 });
208             } catch (Throwable t) { //NOPMD
209                 //ignore
210             }
211         }
212     }
213 
214     private static void addXMLDSigRIInternal() {
215         Security.removeProvider("ApacheXMLDSig");
216         addJceProvider("ApacheXMLDSig", SantuarioUtil.getSantuarioProvider());
217     }
218 
219     private static void initializeResourceBundles() {
220         I18n.init(new WSS4JResourceBundle());
221     }
222 
223     /**
224      * Add a new JCE security provider to use for WSS4J, of the specified name and class. Return
225      * either the name of the previously loaded provider, the name of the new loaded provider, or
226      * null if there's an exception in loading the provider.
227      *
228      * @param name
229      *            The name string of the provider (this may not be the real name of the provider)
230      * @param className
231      *            Name of the class the implements the provider. This class must
232      *            be a subclass of <code>java.security.Provider</code>
233      *
234      * @return Returns the actual name of the provider that was loaded
235      */
236     public static String addJceProvider(String name, String className) {
237         Provider currentProvider = Security.getProvider(name);
238         if (currentProvider == null) {
239             try {
240                 Class<? extends Provider> clazz = Loader.loadClass(className, false, Provider.class);
241                 Provider provider = clazz.getDeclaredConstructor().newInstance();
242                 return addJceProvider(name, provider);
243             } catch (Throwable t) {
244                 if (LOG.isDebugEnabled()) {
245                     LOG.debug("The provider " + name + " could not be added: " + t.getMessage(), t);
246                 }
247                 return null;
248             }
249         }
250         return currentProvider.getName();
251     }
252 
253     /**
254      * Add a new JCE security provider to use for WSS4J, of the specified name and class. Return
255      * either the name of the previously loaded provider, the name of the new loaded provider, or
256      * null if there's an exception in loading the provider.
257      *
258      * @param name
259      *            The name string of the provider (this may not be the real name of the provider)
260      * @param provider
261      *            A subclass of <code>java.security.Provider</code>
262      *
263      * @return Returns the actual name of the provider that was loaded
264      */
265     public static String addJceProvider(String name, Provider provider) {
266         Provider currentProvider = Security.getProvider(name);
267         if (currentProvider == null) {
268             try {
269                 int ret = Security.addProvider(provider);
270                 if (LOG.isDebugEnabled()) {
271                     LOG.debug(
272                         "The provider " + provider.getName() + " - "
273                          + provider.getVersionStr() + " was added at position: " + ret
274                     );
275                 }
276                 return provider.getName();
277             } catch (Throwable t) {
278                 if (LOG.isDebugEnabled()) {
279                     LOG.debug("The provider " + name + " could not be added: " + t.getMessage(), t);
280                 }
281                 return null;
282             }
283         }
284         return currentProvider.getName();
285     }
286 
287 
288     /**
289      * Add a new JCE security provider to use for WSS4J, of the specified name and class. Return
290      * either the name of the previously loaded provider, the name of the new loaded provider, or
291      * null if there's an exception in loading the provider. Append the provider to the provider
292      * list.
293      *
294      * @param name
295      *            The name string of the provider (this may not be the real name of the provider)
296      * @param className
297      *            Name of the class the implements the provider. This class must
298      *            be a subclass of <code>java.security.Provider</code>
299      *
300      * @return Returns the actual name of the provider that was loaded
301      */
302     public static String appendJceProvider(String name, String className) {
303         Provider currentProvider = Security.getProvider(name);
304         if (currentProvider == null) {
305             try {
306                 Class<? extends Provider> clazz = Loader.loadClass(className, false, Provider.class);
307                 Provider provider = clazz.getDeclaredConstructor().newInstance();
308 
309                 int ret = Security.addProvider(provider);
310                 LOG.debug(
311                     "The provider {} was added at position: {}",
312                      provider.getName(), ret
313                 );
314                 return provider.getName();
315             } catch (Throwable t) {
316                 if (LOG.isDebugEnabled()) {
317                     LOG.debug("The provider " + name + " could not be added: " + t.getMessage(), t);
318                 }
319                 return null;
320             }
321         }
322         return currentProvider.getName();
323     }
324 
325     /**
326      * Add a new JCE security provider to use for WSS4J, of the specified name and class. Return
327      * either the name of the previously loaded provider, the name of the new loaded provider, or
328      * null if there's an exception in loading the provider. Append the provider to the provider
329      * list.
330      *
331      * @param name
332      *            The name string of the provider (this may not be the real name of the provider)
333      * @param provider
334      *            A subclass of <code>java.security.Provider</code>
335      *
336      * @return Returns the actual name of the provider that was loaded
337      */
338     public static String appendJceProvider(String name, Provider provider) {
339         Provider currentProvider = Security.getProvider(name);
340         if (currentProvider == null) {
341             try {
342                 int ret = Security.addProvider(provider);
343                 LOG.debug(
344                     "The provider {} was added at position: {}",
345                      provider.getName(), ret
346                 );
347                 return provider.getName();
348             } catch (Throwable t) {
349                 if (LOG.isDebugEnabled()) {
350                     LOG.debug("The provider " + name + " could not be added: " + t.getMessage(), t);
351                 }
352                 return null;
353             }
354         }
355         return currentProvider.getName();
356     }
357 
358 }