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 }