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