CryptoCipherFactory.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.cipher;

  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 CryptoCipher} instances.
  27.  */
  28. public class CryptoCipherFactory {

  29.     /**
  30.      * Defines the internal CryptoCipher implementations.
  31.      * <p>
  32.      * Usage:
  33.      * </p>
  34.      * <blockquote><pre>
  35.      * props.setProperty(CryptoCipherFactory.CLASSES_KEY, CipherProvider.OPENSSL.getClassName());
  36.      * props.setProperty(...); // if required by the implementation
  37.      * cipher = CryptoCipherFactory.getInstance(transformation, props);
  38.      * </pre></blockquote>
  39.      */
  40.     public enum CipherProvider {

  41.         /**
  42.          * The OpenSSL cipher implementation (using JNI)
  43.          * <p>
  44.          * This implementation does not use any properties
  45.          * </p>
  46.          */
  47.         // Please ensure the property description agrees with the implementation
  48.         OPENSSL(OpenSslCipher.class),

  49.         /**
  50.          * The JCE cipher implementation from the JVM
  51.          * <p>
  52.          * uses the property {@link #JCE_PROVIDER_KEY}
  53.          * to define the provider name, if present.
  54.          * </p>
  55.          */
  56.         // Please ensure the property description agrees with the implementation
  57.         JCE(JceCipher.class);

  58.         private final Class<? extends CryptoCipher> klass;

  59.         private final String className;

  60.         /**
  61.          * The private constructor.
  62.          * @param klass the Class of CryptoCipher
  63.          */
  64.         CipherProvider(final Class<? extends CryptoCipher> klass) {
  65.             this.klass = klass;
  66.             this.className = klass.getName();
  67.         }

  68.         /**
  69.          * Gets the class name of the provider.
  70.          *
  71.          * @return the fully qualified name of the provider class
  72.          */
  73.         public String getClassName() {
  74.             return className;
  75.         }

  76.         /**
  77.          * Gets the implementation class of the provider.
  78.          *
  79.          * @return the implementation class of the provider
  80.          */
  81.         public Class<? extends CryptoCipher> getImplClass() {
  82.             return klass;
  83.         }
  84.     }

  85.     /**
  86.      * The configuration key of the provider class for JCE cipher.
  87.      */
  88.     public static final String JCE_PROVIDER_KEY = Crypto.CONF_PREFIX + "cipher.jce.provider";

  89.     /**
  90.      * The configuration key of the CryptoCipher implementation class.
  91.      * <p>
  92.      * The value of CLASSES_KEY needs to be the full name of a
  93.      * class that implements the
  94.      * {@link org.apache.commons.crypto.cipher.CryptoCipher CryptoCipher} interface
  95.      * The internal classes are listed in the enum
  96.      * {@link CipherProvider CipherProvider}
  97.      * which can be used to obtain the full class name.
  98.      * </p>
  99.      * <p>
  100.      * The value can also be a comma-separated list of class names in
  101.      * order of descending priority.
  102.      * </p>
  103.      */
  104.     public static final String CLASSES_KEY = Crypto.CONF_PREFIX + "cipher.classes";

  105.     /**
  106.      * For AES, the algorithm block is fixed size of 128 bits.
  107.      *
  108.      * @see <a href="http://en.wikipedia.org/wiki/Advanced_Encryption_Standard">
  109.      *      http://en.wikipedia.org/wiki/Advanced_Encryption_Standard</a>
  110.      */
  111.     public static final int AES_BLOCK_SIZE = 16;

  112.     /**
  113.      * The default value (OPENSSL,JCE) for crypto cipher.
  114.      */
  115.     private static final String CLASSES_DEFAULT =
  116.             CipherProvider.OPENSSL.getClassName()
  117.             .concat(",")
  118.             .concat(CipherProvider.JCE.getClassName());

  119.     /**
  120.      * Gets the cipher class.
  121.      *
  122.      * @param props The {@code Properties} class represents a set of
  123.      *        properties.
  124.      * @return the cipher class based on the props.
  125.      */
  126.     private static String getCipherClassString(final Properties props) {
  127.         String cipherClassString = props.getProperty(CryptoCipherFactory.CLASSES_KEY, CLASSES_DEFAULT);
  128.         if (cipherClassString.isEmpty()) { // TODO does it make sense to treat the empty string as the default?
  129.             cipherClassString = CLASSES_DEFAULT;
  130.         }
  131.         return cipherClassString;
  132.     }

  133.     /**
  134.      * Gets a cipher for algorithm/mode/padding in config value
  135.      * commons.crypto.cipher.transformation
  136.      *
  137.      * @param transformation the name of the transformation, e.g.,
  138.      * <i>AES/CBC/PKCS5Padding</i>.
  139.      * See the Java Cryptography Architecture Standard Algorithm Name Documentation
  140.      * for information about standard transformation names.
  141.      * @return CryptoCipher the cipher object (defaults to OpenSslCipher if available, else JceCipher)
  142.      * @throws GeneralSecurityException if JCE cipher initialize failed
  143.      */
  144.     public static CryptoCipher getCryptoCipher(final String transformation)
  145.             throws GeneralSecurityException {
  146.         return getCryptoCipher(transformation, new Properties());
  147.     }

  148.     /**
  149.      * Gets a cipher instance for specified algorithm/mode/padding.
  150.      *
  151.      * @param properties  the configuration properties - uses {@link #CLASSES_KEY}
  152.      * @param transformation  algorithm/mode/padding
  153.      * @return CryptoCipher  the cipher  (defaults to OpenSslCipher)
  154.      * @throws GeneralSecurityException if cipher initialize failed
  155.      * @throws IllegalArgumentException if no classname(s) were provided
  156.      */
  157.     public static CryptoCipher getCryptoCipher(final String transformation, final Properties properties) throws GeneralSecurityException {
  158.         final List<String> names = Utils.splitClassNames(getCipherClassString(properties), ",");
  159.         if (names.isEmpty()) {
  160.             throw new IllegalArgumentException("No classname(s) provided");
  161.         }
  162.         CryptoCipher cipher = null;
  163.         Exception lastException = null;

  164.         final StringBuilder errorMessage = new StringBuilder("CryptoCipher ");
  165.         for (final String klass : names) {
  166.             try {
  167.                 final Class<?> cls = ReflectionUtils.getClassByName(klass);
  168.                 cipher = ReflectionUtils.newInstance(cls.asSubclass(CryptoCipher.class), properties, transformation);
  169.                 break;
  170.             } catch (final Exception e) {
  171.                 lastException = e;
  172.                 errorMessage.append("{" + klass + "}");
  173.             }
  174.         }

  175.         if (cipher != null) {
  176.             return cipher;
  177.         }
  178.         errorMessage.append(" is not available or transformation " + transformation + " is not supported.");
  179.         throw new GeneralSecurityException(errorMessage.toString(), lastException);
  180.     }

  181.     /**
  182.      * The private Constructor of {@link CryptoCipherFactory}.
  183.      */
  184.     private CryptoCipherFactory() {
  185.     }

  186. }