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 }