001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.commons.crypto.cipher;
019
020import java.security.GeneralSecurityException;
021import java.util.List;
022import java.util.Properties;
023
024import org.apache.commons.crypto.Crypto;
025import org.apache.commons.crypto.utils.ReflectionUtils;
026import org.apache.commons.crypto.utils.Utils;
027
028/**
029 * Creates {@link CryptoCipher} instances.
030 */
031public class CryptoCipherFactory {
032
033    /**
034     * Defines the internal CryptoCipher implementations.
035     * <p>
036     * Usage:
037     * </p>
038     * <blockquote><pre>
039     * props.setProperty(CryptoCipherFactory.CLASSES_KEY, CipherProvider.OPENSSL.getClassName());
040     * props.setProperty(...); // if required by the implementation
041     * cipher = CryptoCipherFactory.getInstance(transformation, props);
042     * </pre></blockquote>
043     */
044    public enum CipherProvider {
045
046        /**
047         * The OpenSSL cipher implementation (using JNI)
048         * <p>
049         * This implementation does not use any properties
050         * </p>
051         */
052        // Please ensure the property description agrees with the implementation
053        OPENSSL(OpenSslCipher.class),
054
055        /**
056         * The JCE cipher implementation from the JVM
057         * <p>
058         * uses the property {@link #JCE_PROVIDER_KEY}
059         * to define the provider name, if present.
060         * </p>
061         */
062        // Please ensure the property description agrees with the implementation
063        JCE(JceCipher.class);
064
065        private final Class<? extends CryptoCipher> klass;
066
067        private final String className;
068
069        /**
070         * The private constructor.
071         * @param klass the Class of CryptoCipher
072         */
073        CipherProvider(final Class<? extends CryptoCipher> klass) {
074            this.klass = klass;
075            this.className = klass.getName();
076        }
077
078        /**
079         * Gets the class name of the provider.
080         *
081         * @return the fully qualified name of the provider class
082         */
083        public String getClassName() {
084            return className;
085        }
086
087        /**
088         * Gets the implementation class of the provider.
089         *
090         * @return the implementation class of the provider
091         */
092        public Class<? extends CryptoCipher> getImplClass() {
093            return klass;
094        }
095    }
096
097    /**
098     * The configuration key of the provider class for JCE cipher.
099     */
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}