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, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.apache.commons.crypto.random;
19  
20  import java.security.GeneralSecurityException;
21  import java.util.List;
22  import java.util.Properties;
23  
24  import org.apache.commons.crypto.Crypto;
25  import org.apache.commons.crypto.utils.ReflectionUtils;
26  import org.apache.commons.crypto.utils.Utils;
27  
28  /**
29   * This is the factory class used for creating {@link CryptoRandom} instances
30   */
31  public class CryptoRandomFactory {
32  
33      // security random related configuration keys
34      /**
35       * The configuration key of the file path for secure random device.
36       */
37      public static final String DEVICE_FILE_PATH_KEY = Crypto.CONF_PREFIX
38              + "secure.random.device.file.path";
39  
40      /**
41       * The default value ({@value}) of the file path for secure random device.
42       */
43      // Note: this is public mainly for use by the Javadoc
44      public static final String DEVICE_FILE_PATH_DEFAULT = "/dev/urandom";
45  
46      /**
47       * The configuration key of the algorithm of secure random.
48       */
49      public static final String JAVA_ALGORITHM_KEY = Crypto.CONF_PREFIX
50              + "secure.random.java.algorithm";
51  
52      /**
53       * The default value ({@value}) of the algorithm of secure random.
54       */
55      // Note: this is public mainly for use by the Javadoc
56      public static final String JAVA_ALGORITHM_DEFAULT = "SHA1PRNG";
57  
58      /**
59       * The configuration key of the CryptoRandom implementation class.
60       * <p>
61       * The value of the CLASSES_KEY needs to be the full name of a
62       * class that implements the
63       * {@link org.apache.commons.crypto.random.CryptoRandom CryptoRandom} interface
64       * The internal classes are listed in the enum
65       * {@link RandomProvider RandomProvider}
66       * which can be used to obtain the full class name.
67       * <p>
68       * The value can also be a comma-separated list of class names in
69       * order of descending priority.
70       */
71      public static final String CLASSES_KEY = Crypto.CONF_PREFIX
72              + "secure.random.classes";
73      /**
74       * Defines the internal CryptoRandom implementations.
75       * <p>
76       * Usage:
77       * <blockquote><pre>
78       * props.setProperty(CryptoRandomFactory.CLASSES_KEY, RandomProvider.OPENSSL.getClassName());
79       * props.setProperty(...); // if required by the implementation
80       * random = CryptoRandomFactory.getCryptoRandom(transformation, props);
81       * </pre></blockquote>
82       */
83      public enum RandomProvider {
84  
85          /**
86           * The OpenSSL Random implementation (using JNI)
87           * <p>
88           * No properties are used for configuration, but they
89           * are passed to the {@link RandomProvider#JAVA} backup implementation
90           */
91          // Please ensure the property description agrees with the implementation
92          OPENSSL(OpenSslCryptoRandom.class),
93  
94          /**
95           * The SecureRandom implementation from the JVM
96           * <p>
97           * Uses the property with key
98           * {@link #JAVA_ALGORITHM_KEY}
99           * with the default of
100          * {@link #JAVA_ALGORITHM_DEFAULT}
101          */
102         // Please ensure the property description agrees with the implementation
103         JAVA(JavaCryptoRandom.class),
104 
105         /**
106          * The OS random device implementation. May not be available on some OSes.
107          * <p>
108          * Uses {@link #DEVICE_FILE_PATH_KEY} to determine the
109          * path to the random device, default is
110          * {@link #DEVICE_FILE_PATH_DEFAULT}
111          */
112         // Please ensure the property description agrees with the implementation
113         OS(OsCryptoRandom.class);
114 
115         private final Class<? extends CryptoRandom> klass;
116 
117         private final String className;
118 
119         /**
120          * The private constructor.
121          * @param klass the Class of CryptoRandom
122          */
123         private RandomProvider(Class<? extends CryptoRandom> klass) {
124             this.klass = klass;
125             this.className = klass.getName();
126         }
127 
128         /**
129          * Gets the class name of the provider.
130          *
131          * @return the name of the provider class
132          */
133         public String getClassName() {
134             return className;
135         }
136 
137         /**
138          * Gets the implementation class of the provider.
139          *
140          * @return the implementation class of the provider
141          */
142         public Class<? extends CryptoRandom> getImplClass() {
143             return klass;
144         }
145     }
146 
147     /**
148      * The default value (OPENSSL,JAVA) used when creating a {@link CryptoCipher}.
149      */
150     private static final String CLASSES_DEFAULT =
151         RandomProvider.OPENSSL.getClassName()
152         .concat(",")
153         .concat(RandomProvider.JAVA.getClassName());
154 
155     /**
156      * The private constructor of {@link CryptoRandomFactory}.
157      */
158     private CryptoRandomFactory() {
159     }
160 
161     /**
162      * Gets a CryptoRandom instance using the default implementation
163      * as defined by {@link #CLASSES_DEFAULT}
164      *
165      * @return CryptoRandom  the cryptoRandom object.
166      * @throws GeneralSecurityException if cannot create the {@link CryptoRandom} class
167      */
168     public static CryptoRandom getCryptoRandom() throws GeneralSecurityException {
169         Properties properties = new Properties();
170         return getCryptoRandom(properties);
171     }
172 
173     /**
174      * Gets a CryptoRandom instance for specified props.
175      * Uses the SECURE_RANDOM_CLASSES_KEY from the provided
176      * properties.
177      * If it is not set, then it checks the System properties.
178      * Failing that, it defaults to OpenSslCryptoRandom,JavaCryptoRandom
179      * The properties are passed to the generated class.
180      *
181      * @param props the configuration properties.
182      * @return CryptoRandom  the cryptoRandom object.
183      * @throws GeneralSecurityException if cannot create the {@link CryptoRandom} class
184      * @throws IllegalArgumentException if no classname(s) are provided
185      */
186     public static CryptoRandom getCryptoRandom(Properties props)
187             throws GeneralSecurityException {
188         final List<String> names = Utils.splitClassNames(getRandomClassString(props), ",");
189         if (names.size() == 0) {
190             throw new IllegalArgumentException("No classname(s) provided");
191         }
192         StringBuilder errorMessage = new StringBuilder();
193         CryptoRandom random = null;
194         Exception lastException = null;
195         for (String klassName : names) {
196             try {
197                 final Class<?> klass = ReflectionUtils.getClassByName(klassName);
198                 random = (CryptoRandom) ReflectionUtils.newInstance(klass, props);
199                 if (random != null) {
200                     break;
201                 }
202             } catch (ClassCastException e) {
203                 lastException = e;
204                 errorMessage.append("Class: [" + klassName + "] is not a CryptoRandom.");
205             } catch (ClassNotFoundException e) {
206                 lastException = e;
207                 errorMessage.append("CryptoRandom: [" + klassName + "] not found.");
208             } catch (Exception e) {
209                 lastException = e;
210                 errorMessage.append("CryptoRandom: [" + klassName + "] failed with " + e.getMessage());
211             }
212         }
213 
214         if (random != null) {
215             return random;
216         }
217         throw new GeneralSecurityException(errorMessage.toString(), lastException);
218     }
219 
220     /**
221      * Gets the CryptoRandom class.
222      *
223      * @param props The <code>Properties</code> class represents a set of
224      *        properties.
225      * @return the CryptoRandom class based on the props.
226      */
227     private static String getRandomClassString(Properties props) {
228         String randomClassString = props.getProperty(CryptoRandomFactory.CLASSES_KEY, CLASSES_DEFAULT);
229         if (randomClassString.isEmpty()) { // TODO does it make sense to treat the empty string as the default?
230             randomClassString = CLASSES_DEFAULT;
231         }
232         return randomClassString;
233     }
234 }