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