001 package org.apache.fulcrum.jce.crypto; 002 003 /* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021 022 import java.io.IOException; 023 import java.io.InputStream; 024 import java.io.OutputStream; 025 import java.security.GeneralSecurityException; 026 import java.security.Key; 027 028 import javax.crypto.Cipher; 029 import javax.crypto.CipherInputStream; 030 import javax.crypto.CipherOutputStream; 031 import javax.crypto.SecretKeyFactory; 032 import javax.crypto.spec.PBEKeySpec; 033 import javax.crypto.spec.PBEParameterSpec; 034 035 /** 036 * Concrete factory for creating encrypting/decrypting streams. The 037 * implementation uses the JCA (Java Crypto Extension) supplied 038 * by SUN (using SunJCE 1.42). 039 * 040 * The implementation uses as PBEWithMD5AndDES for encryption which 041 * should be sufficent for most applications. 042 * 043 * The implementation also supplies a default password in the case that 044 * the programmer don't want to have additional hassles. It is easy to 045 * reengineer the password being used but much better than a hard-coded 046 * password in the application. 047 * 048 * The code uses parts from Markus Hahn's Blowfish library found at 049 * http://blowfishj.sourceforge.net/ 050 * 051 * @author <a href="mailto:siegfried.goeschl@it20one.at">Siegfried Goeschl </a> 052 * @author <a href="mailto:maakus@earthlink.net">Markus Hahn</a> 053 */ 054 055 public final class CryptoStreamFactoryImpl implements CryptoStreamFactory 056 { 057 /** the salt for the PBE algorithm */ 058 private byte[] salt; 059 060 /** the count paramter for the PBE algorithm */ 061 private int count; 062 063 /** the name of the JCE provider */ 064 private String providerName; 065 066 /** the algorithm to use */ 067 private String algorithm; 068 069 /** the default instance */ 070 private static CryptoStreamFactory instance; 071 072 /** 073 * The JCE provider name known to work. If the value 074 * is set to null an appropriate provider will be 075 * used. 076 */ 077 private static final String PROVIDERNAME = null; 078 079 /** 080 * Factory method to get a default instance 081 * @return an instance of the CryptoStreamFactory 082 */ 083 public synchronized static CryptoStreamFactory getInstance() 084 { 085 if( CryptoStreamFactoryImpl.instance == null ) 086 { 087 CryptoStreamFactoryImpl.instance = new CryptoStreamFactoryImpl(); 088 } 089 090 return CryptoStreamFactoryImpl.instance; 091 } 092 093 /** 094 * Set the default instance from an external application. 095 * @param instance the new default instance 096 */ 097 public static void setInstance( CryptoStreamFactory instance ) 098 { 099 CryptoStreamFactoryImpl.instance = instance; 100 } 101 102 /** 103 * Constructor 104 */ 105 public CryptoStreamFactoryImpl() 106 { 107 this.salt = CryptoParameters.SALT; 108 this.count = CryptoParameters.COUNT; 109 this.providerName = PROVIDERNAME; 110 this.algorithm = CryptoParameters.ALGORITHM; 111 } 112 113 /** 114 * Constructor 115 * 116 * @param salt the salt for the PBE algorithm 117 * @param count the iteration for PBEParameterSpec 118 */ 119 public CryptoStreamFactoryImpl( byte[] salt, int count) 120 { 121 this.salt = salt; 122 this.count = count; 123 this.providerName = PROVIDERNAME; 124 this.algorithm = CryptoParameters.ALGORITHM; 125 } 126 127 128 /** 129 * @see org.apache.fulcrum.jce.crypto.CryptoStreamFactory#getInputStream(java.io.InputStream, String) 130 */ 131 public InputStream getInputStream(InputStream is, String decryptionMode) throws GeneralSecurityException, IOException { 132 133 InputStream result = null; 134 135 if( "auto".equalsIgnoreCase(decryptionMode) ) 136 { 137 result = CryptoStreamFactoryImpl.getInstance().getSmartInputStream(is); 138 } 139 else if( "true".equalsIgnoreCase(decryptionMode) ) 140 { 141 result = CryptoStreamFactoryImpl.getInstance().getInputStream(is); 142 } 143 else 144 { 145 result = is; 146 } 147 return result; 148 } 149 150 /** 151 * @see org.apache.fulcrum.jce.crypto.CryptoStreamFactory#getInputStream(java.io.InputStream, String, char[]) 152 */ 153 public InputStream getInputStream(InputStream is, String decryptionMode, char[] password) throws GeneralSecurityException, IOException { 154 155 InputStream result = null; 156 157 if( "auto".equalsIgnoreCase(decryptionMode) ) 158 { 159 result = CryptoStreamFactoryImpl.getInstance().getSmartInputStream(is, password); 160 } 161 else if( "true".equalsIgnoreCase(decryptionMode) ) 162 { 163 result = CryptoStreamFactoryImpl.getInstance().getInputStream(is, password); 164 } 165 else 166 { 167 result = is; 168 } 169 return result; 170 } 171 172 /** 173 * @see org.apache.fulcrum.jce.crypto.CryptoStreamFactory#getInputStream(java.io.InputStream) 174 */ 175 public InputStream getInputStream( InputStream is ) 176 throws GeneralSecurityException, IOException 177 { 178 Cipher cipher = this.createCipher( Cipher.DECRYPT_MODE, PasswordFactory.create() ); 179 return new CipherInputStream( is, cipher ); 180 } 181 182 /** 183 * @see org.apache.fulcrum.jce.crypto.CryptoStreamFactory#getInputStream(java.io.InputStream,char[]) 184 */ 185 public InputStream getInputStream( InputStream is, char[] password ) 186 throws GeneralSecurityException, IOException 187 { 188 Cipher cipher = this.createCipher( Cipher.DECRYPT_MODE, password ); 189 return new CipherInputStream( is, cipher ); 190 } 191 192 /** 193 * @see org.apache.fulcrum.jce.crypto.CryptoStreamFactory#getSmartInputStream(java.io.InputStream) 194 */ 195 public InputStream getSmartInputStream(InputStream is) 196 throws GeneralSecurityException, IOException 197 { 198 return this.getSmartInputStream( 199 is, 200 PasswordFactory.create() 201 ); 202 } 203 204 /** 205 * @see org.apache.fulcrum.jce.crypto.CryptoStreamFactory#getSmartInputStream(java.io.InputStream,char[]) 206 */ 207 public InputStream getSmartInputStream(InputStream is, char[] password ) 208 throws GeneralSecurityException, IOException 209 { 210 SmartDecryptingInputStream result; 211 212 result = new SmartDecryptingInputStream( 213 getInstance(), 214 is, 215 password 216 ); 217 218 return result; 219 } 220 221 /** 222 * @see org.apache.fulcrum.jce.crypto.CryptoStreamFactory#getOutputStream(java.io.OutputStream) 223 */ 224 public OutputStream getOutputStream( OutputStream os ) 225 throws GeneralSecurityException, IOException 226 { 227 Cipher cipher = this.createCipher( Cipher.ENCRYPT_MODE, PasswordFactory.create() ); 228 return new CipherOutputStream( os, cipher ); } 229 230 /** 231 * @see org.apache.fulcrum.jce.crypto.CryptoStreamFactory#getOutputStream(java.io.OutputStream, char[]) 232 */ 233 public OutputStream getOutputStream( OutputStream os, char[] password ) 234 throws GeneralSecurityException, IOException 235 { 236 Cipher cipher = this.createCipher( Cipher.ENCRYPT_MODE, password ); 237 return new CipherOutputStream( os, cipher ); 238 } 239 240 /** 241 * @return Returns the algorithm. 242 */ 243 private String getAlgorithm() 244 { 245 return algorithm; 246 } 247 248 /** 249 * @return Returns the count. 250 */ 251 private int getCount() 252 { 253 return count; 254 } 255 256 /** 257 * @return Returns the providerName. 258 */ 259 private String getProviderName() 260 { 261 return providerName; 262 } 263 264 /** 265 * @return Returns the salt. 266 */ 267 private byte [] getSalt() 268 { 269 return salt; 270 } 271 272 /** 273 * Create a PBE key. 274 * 275 * @param password the password to use. 276 * @return the key 277 * @throws GeneralSecurityException creating the key failed 278 */ 279 private Key createKey( char[] password ) 280 throws GeneralSecurityException 281 { 282 SecretKeyFactory keyFactory; 283 String algorithm = this.getAlgorithm(); 284 PBEKeySpec keySpec = new PBEKeySpec(password); 285 286 if( this.getProviderName() == null ) 287 { 288 keyFactory = SecretKeyFactory.getInstance( algorithm ); 289 } 290 else 291 { 292 keyFactory = SecretKeyFactory.getInstance( algorithm, this.getProviderName() ); 293 } 294 295 return keyFactory.generateSecret(keySpec); 296 } 297 298 /** 299 * Create a Cipher. 300 * 301 * @param mode the cipher mode 302 * @param password the password 303 * @return an instance of a cipher 304 * @throws GeneralSecurityException creating a cipher failed 305 * @throws IOException creating a cipher failed 306 */ 307 private Cipher createCipher( int mode, char[] password ) 308 throws GeneralSecurityException, IOException 309 { 310 Cipher cipher; 311 PBEParameterSpec paramSpec = new PBEParameterSpec( this.getSalt(), this.getCount() ); 312 Key key = this.createKey( password ); 313 314 if( this.getProviderName() == null ) 315 { 316 cipher = Cipher.getInstance( this.getAlgorithm() ); 317 } 318 else 319 { 320 cipher = Cipher.getInstance( this.getAlgorithm(), this.getProviderName() ); 321 } 322 323 cipher.init( mode, key, paramSpec ); 324 return cipher; 325 } 326 }