CryptoRandomFactory.java
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.crypto.random;
import java.security.GeneralSecurityException;
import java.util.List;
import java.util.Properties;
import org.apache.commons.crypto.Crypto;
import org.apache.commons.crypto.utils.ReflectionUtils;
import org.apache.commons.crypto.utils.Utils;
/**
* Creates {@link CryptoRandom} instances
*/
public class CryptoRandomFactory {
/**
* Defines the internal CryptoRandom implementations.
* <p>
* Usage:
* <blockquote><pre>
* props.setProperty(CryptoRandomFactory.CLASSES_KEY, RandomProvider.OPENSSL.getClassName());
* props.setProperty(...); // if required by the implementation
* random = CryptoRandomFactory.getCryptoRandom(transformation, props);
* </pre></blockquote>
*/
public enum RandomProvider {
/**
* The OpenSSL Random implementation (using JNI)
* <p>
* No properties are used for configuration, but they
* are passed to the {@link RandomProvider#JAVA} backup implementation
*/
// Please ensure the property description agrees with the implementation
OPENSSL(OpenSslCryptoRandom.class),
/**
* The SecureRandom implementation from the JVM
* <p>
* Uses the property with key
* {@link #JAVA_ALGORITHM_KEY}
* with the default of
* {@link #JAVA_ALGORITHM_DEFAULT}
*/
// Please ensure the property description agrees with the implementation
JAVA(JavaCryptoRandom.class),
/**
* The OS random device implementation. May not be available on some OSes.
* <p>
* Uses {@link #DEVICE_FILE_PATH_KEY} to determine the
* path to the random device, default is
* {@link #DEVICE_FILE_PATH_DEFAULT}
*/
// Please ensure the property description agrees with the implementation
OS(OsCryptoRandom.class);
private final Class<? extends CryptoRandom> klass;
private final String className;
/**
* The private constructor.
* @param klass the Class of CryptoRandom
*/
RandomProvider(final Class<? extends CryptoRandom> klass) {
this.klass = klass;
this.className = klass.getName();
}
/**
* Gets the class name of the provider.
*
* @return the name of the provider class
*/
public String getClassName() {
return className;
}
/**
* Gets the implementation class of the provider.
*
* @return the implementation class of the provider
*/
public Class<? extends CryptoRandom> getImplClass() {
return klass;
}
}
// security random related configuration keys
/**
* The configuration key of the file path for secure random device.
*/
public static final String DEVICE_FILE_PATH_KEY = Crypto.CONF_PREFIX + "secure.random.device.file.path";
/**
* The default value ({@value}) of the file path for secure random device.
*/
// Note: this is public mainly for use by the Javadoc
public static final String DEVICE_FILE_PATH_DEFAULT = "/dev/urandom";
/**
* The configuration key of the algorithm of secure random.
*/
public static final String JAVA_ALGORITHM_KEY = Crypto.CONF_PREFIX + "secure.random.java.algorithm";
/**
* The default value ({@value}) of the algorithm of secure random.
*/
// Note: this is public mainly for use by the Javadoc
public static final String JAVA_ALGORITHM_DEFAULT = "SHA1PRNG";
/**
* The configuration key of the CryptoRandom implementation class.
* <p>
* The value of the CLASSES_KEY needs to be the full name of a
* class that implements the
* {@link org.apache.commons.crypto.random.CryptoRandom CryptoRandom} interface
* The internal classes are listed in the enum
* {@link RandomProvider RandomProvider}
* which can be used to obtain the full class name.
* <p>
* The value can also be a comma-separated list of class names in
* order of descending priority.
*/
public static final String CLASSES_KEY = Crypto.CONF_PREFIX + "secure.random.classes";
/**
* The default value (OPENSSL,JAVA) used when creating a {@link org.apache.commons.crypto.cipher.CryptoCipher}.
*/
private static final String CLASSES_DEFAULT =
RandomProvider.OPENSSL.getClassName()
.concat(",")
.concat(RandomProvider.JAVA.getClassName());
/**
* Gets a CryptoRandom instance using the default implementation
* as defined by {@link #CLASSES_DEFAULT}
*
* @return CryptoRandom the cryptoRandom object.
* @throws GeneralSecurityException if cannot create the {@link CryptoRandom} class
*/
public static CryptoRandom getCryptoRandom() throws GeneralSecurityException {
final Properties properties = new Properties();
return getCryptoRandom(properties);
}
/**
* Gets a CryptoRandom instance for specified props.
* Uses the SECURE_RANDOM_CLASSES_KEY from the provided
* properties.
* If it is not set, then it checks the System properties.
* Failing that, it defaults to OpenSslCryptoRandom,JavaCryptoRandom
* The properties are passed to the generated class.
*
* @param props the configuration properties.
* @return CryptoRandom the cryptoRandom object.
* @throws GeneralSecurityException if cannot create the {@link CryptoRandom} class
* @throws IllegalArgumentException if no classname(s) are provided
*/
public static CryptoRandom getCryptoRandom(final Properties props)
throws GeneralSecurityException {
final List<String> names = Utils.splitClassNames(getRandomClassString(props), ",");
if (names.isEmpty()) {
throw new IllegalArgumentException("No class name(s) provided");
}
final StringBuilder errorMessage = new StringBuilder();
CryptoRandom random = null;
Exception lastException = null;
for (final String klassName : names) {
try {
final Class<?> klass = ReflectionUtils.getClassByName(klassName);
random = (CryptoRandom) ReflectionUtils.newInstance(klass, props);
break;
} catch (final ClassCastException e) {
lastException = e;
errorMessage.append("Class: [" + klassName + "] is not a CryptoRandom.");
} catch (final ClassNotFoundException e) {
lastException = e;
errorMessage.append("CryptoRandom: [" + klassName + "] not found.");
} catch (final Exception e) {
lastException = e;
errorMessage.append("CryptoRandom: [" + klassName + "] failed with " + e.getMessage());
}
}
if (random != null) {
return random;
}
throw new GeneralSecurityException(errorMessage.toString(), lastException);
}
/**
* Gets the CryptoRandom class.
*
* @param props The {@code Properties} class represents a set of
* properties.
* @return the CryptoRandom class based on the props.
*/
private static String getRandomClassString(final Properties props) {
String randomClassString = props.getProperty(CryptoRandomFactory.CLASSES_KEY, CLASSES_DEFAULT);
if (randomClassString.isEmpty()) { // TODO does it make sense to treat the empty string as the default?
randomClassString = CLASSES_DEFAULT;
}
return randomClassString;
}
/**
* The private constructor of {@link CryptoRandomFactory}.
*/
private CryptoRandomFactory() {
}
}