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.security.SecureRandom;
31  
32  import javax.crypto.Cipher;
33  import javax.crypto.SecretKey;
34  import javax.crypto.spec.GCMParameterSpec;
35  import javax.crypto.spec.SecretKeySpec;
36  
37  import org.apache.fulcrum.jce.crypto.StreamUtil;
38  import org.apache.fulcrum.jce.crypto.extended.CryptoParametersJ8;
39  import org.apache.fulcrum.jce.crypto.extended.CryptoStreamFactoryJ8Template;
40  import org.apache.fulcrum.jce.crypto.extended.CryptoParametersJ8.TYPES;
41  
42  /**
43   * Concrete implementation for creating encrypting/decrypting streams. The
44   * implementation uses the JCA (Java Crypto Extension) supplied
45   * by SUN (using SunJCE 1.42).
46   *
47   * The implementation uses @see {@link CryptoParametersJ8.TYPES_IMPL#ALGORITHM_J8_GCM} for encryption which
48   * should be sufficent for most applications.
49   *
50   * The implementation also supplies a default password in the case that
51   * the programmer don't want to have additional hassles. It is easy to
52   * reengineer the password being used but much better than a hard-coded
53   * password in the application.
54   *
55   *
56   * @author <a href="mailto:gk@apache.org">Georg Kallidis</a>
57   * 
58   * The Implementation for {@link TYPES} GCM.
59   */
60  
61  public final class CryptoStreamGCMImpl extends CryptoStreamFactoryJ8Template
62  {  
63  
64      protected static final int IV_SIZE = 12;
65      
66      /**
67       * Constructor
68       * @throws GeneralSecurityException  - wraps {@link NoSuchAlgorithmException}
69       */
70      public CryptoStreamGCMImpl() throws GeneralSecurityException
71      {
72          this(generateSalt());
73      }
74  
75  
76      /**
77       * Constructor
78       *
79       * @param salt the salt for the GCM algorithm
80  
81       */
82      public CryptoStreamGCMImpl( byte[] salt) 
83      {
84          setSalt(salt);
85          this.providerName = PROVIDERNAME;
86          setType(TYPES.GCM);
87          this.algorithm = CryptoParametersJ8.TYPES_IMPL.ALGORITHM_J8_GCM.getAlgorithm();
88      }
89  
90      /**
91       * Create a AES/GCM key.
92       *
93       * @param password the password to use.
94       * @param salt if provided this is used, otherweise {@link #getSalt()}.
95       * @return the key
96       * @throws GeneralSecurityException if creating the key failed
97       */
98      @Override
99      protected Key createKey( char[] password, byte[] salt ) 
100             throws GeneralSecurityException
101     {
102         SecretKey key = new SecretKeySpec(((salt == null)? this.getSalt(): salt.clone()), "AES");
103         return key;
104     }
105 
106     /**
107      * Create a Cipher.
108      * 
109      * Find additional information here: {@link GCMParameterSpec}
110      *
111      * @param mode the cipher mode
112      * @param password the password
113      * @return an instance of a cipher
114      * @throws GeneralSecurityException if creating a cipher failed
115      * @throws IOException creating a cipher failed
116      * 
117      */
118     @Override
119     protected byte[] createCipher(InputStream is, int mode, char[] password )
120         throws GeneralSecurityException, IOException
121     {
122         Cipher cipher;
123         
124         ByteArrayOutputStream bos = new ByteArrayOutputStream(1024);
125         StreamUtil.copy(is, bos);
126         byte[] input = bos.toByteArray();
127         
128         byte[] ciphertext = null;  
129         byte[] salt = null;
130         byte[] iv = null;
131         
132         if (mode == Cipher.DECRYPT_MODE) {   
133             
134             ByteBuffer byteBuffer = ByteBuffer.wrap(input);
135             salt = new byte[ SALT_SIZE ];
136             byteBuffer.get(salt);
137             iv = new byte[ IV_SIZE ];
138             byteBuffer.get(iv);
139             ciphertext = new byte[byteBuffer.remaining()];
140             byteBuffer.get(ciphertext);
141             
142 //            salt = Arrays.copyOfRange(input, 0, SALT_SIZE );
143 //            iv = Arrays.copyOfRange(input, salt.length, salt.length + 16 );
144 //            ciphertext = Arrays.copyOfRange(input, salt.length + iv.length, input.length);// cut out salt and iv
145         }
146         
147         Key key = this.createKey( password, salt );
148         
149         if( this.getProviderName() == null )
150         {
151             cipher = Cipher.getInstance( this.getAlgorithm() );
152         }
153         else
154         {
155             cipher = Cipher.getInstance( this.getAlgorithm(), this.getProviderName() );
156         }
157         
158         // save
159         if (mode == Cipher.DECRYPT_MODE) {
160             
161             GCMParameterSpec gcmParamSpec = new GCMParameterSpec(128, iv);
162             cipher.init( mode, key, gcmParamSpec );
163             
164             //cipher.init( mode, key, algorithmParameters );
165             ciphertext = cipher.doFinal(ciphertext); // actually the unencrypted bytes
166         }
167         
168         // save
169         if (mode == Cipher.ENCRYPT_MODE) {        
170             iv = generateIV();
171             GCMParameterSpec gcmParamSpec = new GCMParameterSpec(128, iv);
172             
173             salt = this.getSalt();
174             cipher.init( mode, key, gcmParamSpec );
175 
176             //algorithmParameters = cipher.getParameters();
177             // might update with associated Data
178             // cipher.updateAAD(associatedData );// not supported PBEWithHmacSHA256AndAES_256
179             
180             byte[] result = cipher.doFinal(input);
181             //iv = cipher.getIV(); // AES has 128bit block size, but iv is 16bit 
182            
183             // Salt and IV need to be stored with the result, otherwise we can't decrypt the message later.
184             ByteBuffer byteBuffer = ByteBuffer.allocate(salt.length + iv.length + result.length);
185             ciphertext = byteBuffer.put(salt).put(iv).put(result).array();
186             
187 //            ciphertext = new byte[salt.length + iv.length + result.length];
188 //            
189 //            System.arraycopy(salt, 0, ciphertext, 0, salt.length);
190 //            System.arraycopy(iv, 0, ciphertext, salt.length, iv.length);
191 //            System.arraycopy(result, 0, ciphertext, salt.length + iv.length, result.length);// push after salt and iv  
192         }
193         return ciphertext;
194     }
195     
196     /**
197      * 
198      * @return the random byte array from {@link SecureRandom} SHA1PRNG (as default)
199      * @throws GeneralSecurityException - if creating the key failed
200      */
201     private byte[] generateIV( ) throws GeneralSecurityException {
202         SecureRandom random;
203         try {
204             random = SecureRandom.getInstance("SHA1PRNG");
205             byte[] iv = new byte[ IV_SIZE ];
206             random.nextBytes(iv);
207             return iv;
208         } catch (NoSuchAlgorithmException e) {
209             throw new GeneralSecurityException(e);  
210         }
211     }
212 
213 
214 
215 }