View Javadoc
1   package org.apache.fulcrum.jce.crypto.algo;
2   
3   import java.io.ByteArrayOutputStream;
4   
5   /*
6    * Licensed to the Apache Software Foundation (ASF) under one
7    * or more contributor license agreements.  See the NOTICE file
8    * distributed with this work for additional information
9    * regarding copyright ownership.  The ASF licenses this file
10   *  * to you under the Apache License, Version 2.0 (the
11   * "License"); you may not use this file except in compliance
12   * with the License.  You may obtain a copy of the License at
13   *
14   *   http://www.apache.org/licenses/LICENSE-2.0
15   *
16   * Unless required by applicable law or agreed to in writing,
17   * software distributed under the License is distributed on an
18   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
19   * KIND, either express or implied.  See the License for the
20   * specific language governing permissions and limitations
21   * under the License.
22   */
23  
24  import java.io.IOException;
25  import java.io.InputStream;
26  import java.nio.ByteBuffer;
27  import java.security.GeneralSecurityException;
28  import java.security.Key;
29  import java.security.NoSuchAlgorithmException;
30  import java.util.Arrays;
31  
32  import javax.crypto.Cipher;
33  import javax.crypto.SecretKeyFactory;
34  import javax.crypto.spec.IvParameterSpec;
35  import javax.crypto.spec.PBEKeySpec;
36  import javax.crypto.spec.PBEParameterSpec;
37  
38  import org.apache.fulcrum.jce.crypto.StreamUtil;
39  import org.apache.fulcrum.jce.crypto.extended.CryptoParametersJ8;
40  import org.apache.fulcrum.jce.crypto.extended.CryptoParametersJ8.TYPES;
41  import org.apache.fulcrum.jce.crypto.extended.CryptoStreamFactoryJ8Template;
42  
43  /**
44   * Concrete implementation for creating encrypting/decrypting streams. The
45   * implementation uses the JCA (Java Crypto Extension) supplied
46   * by SUN (using SunJCE 1.42).
47   *
48   * The implementation uses as @see {@link CryptoParametersJ8} ALGORITHM_J8_PBE for encryption which
49   * should be sufficient for most applications.
50   *
51   * The implementation also supplies a default password in the case that
52   * the programmer don't want to have additional hassles. It is easy to
53   * re-engineer the password being used but much better than a hard-coded
54   * password in the application.
55   *
56   * The code uses parts from Markus Hahn's Blowfish library found at
57   * http://blowfishj.sourceforge.net/
58   *
59   * @author <a href="mailto:gk@apache.org">Georg Kallidis</a>
60   * @author <a href="mailto:siegfried.goeschl@it20one.at">Siegfried Goeschl </a>
61   * @author <a href="mailto:maakus@earthlink.net">Markus Hahn</a>
62   * 
63   * The Implementation for {@link TYPES} PBE.
64   */
65  
66  public final class CryptoStreamPBEImpl extends CryptoStreamFactoryJ8Template
67  {
68  
69      protected static final int IV_SIZE = 16;
70      
71      protected static final int KEY_SIZE = 256;
72      
73  	/**
74  	 * default count for pbe spec
75  	 */
76  	protected static final int COUNT_J8 = 10_000; // 200_000;
77  
78      /**
79       * Constructor
80       * count is set to {@link #COUNT_J8}.
81       * 
82       * @throws GeneralSecurityException  if no algo could be found.
83       */
84      public CryptoStreamPBEImpl() throws GeneralSecurityException
85      {
86          this(generateSalt(), COUNT_J8);
87      }
88      
89      /**
90       * Constructor
91       *
92       * @param salt the salt for the PBE algorithm
93       */
94      public CryptoStreamPBEImpl( byte[] salt)
95      {
96          this(salt, COUNT_J8);
97          
98      }
99  
100     
101     /**
102      * Constructor
103      *
104      * @param salt the salt for the PBE algorithm
105      * @param count the iteration for PBEParameterSpec
106      */
107     public CryptoStreamPBEImpl( byte[] salt, int count)
108     {
109         setSalt(salt);
110         this.count = count;
111         this.providerName = PROVIDERNAME;
112         setType(TYPES.PBE);
113         this.algorithm = CryptoParametersJ8.TYPES_IMPL.ALGORITHM_J8_PBE.getAlgorithm();
114     }
115 
116     /**
117      * Create a PBE key.
118      *
119      * @param password the password to use.
120      * @param salt if provided this is used, otherweise {@link #getSalt()}.
121      * @return the key
122      * @throws GeneralSecurityException if creating the key failed
123      */
124     @Override
125     protected Key createKey( char[] password, byte[] salt ) 
126             throws GeneralSecurityException
127     {
128         SecretKeyFactory keyFactory;
129         String algorithm = this.getAlgorithm();
130         
131         PBEKeySpec keySpec = new PBEKeySpec(password, (salt == null)? this.getSalt(): salt.clone(), this.getCount(), KEY_SIZE );
132 
133         byte[] encodedTmp = null;
134         try {
135             if( this.getProviderName() == null )
136             {
137                 keyFactory = SecretKeyFactory.getInstance( algorithm );
138             }
139             else
140             {
141                 keyFactory = SecretKeyFactory.getInstance( algorithm, this.getProviderName() );
142             }
143             return keyFactory.generateSecret(keySpec);
144             
145         } catch (NoSuchAlgorithmException e) {
146             throw new GeneralSecurityException(e);
147         } finally {
148             if (encodedTmp != null) {
149                 Arrays.fill(encodedTmp, (byte)0); 
150             }
151             if (keySpec != null) {
152                 keySpec.clearPassword();
153             }
154         }
155     }
156 
157     /**
158      * Create a Cipher.
159      * 
160      * Find additional information here: {@link PBEParameterSpec}.
161      *
162      * @param mode the cipher mode
163      * @param password the password
164      * @return an instance of a cipher
165      * @throws GeneralSecurityException creating a cipher failed
166      * @throws IOException creating a cipher failed
167 
168      */
169     @Override
170     protected byte[] createCipher(InputStream is, int mode, char[] password )
171         throws GeneralSecurityException, IOException
172     {
173         Cipher cipher;
174         PBEParameterSpec paramSpec = null; 
175         
176         ByteArrayOutputStream bos = new ByteArrayOutputStream(1024);
177         StreamUtil.copy(is, bos);
178         byte[] input = bos.toByteArray();
179         
180         byte[] ciphertext = null;
181         
182         byte[] salt = null;
183         byte[] iv = null;
184         if (mode == Cipher.DECRYPT_MODE) {   
185             
186             ByteBuffer byteBuffer = ByteBuffer.wrap(input);
187             salt = new byte[SALT_SIZE ];
188             byteBuffer.get(salt);
189             iv = new byte[ IV_SIZE ];
190             byteBuffer.get(iv);
191             ciphertext = new byte[byteBuffer.remaining()];
192             byteBuffer.get(ciphertext);
193             
194 //            salt = Arrays.copyOfRange(input, 0, SALT_SIZE );
195 //            iv = Arrays.copyOfRange(input, salt.length, salt.length + 16 );
196 //            ciphertext = Arrays.copyOfRange(input, salt.length + iv.length, input.length);// cut out salt and iv
197         }
198         
199         Key key = this.createKey( password, salt );
200         
201         if( this.getProviderName() == null )
202         {
203             cipher = Cipher.getInstance( this.getAlgorithm() );
204         }
205         else
206         {
207             cipher = Cipher.getInstance( this.getAlgorithm(), this.getProviderName() );
208         }
209         
210         // save
211         if (mode == Cipher.DECRYPT_MODE) {
212            
213             paramSpec = new PBEParameterSpec( salt, this.getCount(), new IvParameterSpec(iv) );
214      
215             cipher.init( mode, key, paramSpec );
216             //cipher.init( mode, key, algorithmParameters );
217             ciphertext = cipher.doFinal(ciphertext); // actually the unencrypted bytes
218         }
219         
220         // save
221         if (mode == Cipher.ENCRYPT_MODE) {        
222             paramSpec = new PBEParameterSpec( this.getSalt(), this.getCount() );
223             salt = paramSpec.getSalt();
224             cipher.init( mode, key, paramSpec );   
225             //algorithmParameters = cipher.getParameters();
226  
227             byte[] result = cipher.doFinal(input);
228             iv = cipher.getIV(); // AES has 128bit block size, but iv is 16bit 
229            
230             // Salt and IV need to be stored with the result, otherwise we can't decrypt the message later.
231             ByteBuffer byteBuffer = ByteBuffer.allocate(salt.length + iv.length + result.length);
232             ciphertext = byteBuffer.put(salt).put(iv).put(result).array();
233             
234 //            ciphertext = new byte[salt.length + iv.length + result.length];         
235 //            System.arraycopy(salt, 0, ciphertext, 0, salt.length);
236 //            System.arraycopy(iv, 0, ciphertext, salt.length, iv.length);
237 //            System.arraycopy(result, 0, ciphertext, salt.length + iv.length, result.length);// push after salt and iv  
238         }
239         return ciphertext;
240     }
241 
242 }