View Javadoc
1   package org.jastacry.layer;
2   
3   import java.io.ByteArrayOutputStream;
4   import java.io.IOException;
5   import java.io.InputStream;
6   import java.io.OutputStream;
7   import java.security.InvalidAlgorithmParameterException;
8   import java.security.InvalidKeyException;
9   import java.security.NoSuchAlgorithmException;
10  import java.security.SecureRandom;
11  
12  import javax.crypto.BadPaddingException;
13  import javax.crypto.Cipher;
14  import javax.crypto.IllegalBlockSizeException;
15  import javax.crypto.NoSuchPaddingException;
16  import javax.crypto.SecretKey;
17  import javax.crypto.SecretKeyFactory;
18  import javax.crypto.spec.IvParameterSpec;
19  import javax.crypto.spec.PBEKeySpec;
20  import javax.crypto.spec.SecretKeySpec;
21  
22  import org.jastacry.JastacryException;
23  
24  /**
25   * Abstract base class for encryption.
26   *
27   * <p>SPDX-License-Identifier: MIT
28   *
29   * @author Kai Kretschmann
30   */
31  public abstract class AbstractCipherLayer extends AbstractBasicLayer
32  {
33  
34      /**
35       * Block size.
36       */
37      private static final int ONEBLOCKSIZE = 256;
38  
39      /**
40       * How many bits in a byte.
41       */
42      protected static final int BITSPERBYTE = 8;
43  
44      /**
45       * PBEKeySpec.
46       */
47      protected PBEKeySpec pbeKeySpec;
48  
49      /**
50       * SecretKeyFactory.
51       */
52      protected SecretKeyFactory keyFac;
53  
54      /**
55       * SecretKey.
56       */
57      protected SecretKey pbeKey;
58  
59      /**
60       * SecretKeySpec.
61       */
62      protected SecretKeySpec pbeSecretKeySpec;
63  
64      /**
65       * ALG for the data.
66       */
67      protected String strAlg;
68  
69      /**
70       * Algorithm for the key.
71       */
72      protected String strKeyAlg;
73  
74      /**
75       * char array of password.
76       */
77      protected char[] chPasswd;
78  
79      /**
80       * Iterations count as defined by child class.
81       */
82      protected int iterCount;
83  
84      /**
85       * Key size as defined by child class.
86       */
87      protected int currentKeysize;
88  
89      /**
90       * IV length.
91       */
92      protected int currentIvLen;
93  
94      /**
95       * IV bytes.
96       */
97      protected byte[] ivBytes;
98  
99      /**
100      * Salt length.
101      */
102     protected int currentSaltLen;
103 
104     /**
105      * salt.
106      */
107     protected byte[] salt;
108 
109     /**
110      * Constructor of abstract class.
111      *
112      * @param cClass class name of calling class
113      * @param layerName name of real layer
114      */
115     public AbstractCipherLayer(final Class<?> cClass, final String layerName)
116     {
117         super(cClass, layerName);
118     }
119 
120     /**
121      * Abstract base method for getting algorithm name back.
122      *
123      * @return String
124      */
125     protected abstract String getMyAlg();
126 
127     /**
128      * Abstract base method for getting key algorithm name back.
129      *
130      * @return String
131      */
132     protected abstract String getMyKeyAlg();
133 
134     /**
135      * Abstract base method for getting salt len back.
136      *
137      * @return int length
138      */
139     protected abstract int getMySaltLen();
140 
141     /**
142      * Abstract base method for getting IV length back.
143      *
144      * @return int length
145      */
146     protected abstract int getMyIvLen();
147 
148     /**
149      * Abstract base method for getting a counter back.
150      *
151      * @return int
152      */
153     protected abstract int getMyCount();
154 
155     /**
156      * Abstract base method for getting key size back.
157      *
158      * @return int length
159      */
160     protected abstract int getMyKeysize();
161 
162     /**
163      * Generate random salt.
164      */
165     protected final void getSalt()
166     {
167         salt = new byte[currentSaltLen];
168         new SecureRandom().nextBytes(salt);
169     }
170 
171     /**
172      * store IV bytes.
173      */
174     protected final void getIv()
175     {
176         ivBytes = new byte[currentIvLen];
177     }
178 
179     /**
180      * Set base values via own getters, which are defined in child classes.
181      */
182     protected final void init()
183     {
184         this.strAlg = getMyAlg();
185         this.strKeyAlg = getMyKeyAlg();
186         this.currentSaltLen = getMySaltLen();
187         this.currentIvLen = getMyIvLen();
188         this.iterCount = getMyCount();
189         this.currentKeysize = getMyKeysize();
190     }
191 
192     /**
193      * Generate Keys from plain password.
194      *
195      * @throws JastacryException on error
196      */
197     protected abstract void setupPbe() throws JastacryException;
198 
199     /**
200      * Write IV data if any.
201      * @param outputStream stream to write to
202      * @param pbeCipher the cipher object
203      * @throws IOException in case of error
204      */
205     private void writeIv(final OutputStream outputStream, final Cipher pbeCipher) throws IOException
206     {
207         if (0 == currentIvLen)
208         {
209             logger.trace("No IV to write");
210         }
211         else
212         {
213             ivBytes = pbeCipher.getIV();
214 
215             if (null == ivBytes)
216             {
217                 logger.error("IV empty");
218             }
219             else
220             {
221                 outputStream.write(ivBytes, 0, currentIvLen);
222             } // if
223         } // if
224     }
225 
226     /**
227      * Read IV data if any.
228      * @param inputStream stream to read from
229      * @throws IOException in case of error
230      */
231     private void readIv(final InputStream inputStream) throws IOException
232     {
233         int iReadBytes;
234         if (0 == currentIvLen)
235         {
236             logger.trace("No IV to read");
237         }
238         else
239         {
240             ivBytes = new byte[currentIvLen];
241             iReadBytes = inputStream.read(ivBytes, 0, currentIvLen);
242             if (currentIvLen != iReadBytes)
243             {
244                 logger.error("read {} bytes of IV, expecting {}.", iReadBytes, currentIvLen);
245             } // if
246         } // if
247     }
248 
249     /**
250      * Write salt to stream.
251      * @param outputStream to write to
252      * @throws IOException in case of error
253      */
254     private void writeSalt(final OutputStream outputStream) throws IOException
255     {
256         outputStream.write(salt, 0, currentSaltLen);
257     }
258 
259     /**
260      * Read salt from stream.
261      * @param inputStream to read from
262      * @throws IOException in case of error
263      */
264     private void readSalt(final InputStream inputStream) throws IOException
265     {
266         int iReadBytes;
267         salt = new byte[currentSaltLen];
268         iReadBytes = inputStream.read(salt, 0, currentSaltLen);
269         if (currentSaltLen != iReadBytes)
270         {
271             logger.error("read {} bytes of salt, expecting {}.", iReadBytes, currentSaltLen);
272         } // if
273     }
274 
275     /**
276      * encode Stream function.
277      *
278      * @param inputStream incoming data
279      * @param outputStream outgoing data
280      * @throws JastacryException thrown on error
281      */
282     @Override
283     public final void encStream(final InputStream inputStream, final OutputStream outputStream) throws JastacryException
284     {
285         Cipher pbeCipher;
286         try
287         {
288             getSalt();
289             setupPbe();
290 
291             pbeCipher = Cipher.getInstance(strAlg);
292             pbeCipher.init(Cipher.ENCRYPT_MODE, pbeSecretKeySpec);
293 
294             final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
295 
296             int nRead;
297             final byte[] data = new byte[ONEBLOCKSIZE];
298 
299             while ((nRead = inputStream.read(data, 0, data.length)) != -1)
300             {
301                 buffer.write(data, 0, nRead);
302             }
303 
304             buffer.flush();
305 
306             final byte[] bInput = buffer.toByteArray();
307 
308             // Encrypt the clear text
309             final byte[] ciphertext = pbeCipher.doFinal(bInput);
310             writeIv(outputStream, pbeCipher);
311             writeSalt(outputStream);
312 
313             outputStream.write(ciphertext);
314             outputStream.close();
315         }
316         catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException
317                 | JastacryException | BadPaddingException | IOException e)
318         {
319             logger.catching(e);
320             throw (JastacryExceptionl#JastacryException">JastacryException) new JastacryException("encStream failed").initCause(e);
321         }
322 
323     }
324 
325     /**
326      * decode Stream function.
327      *
328      * @param inputStream incoming data
329      * @param outputStream outgoing data
330      * @throws JastacryException thrown on error
331      */
332     @Override
333     public final void decStream(final InputStream inputStream, final OutputStream outputStream) throws JastacryException
334     {
335         // Create PBE Cipher
336         Cipher pbeCipher;
337         try
338         {
339             readIv(inputStream);
340             readSalt(inputStream);
341 
342             // call implementation of child class method.
343             setupPbe();
344 
345             pbeCipher = Cipher.getInstance(strAlg);
346             if (0 == currentIvLen)
347             {
348                 pbeCipher.init(Cipher.DECRYPT_MODE, pbeSecretKeySpec);
349             }
350             else
351             {
352                 pbeCipher.init(Cipher.DECRYPT_MODE, pbeSecretKeySpec, new IvParameterSpec(ivBytes));
353             } // if
354 
355             final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
356 
357             int nRead = 0;
358             final byte[] data = new byte[ONEBLOCKSIZE];
359 
360             while ((nRead = inputStream.read(data, 0, data.length)) != -1)
361             {
362                 buffer.write(data, 0, nRead);
363             }
364 
365             buffer.flush();
366 
367             final byte[] bInput = buffer.toByteArray();
368 
369             // Encrypt the clear text
370             final byte[] ciphertext = pbeCipher.doFinal(bInput);
371             outputStream.write(ciphertext);
372             outputStream.close();
373         }
374         catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException
375                 | BadPaddingException | InvalidAlgorithmParameterException | JastacryException | IOException e)
376         {
377             logger.catching(e);
378             throw (JastacryExceptionl#JastacryException">JastacryException) new JastacryException("decStream failed").initCause(e);
379         }
380     }
381 
382     @Override
383     public final void encodeAndDecode(final InputStream inputStream, final OutputStream outputStream) throws JastacryException
384     {
385         throw new UnsupportedOperationException();
386     }
387 
388 }