View Javadoc
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  
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 CryptoCipher} instances.
30   */
31  public class CryptoCipherFactory {
32  
33      /**
34       * Defines the internal CryptoCipher implementations.
35       * <p>
36       * Usage:
37       * </p>
38       * <blockquote><pre>
39       * props.setProperty(CryptoCipherFactory.CLASSES_KEY, CipherProvider.OPENSSL.getClassName());
40       * props.setProperty(...); // if required by the implementation
41       * cipher = CryptoCipherFactory.getInstance(transformation, props);
42       * </pre></blockquote>
43       */
44      public enum CipherProvider {
45  
46          /**
47           * The OpenSSL cipher implementation (using JNI)
48           * <p>
49           * This implementation does not use any properties
50           * </p>
51           */
52          // Please ensure the property description agrees with the implementation
53          OPENSSL(OpenSslCipher.class),
54  
55          /**
56           * The JCE cipher implementation from the JVM
57           * <p>
58           * uses the property {@link #JCE_PROVIDER_KEY}
59           * to define the provider name, if present.
60           * </p>
61           */
62          // Please ensure the property description agrees with the implementation
63          JCE(JceCipher.class);
64  
65          private final Class<? extends CryptoCipher> klass;
66  
67          private final String className;
68  
69          /**
70           * The private constructor.
71           * @param klass the Class of CryptoCipher
72           */
73          CipherProvider(final Class<? extends CryptoCipher> klass) {
74              this.klass = klass;
75              this.className = klass.getName();
76          }
77  
78          /**
79           * Gets the class name of the provider.
80           *
81           * @return the fully qualified name of the provider class
82           */
83          public String getClassName() {
84              return className;
85          }
86  
87          /**
88           * Gets the implementation class of the provider.
89           *
90           * @return the implementation class of the provider
91           */
92          public Class<? extends CryptoCipher> getImplClass() {
93              return klass;
94          }
95      }
96  
97      /**
98       * The configuration key of the provider class for JCE cipher.
99       */
100     public static final String JCE_PROVIDER_KEY = Crypto.CONF_PREFIX + "cipher.jce.provider";
101 
102     /**
103      * The configuration key of the CryptoCipher implementation class.
104      * <p>
105      * The value of CLASSES_KEY needs to be the full name of a
106      * class that implements the
107      * {@link org.apache.commons.crypto.cipher.CryptoCipher CryptoCipher} interface
108      * The internal classes are listed in the enum
109      * {@link CipherProvider CipherProvider}
110      * which can be used to obtain the full class name.
111      * </p>
112      * <p>
113      * The value can also be a comma-separated list of class names in
114      * order of descending priority.
115      * </p>
116      */
117     public static final String CLASSES_KEY = Crypto.CONF_PREFIX + "cipher.classes";
118 
119     /**
120      * For AES, the algorithm block is fixed size of 128 bits.
121      *
122      * @see <a href="http://en.wikipedia.org/wiki/Advanced_Encryption_Standard">
123      *      http://en.wikipedia.org/wiki/Advanced_Encryption_Standard</a>
124      */
125     public static final int AES_BLOCK_SIZE = 16;
126 
127     /**
128      * The default value (OPENSSL,JCE) for crypto cipher.
129      */
130     private static final String CLASSES_DEFAULT =
131             CipherProvider.OPENSSL.getClassName()
132             .concat(",")
133             .concat(CipherProvider.JCE.getClassName());
134 
135     /**
136      * Gets the cipher class.
137      *
138      * @param props The {@code Properties} class represents a set of
139      *        properties.
140      * @return the cipher class based on the props.
141      */
142     private static String getCipherClassString(final Properties props) {
143         String cipherClassString = props.getProperty(CryptoCipherFactory.CLASSES_KEY, CLASSES_DEFAULT);
144         if (cipherClassString.isEmpty()) { // TODO does it make sense to treat the empty string as the default?
145             cipherClassString = CLASSES_DEFAULT;
146         }
147         return cipherClassString;
148     }
149 
150     /**
151      * Gets a cipher for algorithm/mode/padding in config value
152      * commons.crypto.cipher.transformation
153      *
154      * @param transformation the name of the transformation, e.g.,
155      * <i>AES/CBC/PKCS5Padding</i>.
156      * See the Java Cryptography Architecture Standard Algorithm Name Documentation
157      * for information about standard transformation names.
158      * @return CryptoCipher the cipher object (defaults to OpenSslCipher if available, else JceCipher)
159      * @throws GeneralSecurityException if JCE cipher initialize failed
160      */
161     public static CryptoCipher getCryptoCipher(final String transformation)
162             throws GeneralSecurityException {
163         return getCryptoCipher(transformation, new Properties());
164     }
165 
166     /**
167      * Gets a cipher instance for specified algorithm/mode/padding.
168      *
169      * @param properties  the configuration properties - uses {@link #CLASSES_KEY}
170      * @param transformation  algorithm/mode/padding
171      * @return CryptoCipher  the cipher  (defaults to OpenSslCipher)
172      * @throws GeneralSecurityException if cipher initialize failed
173      * @throws IllegalArgumentException if no classname(s) were provided
174      */
175     public static CryptoCipher getCryptoCipher(final String transformation, final Properties properties) throws GeneralSecurityException {
176         final List<String> names = Utils.splitClassNames(getCipherClassString(properties), ",");
177         if (names.isEmpty()) {
178             throw new IllegalArgumentException("No classname(s) provided");
179         }
180         CryptoCipher cipher = null;
181         Exception lastException = null;
182 
183         final StringBuilder errorMessage = new StringBuilder("CryptoCipher ");
184         for (final String klass : names) {
185             try {
186                 final Class<?> cls = ReflectionUtils.getClassByName(klass);
187                 cipher = ReflectionUtils.newInstance(cls.asSubclass(CryptoCipher.class), properties, transformation);
188                 break;
189             } catch (final Exception e) {
190                 lastException = e;
191                 errorMessage.append("{" + klass + "}");
192             }
193         }
194 
195         if (cipher != null) {
196             return cipher;
197         }
198         errorMessage.append(" is not available or transformation " + transformation + " is not supported.");
199         throw new GeneralSecurityException(errorMessage.toString(), lastException);
200     }
201 
202     /**
203      * The private Constructor of {@link CryptoCipherFactory}.
204      */
205     private CryptoCipherFactory() {
206     }
207 
208 }