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 }