CryptoRandomFactory.java

  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. import java.security.GeneralSecurityException;
  20. import java.util.List;
  21. import java.util.Properties;

  22. import org.apache.commons.crypto.Crypto;
  23. import org.apache.commons.crypto.utils.ReflectionUtils;
  24. import org.apache.commons.crypto.utils.Utils;

  25. /**
  26.  * Creates {@link CryptoRandom} instances
  27.  */
  28. public class CryptoRandomFactory {

  29.     /**
  30.      * Defines the internal CryptoRandom implementations.
  31.      * <p>
  32.      * Usage:
  33.      * <blockquote><pre>
  34.      * props.setProperty(CryptoRandomFactory.CLASSES_KEY, RandomProvider.OPENSSL.getClassName());
  35.      * props.setProperty(...); // if required by the implementation
  36.      * random = CryptoRandomFactory.getCryptoRandom(transformation, props);
  37.      * </pre></blockquote>
  38.      */
  39.     public enum RandomProvider {

  40.         /**
  41.          * The OpenSSL Random implementation (using JNI)
  42.          * <p>
  43.          * No properties are used for configuration, but they
  44.          * are passed to the {@link RandomProvider#JAVA} backup implementation
  45.          */
  46.         // Please ensure the property description agrees with the implementation
  47.         OPENSSL(OpenSslCryptoRandom.class),

  48.         /**
  49.          * The SecureRandom implementation from the JVM
  50.          * <p>
  51.          * Uses the property with key
  52.          * {@link #JAVA_ALGORITHM_KEY}
  53.          * with the default of
  54.          * {@link #JAVA_ALGORITHM_DEFAULT}
  55.          */
  56.         // Please ensure the property description agrees with the implementation
  57.         JAVA(JavaCryptoRandom.class),

  58.         /**
  59.          * The OS random device implementation. May not be available on some OSes.
  60.          * <p>
  61.          * Uses {@link #DEVICE_FILE_PATH_KEY} to determine the
  62.          * path to the random device, default is
  63.          * {@link #DEVICE_FILE_PATH_DEFAULT}
  64.          */
  65.         // Please ensure the property description agrees with the implementation
  66.         OS(OsCryptoRandom.class);

  67.         private final Class<? extends CryptoRandom> klass;

  68.         private final String className;

  69.         /**
  70.          * The private constructor.
  71.          * @param klass the Class of CryptoRandom
  72.          */
  73.         RandomProvider(final Class<? extends CryptoRandom> klass) {
  74.             this.klass = klass;
  75.             this.className = klass.getName();
  76.         }

  77.         /**
  78.          * Gets the class name of the provider.
  79.          *
  80.          * @return the name of the provider class
  81.          */
  82.         public String getClassName() {
  83.             return className;
  84.         }

  85.         /**
  86.          * Gets the implementation class of the provider.
  87.          *
  88.          * @return the implementation class of the provider
  89.          */
  90.         public Class<? extends CryptoRandom> getImplClass() {
  91.             return klass;
  92.         }
  93.     }

  94.     // security random related configuration keys
  95.     /**
  96.      * The configuration key of the file path for secure random device.
  97.      */
  98.     public static final String DEVICE_FILE_PATH_KEY = Crypto.CONF_PREFIX + "secure.random.device.file.path";

  99.     /**
  100.      * The default value ({@value}) of the file path for secure random device.
  101.      */
  102.     // Note: this is public mainly for use by the Javadoc
  103.     public static final String DEVICE_FILE_PATH_DEFAULT = "/dev/urandom";

  104.     /**
  105.      * The configuration key of the algorithm of secure random.
  106.      */
  107.     public static final String JAVA_ALGORITHM_KEY = Crypto.CONF_PREFIX + "secure.random.java.algorithm";

  108.     /**
  109.      * The default value ({@value}) of the algorithm of secure random.
  110.      */
  111.     // Note: this is public mainly for use by the Javadoc
  112.     public static final String JAVA_ALGORITHM_DEFAULT = "SHA1PRNG";

  113.     /**
  114.      * The configuration key of the CryptoRandom implementation class.
  115.      * <p>
  116.      * The value of the CLASSES_KEY needs to be the full name of a
  117.      * class that implements the
  118.      * {@link org.apache.commons.crypto.random.CryptoRandom CryptoRandom} interface
  119.      * The internal classes are listed in the enum
  120.      * {@link RandomProvider RandomProvider}
  121.      * which can be used to obtain the full class name.
  122.      * <p>
  123.      * The value can also be a comma-separated list of class names in
  124.      * order of descending priority.
  125.      */
  126.     public static final String CLASSES_KEY = Crypto.CONF_PREFIX + "secure.random.classes";

  127.     /**
  128.      * The default value (OPENSSL,JAVA) used when creating a {@link org.apache.commons.crypto.cipher.CryptoCipher}.
  129.      */
  130.     private static final String CLASSES_DEFAULT =
  131.         RandomProvider.OPENSSL.getClassName()
  132.         .concat(",")
  133.         .concat(RandomProvider.JAVA.getClassName());

  134.     /**
  135.      * Gets a CryptoRandom instance using the default implementation
  136.      * as defined by {@link #CLASSES_DEFAULT}
  137.      *
  138.      * @return CryptoRandom  the cryptoRandom object.
  139.      * @throws GeneralSecurityException if cannot create the {@link CryptoRandom} class
  140.      */
  141.     public static CryptoRandom getCryptoRandom() throws GeneralSecurityException {
  142.         final Properties properties = new Properties();
  143.         return getCryptoRandom(properties);
  144.     }

  145.     /**
  146.      * Gets a CryptoRandom instance for specified props.
  147.      * Uses the SECURE_RANDOM_CLASSES_KEY from the provided
  148.      * properties.
  149.      * If it is not set, then it checks the System properties.
  150.      * Failing that, it defaults to OpenSslCryptoRandom,JavaCryptoRandom
  151.      * The properties are passed to the generated class.
  152.      *
  153.      * @param props the configuration properties.
  154.      * @return CryptoRandom  the cryptoRandom object.
  155.      * @throws GeneralSecurityException if cannot create the {@link CryptoRandom} class
  156.      * @throws IllegalArgumentException if no classname(s) are provided
  157.      */
  158.     public static CryptoRandom getCryptoRandom(final Properties props)
  159.             throws GeneralSecurityException {
  160.         final List<String> names = Utils.splitClassNames(getRandomClassString(props), ",");
  161.         if (names.isEmpty()) {
  162.             throw new IllegalArgumentException("No class name(s) provided");
  163.         }
  164.         final StringBuilder errorMessage = new StringBuilder();
  165.         CryptoRandom random = null;
  166.         Exception lastException = null;
  167.         for (final String klassName : names) {
  168.             try {
  169.                 final Class<?> klass = ReflectionUtils.getClassByName(klassName);
  170.                 random = (CryptoRandom) ReflectionUtils.newInstance(klass, props);
  171.                 break;
  172.             } catch (final ClassCastException e) {
  173.                 lastException = e;
  174.                 errorMessage.append("Class: [" + klassName + "] is not a CryptoRandom.");
  175.             } catch (final ClassNotFoundException e) {
  176.                 lastException = e;
  177.                 errorMessage.append("CryptoRandom: [" + klassName + "] not found.");
  178.             } catch (final Exception e) {
  179.                 lastException = e;
  180.                 errorMessage.append("CryptoRandom: [" + klassName + "] failed with " + e.getMessage());
  181.             }
  182.         }

  183.         if (random != null) {
  184.             return random;
  185.         }
  186.         throw new GeneralSecurityException(errorMessage.toString(), lastException);
  187.     }

  188.     /**
  189.      * Gets the CryptoRandom class.
  190.      *
  191.      * @param props The {@code Properties} class represents a set of
  192.      *        properties.
  193.      * @return the CryptoRandom class based on the props.
  194.      */
  195.     private static String getRandomClassString(final Properties props) {
  196.         String randomClassString = props.getProperty(CryptoRandomFactory.CLASSES_KEY, CLASSES_DEFAULT);
  197.         if (randomClassString.isEmpty()) { // TODO does it make sense to treat the empty string as the default?
  198.             randomClassString = CLASSES_DEFAULT;
  199.         }
  200.         return randomClassString;
  201.     }

  202.     /**
  203.      * The private constructor of {@link CryptoRandomFactory}.
  204.      */
  205.     private CryptoRandomFactory() {
  206.     }
  207. }