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    }