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 }