1 /*
2  * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package com.sun.crypto.provider;
27 
28 import java.io.UnsupportedEncodingException;
29 import java.security.*;
30 import java.security.spec.*;
31 import javax.crypto.*;
32 import javax.crypto.spec.*;
33 
34 /**
35  * This class implements a proprietary password-based encryption algorithm.
36  * It is based on password-based encryption as defined by the PKCS #5
37  * standard, except that is uses triple DES instead of DES.
38  *
39  * Here's how this algorithm works:
40  *
41  * 1. Create random salt and split it in two halves. If the two halves are
42  *    identical, invert one of them.
43  * 2. Concatenate password with each of the halves.
44  * 3. Digest each concatenation with c iterations, where c is the
45  *    iterationCount. Concatenate the output from each digest round with the
46  *    password, and use the result as the input to the next digest operation.
47  *    The digest algorithm is MD5.
48  * 4. After c iterations, use the 2 resulting digests as follows:
49  *    The 16 bytes of the first digest and the 1st 8 bytes of the 2nd digest
50  *    form the triple DES key, and the last 8 bytes of the 2nd digest form the
51  *    IV.
52  *
53  * @author Jan Luehe
54  * @see javax.crypto.Cipher
55  */
56 public final class PBEWithMD5AndTripleDESCipher extends CipherSpi {
57 
58     private PBES1Core core;
59 
60     /**
61      * Creates an instance of this cipher, and initializes its mode (CBC) and
62      * padding (PKCS5).
63      *
64      * @exception NoSuchAlgorithmException if the required cipher mode (CBC) is
65      * unavailable
66      * @exception NoSuchPaddingException if the required padding mechanism
67      * (PKCS5Padding) is unavailable
68      */
PBEWithMD5AndTripleDESCipher()69     public PBEWithMD5AndTripleDESCipher()
70         throws NoSuchAlgorithmException, NoSuchPaddingException
71     {
72         // set the encapsulated cipher to do triple DES
73         core = new PBES1Core("DESede");
74     }
75 
76     /**
77      * Sets the mode of this cipher. This algorithm can only be run in CBC
78      * mode.
79      *
80      * @param mode the cipher mode
81      *
82      * @exception NoSuchAlgorithmException if the requested cipher mode is
83      * invalid
84      */
engineSetMode(String mode)85     protected void engineSetMode(String mode) throws NoSuchAlgorithmException {
86         if ((mode != null) && (!mode.equalsIgnoreCase("CBC"))) {
87             throw new NoSuchAlgorithmException("Invalid cipher mode: " + mode);
88         }
89     }
90 
91      /**
92      * Sets the padding mechanism of this cipher. This algorithm only uses
93      * PKCS #5 padding.
94      *
95      * @param paddingScheme the padding mechanism
96      *
97      * @exception NoSuchPaddingException if the requested padding mechanism
98      * is invalid
99      */
engineSetPadding(String paddingScheme)100     protected void engineSetPadding(String paddingScheme)
101         throws NoSuchPaddingException
102     {
103         if ((paddingScheme != null) &&
104             (!paddingScheme.equalsIgnoreCase("PKCS5Padding"))) {
105             throw new NoSuchPaddingException("Invalid padding scheme: " +
106                                              paddingScheme);
107         }
108     }
109 
110     /**
111      * Returns the block size (in bytes).
112      *
113      * @return the block size (in bytes)
114      */
engineGetBlockSize()115     protected int engineGetBlockSize() {
116         return core.getBlockSize();
117     }
118 
119     /**
120      * Returns the length in bytes that an output buffer would need to be in
121      * order to hold the result of the next <code>update</code> or
122      * <code>doFinal</code> operation, given the input length
123      * <code>inputLen</code> (in bytes).
124      *
125      * <p>This call takes into account any unprocessed (buffered) data from a
126      * previous <code>update</code> call, and padding.
127      *
128      * <p>The actual output length of the next <code>update</code> or
129      * <code>doFinal</code> call may be smaller than the length returned by
130      * this method.
131      *
132      * @param inputLen the input length (in bytes)
133      *
134      * @return the required output buffer size (in bytes)
135      *
136      */
engineGetOutputSize(int inputLen)137     protected int engineGetOutputSize(int inputLen) {
138         return core.getOutputSize(inputLen);
139     }
140 
141     /**
142      * Returns the initialization vector (IV) in a new buffer.
143      *
144      * <p> This is useful in the case where a random IV has been created
145      * (see <a href = "#init">init</a>),
146      * or in the context of password-based encryption or
147      * decryption, where the IV is derived from a user-supplied password.
148      *
149      * @return the initialization vector in a new buffer, or null if the
150      * underlying algorithm does not use an IV, or if the IV has not yet
151      * been set.
152      */
engineGetIV()153     protected byte[] engineGetIV() {
154         return core.getIV();
155     }
156 
157     /**
158      * Returns the parameters used with this cipher.
159      *
160      * <p>The returned parameters may be the same that were used to initialize
161      * this cipher, or may contain the default set of parameters or a set of
162      * randomly generated parameters used by the underlying cipher
163      * implementation (provided that the underlying cipher implementation
164      * uses a default set of parameters or creates new parameters if it needs
165      * parameters but was not initialized with any).
166      *
167      * @return the parameters used with this cipher, or null if this cipher
168      * does not use any parameters.
169      */
engineGetParameters()170     protected AlgorithmParameters engineGetParameters() {
171         return core.getParameters();
172     }
173 
174     /**
175      * Initializes this cipher with a key and a source
176      * of randomness.
177      * The cipher is initialized for one of the following four operations:
178      * encryption, decryption, key wrapping or key unwrapping, depending on
179      * the value of <code>opmode</code>.
180      *
181      * <p>If this cipher (including its underlying feedback or padding scheme)
182      * requires any random bytes, it will get them from <code>random</code>.
183      *
184      * @param opmode the operation mode of this cipher (this is one of
185      * the following:
186      * <code>ENCRYPT_MODE</code>, <code>DECRYPT_MODE</code>),
187      * <code>WRAP_MODE</code> or <code>UNWRAP_MODE</code>)
188      * @param key the encryption key
189      * @param random the source of randomness
190      *
191      * @exception InvalidKeyException if the given key is inappropriate for
192      * initializing this cipher
193      */
engineInit(int opmode, Key key, SecureRandom random)194     protected void engineInit(int opmode, Key key, SecureRandom random)
195         throws InvalidKeyException {
196         try {
197             core.init(opmode, key, (AlgorithmParameterSpec) null, random);
198         } catch (InvalidAlgorithmParameterException ie) {
199             InvalidKeyException ike =
200                 new InvalidKeyException("requires PBE parameters");
201             ike.initCause(ie);
202             throw ike;
203         }
204     }
205 
206     /**
207      * Initializes this cipher with a key, a set of
208      * algorithm parameters, and a source of randomness.
209      * The cipher is initialized for encryption or decryption, depending on
210      * the value of <code>opmode</code>.
211      *
212      * <p>If this cipher (including its underlying feedback or padding scheme)
213      * requires any random bytes, it will get them from <code>random</code>.
214      *
215      * @param opmode the operation mode of this cipher (this is either
216      * <code>ENCRYPT_MODE</code> or <code>DECRYPT_MODE</code>)
217      * @param key the encryption key
218      * @param params the algorithm parameters
219      * @param random the source of randomness
220      *
221      * @exception InvalidKeyException if the given key is inappropriate for
222      * initializing this cipher
223      * @exception InvalidAlgorithmParameterException if the given algorithm
224      * parameters are inappropriate for this cipher
225      */
engineInit(int opmode, Key key, AlgorithmParameterSpec params, SecureRandom random)226     protected void engineInit(int opmode, Key key,
227                               AlgorithmParameterSpec params,
228                               SecureRandom random)
229         throws InvalidKeyException, InvalidAlgorithmParameterException {
230         core.init(opmode, key, params, random);
231     }
232 
engineInit(int opmode, Key key, AlgorithmParameters params, SecureRandom random)233     protected void engineInit(int opmode, Key key,
234                               AlgorithmParameters params,
235                               SecureRandom random)
236         throws InvalidKeyException, InvalidAlgorithmParameterException
237     {
238         core.init(opmode, key, params, random);
239     }
240 
241     /**
242      * Continues a multiple-part encryption or decryption operation
243      * (depending on how this cipher was initialized), processing another data
244      * part.
245      *
246      * <p>The first <code>inputLen</code> bytes in the <code>input</code>
247      * buffer, starting at <code>inputOffset</code>, are processed, and the
248      * result is stored in a new buffer.
249      *
250      * @param input the input buffer
251      * @param inputOffset the offset in <code>input</code> where the input
252      * starts
253      * @param inputLen the input length
254      *
255      * @return the new buffer with the result
256      *
257      */
engineUpdate(byte[] input, int inputOffset, int inputLen)258     protected byte[] engineUpdate(byte[] input, int inputOffset, int inputLen)
259     {
260         return core.update(input, inputOffset, inputLen);
261     }
262 
263     /**
264      * Continues a multiple-part encryption or decryption operation
265      * (depending on how this cipher was initialized), processing another data
266      * part.
267      *
268      * <p>The first <code>inputLen</code> bytes in the <code>input</code>
269      * buffer, starting at <code>inputOffset</code>, are processed, and the
270      * result is stored in the <code>output</code> buffer, starting at
271      * <code>outputOffset</code>.
272      *
273      * @param input the input buffer
274      * @param inputOffset the offset in <code>input</code> where the input
275      * starts
276      * @param inputLen the input length
277      * @param output the buffer for the result
278      * @param outputOffset the offset in <code>output</code> where the result
279      * is stored
280      *
281      * @return the number of bytes stored in <code>output</code>
282      *
283      * @exception ShortBufferException if the given output buffer is too small
284      * to hold the result
285      */
engineUpdate(byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset)286     protected int engineUpdate(byte[] input, int inputOffset, int inputLen,
287                                byte[] output, int outputOffset)
288         throws ShortBufferException
289     {
290         return core.update(input, inputOffset, inputLen,
291                            output, outputOffset);
292     }
293 
294     /**
295      * Encrypts or decrypts data in a single-part operation,
296      * or finishes a multiple-part operation.
297      * The data is encrypted or decrypted, depending on how this cipher was
298      * initialized.
299      *
300      * <p>The first <code>inputLen</code> bytes in the <code>input</code>
301      * buffer, starting at <code>inputOffset</code>, and any input bytes that
302      * may have been buffered during a previous <code>update</code> operation,
303      * are processed, with padding (if requested) being applied.
304      * The result is stored in a new buffer.
305      *
306      * <p>The cipher is reset to its initial state (uninitialized) after this
307      * call.
308      *
309      * @param input the input buffer
310      * @param inputOffset the offset in <code>input</code> where the input
311      * starts
312      * @param inputLen the input length
313      *
314      * @return the new buffer with the result
315      *
316      * @exception IllegalBlockSizeException if this cipher is a block cipher,
317      * no padding has been requested (only in encryption mode), and the total
318      * input length of the data processed by this cipher is not a multiple of
319      * block size
320      * @exception BadPaddingException if decrypting and padding is chosen,
321      * but the last input data does not have proper padding bytes.
322      */
engineDoFinal(byte[] input, int inputOffset, int inputLen)323     protected byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen)
324         throws IllegalBlockSizeException, BadPaddingException
325     {
326         return core.doFinal(input, inputOffset, inputLen);
327     }
328 
329     /**
330      * Encrypts or decrypts data in a single-part operation,
331      * or finishes a multiple-part operation.
332      * The data is encrypted or decrypted, depending on how this cipher was
333      * initialized.
334      *
335      * <p>The first <code>inputLen</code> bytes in the <code>input</code>
336      * buffer, starting at <code>inputOffset</code>, and any input bytes that
337      * may have been buffered during a previous <code>update</code> operation,
338      * are processed, with padding (if requested) being applied.
339      * The result is stored in the <code>output</code> buffer, starting at
340      * <code>outputOffset</code>.
341      *
342      * <p>The cipher is reset to its initial state (uninitialized) after this
343      * call.
344      *
345      * @param input the input buffer
346      * @param inputOffset the offset in <code>input</code> where the input
347      * starts
348      * @param inputLen the input length
349      * @param output the buffer for the result
350      * @param outputOffset the offset in <code>output</code> where the result
351      * is stored
352      *
353      * @return the number of bytes stored in <code>output</code>
354      *
355      * @exception IllegalBlockSizeException if this cipher is a block cipher,
356      * no padding has been requested (only in encryption mode), and the total
357      * input length of the data processed by this cipher is not a multiple of
358      * block size
359      * @exception ShortBufferException if the given output buffer is too small
360      * to hold the result
361      * @exception BadPaddingException if decrypting and padding is chosen,
362      * but the last input data does not have proper padding bytes.
363      */
engineDoFinal(byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset)364     protected int engineDoFinal(byte[] input, int inputOffset, int inputLen,
365                                 byte[] output, int outputOffset)
366         throws ShortBufferException, IllegalBlockSizeException,
367                BadPaddingException
368     {
369         return core.doFinal(input, inputOffset, inputLen,
370                             output, outputOffset);
371     }
372 
373     /**
374      *  Returns the key size of the given key object.
375      *
376      * @param key the key object.
377      *
378      * @return the key size of the given key object.
379      *
380      * @exception InvalidKeyException if <code>key</code> is invalid.
381      */
engineGetKeySize(Key key)382     protected int engineGetKeySize(Key key) throws InvalidKeyException {
383         return 168;
384     }
385 
386     /**
387      * Wrap a key.
388      *
389      * @param key the key to be wrapped.
390      *
391      * @return the wrapped key.
392      *
393      * @exception IllegalBlockSizeException if this cipher is a block
394      * cipher, no padding has been requested, and the length of the
395      * encoding of the key to be wrapped is not a
396      * multiple of the block size.
397      *
398      * @exception InvalidKeyException if it is impossible or unsafe to
399      * wrap the key with this cipher (e.g., a hardware protected key is
400      * being passed to a software only cipher).
401      */
engineWrap(Key key)402     protected byte[] engineWrap(Key key)
403         throws IllegalBlockSizeException, InvalidKeyException {
404         return core.wrap(key);
405     }
406 
407     /**
408      * Unwrap a previously wrapped key.
409      *
410      * @param wrappedKey the key to be unwrapped.
411      *
412      * @param wrappedKeyAlgorithm the algorithm the wrapped key is for.
413      *
414      * @param wrappedKeyType the type of the wrapped key.
415      * This is one of <code>Cipher.SECRET_KEY</code>,
416      * <code>Cipher.PRIVATE_KEY</code>, or <code>Cipher.PUBLIC_KEY</code>.
417      *
418      * @return the unwrapped key.
419      *
420      * @exception NoSuchAlgorithmException if no installed providers
421      * can create keys of type <code>wrappedKeyType</code> for the
422      * <code>wrappedKeyAlgorithm</code>.
423      *
424      * @exception InvalidKeyException if <code>wrappedKey</code> does not
425      * represent a wrapped key of type <code>wrappedKeyType</code> for
426      * the <code>wrappedKeyAlgorithm</code>.
427      */
engineUnwrap(byte[] wrappedKey, String wrappedKeyAlgorithm, int wrappedKeyType)428     protected Key engineUnwrap(byte[] wrappedKey,
429                                      String wrappedKeyAlgorithm,
430                                      int wrappedKeyType)
431         throws InvalidKeyException, NoSuchAlgorithmException {
432         return core.unwrap(wrappedKey, wrappedKeyAlgorithm,
433                            wrappedKeyType);
434     }
435 }
436