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 * This is the factory class used for creating {@link CryptoCipher} instances. 030 */ 031public class CryptoCipherFactory { 032 033 /** 034 * The configuration key of the provider class for JCE cipher. 035 */ 036 public static final String JCE_PROVIDER_KEY = Crypto.CONF_PREFIX 037 + "cipher.jce.provider"; 038 /** 039 * The configuration key of the CryptoCipher implementation class. 040 * <p> 041 * The value of CLASSES_KEY needs to be the full name of a 042 * class that implements the 043 * {@link org.apache.commons.crypto.cipher.CryptoCipher CryptoCipher} interface 044 * The internal classes are listed in the enum 045 * {@link CipherProvider CipherProvider} 046 * which can be used to obtain the full class name. 047 * <p> 048 * The value can also be a comma-separated list of class names in 049 * order of descending priority. 050 */ 051 052 public static final String CLASSES_KEY = Crypto.CONF_PREFIX 053 + "cipher.classes"; 054 055 /** 056 * Defines the internal CryptoCipher implementations. 057 * <p> 058 * Usage: 059 * <blockquote><pre> 060 * props.setProperty(CryptoCipherFactory.CLASSES_KEY, CipherProvider.OPENSSL.getClassName()); 061 * props.setProperty(...); // if required by the implementation 062 * cipher = CryptoCipherFactory.getInstance(transformation, props); 063 * </pre></blockquote> 064 */ 065 public enum CipherProvider { 066 067 /** 068 * The OpenSSL cipher implementation (using JNI) 069 * <p> 070 * This implementation does not use any properties 071 */ 072 // Please ensure the property description agrees with the implementation 073 OPENSSL(OpenSslCipher.class), 074 075 /** 076 * The JCE cipher implementation from the JVM 077 * <p> 078 * uses the property {@link #JCE_PROVIDER_KEY} 079 * to define the provider name, if present. 080 */ 081 // Please ensure the property description agrees with the implementation 082 JCE(JceCipher.class); 083 084 private final Class<? extends CryptoCipher> klass; 085 086 private final String className; 087 088 /** 089 * The private constructor. 090 * @param klass the Class of CryptoCipher 091 */ 092 private CipherProvider(final Class<? extends CryptoCipher> klass) { 093 this.klass = klass; 094 this.className = klass.getName(); 095 } 096 097 /** 098 * Gets the class name of the provider. 099 * 100 * @return the fully qualified name of the provider class 101 */ 102 public String getClassName() { 103 return className; 104 } 105 106 /** 107 * Gets the implementation class of the provider. 108 * 109 * @return the implementation class of the provider 110 */ 111 public Class<? extends CryptoCipher> getImplClass() { 112 return klass; 113 } 114 } 115 116 /** 117 * For AES, the algorithm block is fixed size of 128 bits. 118 * 119 * @see <a href="http://en.wikipedia.org/wiki/Advanced_Encryption_Standard"> 120 * http://en.wikipedia.org/wiki/Advanced_Encryption_Standard</a> 121 */ 122 public static final int AES_BLOCK_SIZE = 16; 123 124 /** 125 * The default value (OPENSSL,JCE) for crypto cipher. 126 */ 127 private static final String CLASSES_DEFAULT = 128 CipherProvider.OPENSSL.getClassName() 129 .concat(",") 130 .concat(CipherProvider.JCE.getClassName()); 131 132 /** 133 * The private Constructor of {@link CryptoCipherFactory}. 134 */ 135 private CryptoCipherFactory() { 136 } 137 138 /** 139 * Gets a cipher instance for specified algorithm/mode/padding. 140 * 141 * @param properties the configuration properties - uses {@link #CLASSES_KEY} 142 * @param transformation algorithm/mode/padding 143 * @return CryptoCipher the cipher (defaults to OpenSslCipher) 144 * @throws GeneralSecurityException if cipher initialize failed 145 * @throws IllegalArgumentException if no classname(s) were provided 146 */ 147 public static CryptoCipher getCryptoCipher(final String transformation, final Properties properties) 148 throws GeneralSecurityException { 149 150 final List<String> names = Utils.splitClassNames(getCipherClassString(properties), ","); 151 if (names.size() == 0) { 152 throw new IllegalArgumentException("No classname(s) provided"); 153 } 154 CryptoCipher cipher = null; 155 Exception lastException = null; 156 157 final StringBuilder errorMessage = new StringBuilder("CryptoCipher "); 158 for (final String klass : names) { 159 try { 160 final Class<?> cls = ReflectionUtils.getClassByName(klass); 161 cipher = ReflectionUtils.newInstance(cls.asSubclass 162 (CryptoCipher.class), properties, transformation); 163 if (cipher != null) { 164 break; 165 } 166 } catch (final Exception e) { 167 lastException = e; 168 errorMessage.append("{" + klass + "}"); 169 } 170 } 171 172 if (cipher != null) { 173 return cipher; 174 } 175 errorMessage.append(" is not available or transformation " + 176 transformation + " is not supported."); 177 throw new GeneralSecurityException(errorMessage.toString(), lastException); 178 } 179 180 /** 181 * Gets a cipher for algorithm/mode/padding in config value 182 * commons.crypto.cipher.transformation 183 * 184 * @param transformation the name of the transformation, e.g., 185 * <i>AES/CBC/PKCS5Padding</i>. 186 * See the Java Cryptography Architecture Standard Algorithm Name Documentation 187 * for information about standard transformation names. 188 * @return CryptoCipher the cipher object (defaults to OpenSslCipher if available, else JceCipher) 189 * @throws GeneralSecurityException if JCE cipher initialize failed 190 */ 191 public static CryptoCipher getCryptoCipher(final String transformation) 192 throws GeneralSecurityException { 193 return getCryptoCipher(transformation, new Properties()); 194 } 195 196 /** 197 * Gets the cipher class. 198 * 199 * @param props The {@code Properties} class represents a set of 200 * properties. 201 * @return the cipher class based on the props. 202 */ 203 private static String getCipherClassString(final Properties props) { 204 String cipherClassString = props.getProperty(CryptoCipherFactory.CLASSES_KEY, CLASSES_DEFAULT); 205 if (cipherClassString.isEmpty()) { // TODO does it make sense to treat the empty string as the default? 206 cipherClassString = CLASSES_DEFAULT; 207 } 208 return cipherClassString; 209 } 210 211}