1 /*
2  * Copyright (c) 2002, 2017, 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 java.util.Arrays;
31 import javax.crypto.*;
32 import javax.crypto.spec.*;
33 
34 /**
35  * This class represents password-based encryption as defined by the PKCS #5
36  * standard.
37  *
38  * @author Jan Luehe
39  *
40  *
41  * @see javax.crypto.Cipher
42  */
43 final class PBES1Core {
44 
45     // the encapsulated DES cipher
46     private CipherCore cipher;
47     private MessageDigest md;
48     private int blkSize;
49     private String algo = null;
50     private byte[] salt = null;
51     private int iCount = 10;
52 
53     /**
54      * Creates an instance of PBE Cipher using the specified CipherSpi
55      * instance.
56      *
57      */
PBES1Core(String cipherAlg)58     PBES1Core(String cipherAlg) throws NoSuchAlgorithmException,
59         NoSuchPaddingException {
60         algo = cipherAlg;
61         if (algo.equals("DES")) {
62             cipher = new CipherCore(new DESCrypt(),
63                                     DESConstants.DES_BLOCK_SIZE);
64         } else if (algo.equals("DESede")) {
65 
66             cipher = new CipherCore(new DESedeCrypt(),
67                                     DESConstants.DES_BLOCK_SIZE);
68         } else {
69             throw new NoSuchAlgorithmException("No Cipher implementation " +
70                                                "for PBEWithMD5And" + algo);
71         }
72         cipher.setMode("CBC");
73         cipher.setPadding("PKCS5Padding");
74         // get instance of MD5
75         md = MessageDigest.getInstance("MD5");
76     }
77 
78     /**
79      * Sets the mode of this cipher. This algorithm can only be run in CBC
80      * mode.
81      *
82      * @param mode the cipher mode
83      *
84      * @exception NoSuchAlgorithmException if the requested cipher mode is
85      * invalid
86      */
setMode(String mode)87     void setMode(String mode) throws NoSuchAlgorithmException {
88         cipher.setMode(mode);
89     }
90 
91      /**
92      * Sets the padding mechanism of this cipher. This algorithm only uses
93      * PKCS #5 padding.
94      *
95      * @param padding the padding mechanism
96      *
97      * @exception NoSuchPaddingException if the requested padding mechanism
98      * is invalid
99      */
setPadding(String paddingScheme)100     void setPadding(String paddingScheme) throws NoSuchPaddingException {
101         cipher.setPadding(paddingScheme);
102     }
103 
104     /**
105      * Returns the block size (in bytes).
106      *
107      * @return the block size (in bytes)
108      */
getBlockSize()109     int getBlockSize() {
110         return DESConstants.DES_BLOCK_SIZE;
111     }
112 
113     /**
114      * Returns the length in bytes that an output buffer would need to be in
115      * order to hold the result of the next <code>update</code> or
116      * <code>doFinal</code> operation, given the input length
117      * <code>inputLen</code> (in bytes).
118      *
119      * <p>This call takes into account any unprocessed (buffered) data from a
120      * previous <code>update</code> call, and padding.
121      *
122      * <p>The actual output length of the next <code>update</code> or
123      * <code>doFinal</code> call may be smaller than the length returned by
124      * this method.
125      *
126      * @param inputLen the input length (in bytes)
127      *
128      * @return the required output buffer size (in bytes)
129      *
130      */
getOutputSize(int inputLen)131     int getOutputSize(int inputLen) {
132         return cipher.getOutputSize(inputLen);
133     }
134 
135     /**
136      * Returns the initialization vector (IV) in a new buffer.
137      *
138      * <p> This is useful in the case where a random IV has been created
139      * (see <a href = "#init">init</a>),
140      * or in the context of password-based encryption or
141      * decryption, where the IV is derived from a user-supplied password.
142      *
143      * @return the initialization vector in a new buffer, or null if the
144      * underlying algorithm does not use an IV, or if the IV has not yet
145      * been set.
146      */
getIV()147     byte[] getIV() {
148         return cipher.getIV();
149     }
150 
151     /**
152      * Returns the parameters used with this cipher.
153      *
154      * <p>The returned parameters may be the same that were used to initialize
155      * this cipher, or may contain the default set of parameters or a set of
156      * randomly generated parameters used by the underlying cipher
157      * implementation (provided that the underlying cipher implementation
158      * uses a default set of parameters or creates new parameters if it needs
159      * parameters but was not initialized with any).
160      *
161      * @return the parameters used with this cipher, or null if this cipher
162      * does not use any parameters.
163      */
getParameters()164     AlgorithmParameters getParameters() {
165         AlgorithmParameters params = null;
166         if (salt == null) {
167             salt = new byte[8];
168             SunJCE.getRandom().nextBytes(salt);
169         }
170         PBEParameterSpec pbeSpec = new PBEParameterSpec(salt, iCount);
171         try {
172             params = AlgorithmParameters.getInstance("PBEWithMD5And" +
173                 (algo.equalsIgnoreCase("DES")? "DES":"TripleDES"),
174                 SunJCE.getInstance());
175             params.init(pbeSpec);
176         } catch (NoSuchAlgorithmException nsae) {
177             // should never happen
178             throw new RuntimeException("SunJCE called, but not configured");
179         } catch (InvalidParameterSpecException ipse) {
180             // should never happen
181             throw new RuntimeException("PBEParameterSpec not supported");
182         }
183         return params;
184     }
185 
186     /**
187      * Initializes this cipher with a key, a set of
188      * algorithm parameters, and a source of randomness.
189      * The cipher is initialized for one of the following four operations:
190      * encryption, decryption, key wrapping or key unwrapping, depending on
191      * the value of <code>opmode</code>.
192      *
193      * <p>If this cipher (including its underlying feedback or padding scheme)
194      * requires any random bytes, it will get them from <code>random</code>.
195      *
196      * @param opmode the operation mode of this cipher (this is one of
197      * the following:
198      * <code>ENCRYPT_MODE</code>, <code>DECRYPT_MODE</code>),
199      * <code>WRAP_MODE</code> or <code>UNWRAP_MODE</code>)
200      * @param key the encryption key
201      * @param params the algorithm parameters
202      * @param random the source of randomness
203      *
204      * @exception InvalidKeyException if the given key is inappropriate for
205      * initializing this cipher
206      * @exception InvalidAlgorithmParameterException if the given algorithm
207      * parameters are inappropriate for this cipher
208      */
init(int opmode, Key key, AlgorithmParameterSpec params, SecureRandom random)209     void init(int opmode, Key key, AlgorithmParameterSpec params,
210               SecureRandom random)
211         throws InvalidKeyException, InvalidAlgorithmParameterException {
212         if (((opmode == Cipher.DECRYPT_MODE) ||
213              (opmode == Cipher.UNWRAP_MODE)) && (params == null)) {
214             throw new InvalidAlgorithmParameterException("Parameters "
215                                                          + "missing");
216         }
217         if (key == null) {
218             throw new InvalidKeyException("Null key");
219         }
220 
221         byte[] derivedKey;
222         byte[] passwdBytes = key.getEncoded();
223         try {
224             if ((passwdBytes == null) ||
225                     !(key.getAlgorithm().regionMatches(true, 0, "PBE", 0, 3))) {
226                 throw new InvalidKeyException("Missing password");
227             }
228 
229             if (params == null) {
230                 // create random salt and use default iteration count
231                 salt = new byte[8];
232                 random.nextBytes(salt);
233             } else {
234                 if (!(params instanceof PBEParameterSpec)) {
235                     throw new InvalidAlgorithmParameterException
236                             ("Wrong parameter type: PBE expected");
237                 }
238                 salt = ((PBEParameterSpec) params).getSalt();
239                 // salt must be 8 bytes long (by definition)
240                 if (salt.length != 8) {
241                     throw new InvalidAlgorithmParameterException
242                             ("Salt must be 8 bytes long");
243                 }
244                 iCount = ((PBEParameterSpec) params).getIterationCount();
245                 if (iCount <= 0) {
246                     throw new InvalidAlgorithmParameterException
247                             ("IterationCount must be a positive number");
248                 }
249             }
250             derivedKey = deriveCipherKey(passwdBytes);
251         } finally {
252             if (passwdBytes != null) Arrays.fill(passwdBytes, (byte) 0x00);
253         }
254         // use all but the last 8 bytes as the key value
255         SecretKeySpec cipherKey = new SecretKeySpec(derivedKey, 0,
256                                                     derivedKey.length-8, algo);
257         // use the last 8 bytes as the IV
258         IvParameterSpec ivSpec = new IvParameterSpec(derivedKey,
259                                                      derivedKey.length-8,
260                                                      8);
261         // initialize the underlying cipher
262         cipher.init(opmode, cipherKey, ivSpec, random);
263     }
264 
deriveCipherKey(byte[] passwdBytes)265     private byte[] deriveCipherKey(byte[] passwdBytes) {
266 
267         byte[] result = null;
268 
269         if (algo.equals("DES")) {
270             // P || S (password concatenated with salt)
271             byte[] concat = new byte[Math.addExact(passwdBytes.length, salt.length)];
272             System.arraycopy(passwdBytes, 0, concat, 0, passwdBytes.length);
273             System.arraycopy(salt, 0, concat, passwdBytes.length, salt.length);
274 
275             // digest P || S with c iterations
276             byte[] toBeHashed = concat;
277             for (int i = 0; i < iCount; i++) {
278                 md.update(toBeHashed);
279                 toBeHashed = md.digest(); // this resets the digest
280             }
281             Arrays.fill(concat, (byte)0x00);
282             result = toBeHashed;
283         } else if (algo.equals("DESede")) {
284             // if the 2 salt halves are the same, invert one of them
285             int i;
286             for (i=0; i<4; i++) {
287                 if (salt[i] != salt[i+4])
288                     break;
289             }
290             if (i==4) { // same, invert 1st half
291                 for (i=0; i<2; i++) {
292                     byte tmp = salt[i];
293                     salt[i] = salt[3-i];
294                     salt[3-i] = tmp;
295                 }
296             }
297 
298             // Now digest each half (concatenated with password). For each
299             // half, go through the loop as many times as specified by the
300             // iteration count parameter (inner for loop).
301             // Concatenate the output from each digest round with the
302             // password, and use the result as the input to the next digest
303             // operation.
304             byte[] toBeHashed = null;
305             result = new byte[DESedeKeySpec.DES_EDE_KEY_LEN +
306                               DESConstants.DES_BLOCK_SIZE];
307             for (i = 0; i < 2; i++) {
308                 toBeHashed = new byte[salt.length/2];
309                 System.arraycopy(salt, i*(salt.length/2), toBeHashed, 0,
310                                  toBeHashed.length);
311                 for (int j=0; j < iCount; j++) {
312                     md.update(toBeHashed);
313                     md.update(passwdBytes);
314                     toBeHashed = md.digest();
315                 }
316                 System.arraycopy(toBeHashed, 0, result, i*16,
317                                  toBeHashed.length);
318             }
319         }
320         // clear data used in message
321         md.reset();
322         return result;
323     }
324 
init(int opmode, Key key, AlgorithmParameters params, SecureRandom random)325     void init(int opmode, Key key, AlgorithmParameters params,
326               SecureRandom random)
327         throws InvalidKeyException, InvalidAlgorithmParameterException {
328         PBEParameterSpec pbeSpec = null;
329         if (params != null) {
330             try {
331                 pbeSpec = params.getParameterSpec(PBEParameterSpec.class);
332             } catch (InvalidParameterSpecException ipse) {
333                 throw new InvalidAlgorithmParameterException("Wrong parameter "
334                                                              + "type: PBE "
335                                                              + "expected");
336             }
337         }
338         init(opmode, key, pbeSpec, random);
339     }
340 
341     /**
342      * Continues a multiple-part encryption or decryption operation
343      * (depending on how this cipher was initialized), processing another data
344      * part.
345      *
346      * <p>The first <code>inputLen</code> bytes in the <code>input</code>
347      * buffer, starting at <code>inputOffset</code>, are processed, and the
348      * result is stored in a new buffer.
349      *
350      * @param input the input buffer
351      * @param inputOffset the offset in <code>input</code> where the input
352      * starts
353      * @param inputLen the input length
354      *
355      * @return the new buffer with the result
356      *
357      */
update(byte[] input, int inputOffset, int inputLen)358     byte[] update(byte[] input, int inputOffset, int inputLen) {
359         return cipher.update(input, inputOffset, inputLen);
360     }
361 
362     /**
363      * Continues a multiple-part encryption or decryption operation
364      * (depending on how this cipher was initialized), processing another data
365      * part.
366      *
367      * <p>The first <code>inputLen</code> bytes in the <code>input</code>
368      * buffer, starting at <code>inputOffset</code>, are processed, and the
369      * result is stored in the <code>output</code> buffer, starting at
370      * <code>outputOffset</code>.
371      *
372      * @param input the input buffer
373      * @param inputOffset the offset in <code>input</code> where the input
374      * starts
375      * @param inputLen the input length
376      * @param output the buffer for the result
377      * @param outputOffset the offset in <code>output</code> where the result
378      * is stored
379      *
380      * @return the number of bytes stored in <code>output</code>
381      *
382      * @exception ShortBufferException if the given output buffer is too small
383      * to hold the result
384      */
update(byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset)385     int update(byte[] input, int inputOffset, int inputLen,
386                byte[] output, int outputOffset)
387         throws ShortBufferException {
388         return cipher.update(input, inputOffset, inputLen,
389                              output, outputOffset);
390     }
391 
392     /**
393      * Encrypts or decrypts data in a single-part operation,
394      * or finishes a multiple-part operation.
395      * The data is encrypted or decrypted, depending on how this cipher was
396      * initialized.
397      *
398      * <p>The first <code>inputLen</code> bytes in the <code>input</code>
399      * buffer, starting at <code>inputOffset</code>, and any input bytes that
400      * may have been buffered during a previous <code>update</code> operation,
401      * are processed, with padding (if requested) being applied.
402      * The result is stored in a new buffer.
403      *
404      * <p>The cipher is reset to its initial state (uninitialized) after this
405      * call.
406      *
407      * @param input the input buffer
408      * @param inputOffset the offset in <code>input</code> where the input
409      * starts
410      * @param inputLen the input length
411      *
412      * @return the new buffer with the result
413      *
414      * @exception IllegalBlockSizeException if this cipher is a block cipher,
415      * no padding has been requested (only in encryption mode), and the total
416      * input length of the data processed by this cipher is not a multiple of
417      * block size
418      * @exception BadPaddingException if decrypting and padding is chosen,
419      * but the last input data does not have proper padding bytes.
420      */
doFinal(byte[] input, int inputOffset, int inputLen)421     byte[] doFinal(byte[] input, int inputOffset, int inputLen)
422         throws IllegalBlockSizeException, BadPaddingException {
423         return cipher.doFinal(input, inputOffset, inputLen);
424     }
425 
426     /**
427      * Encrypts or decrypts data in a single-part operation,
428      * or finishes a multiple-part operation.
429      * The data is encrypted or decrypted, depending on how this cipher was
430      * initialized.
431      *
432      * <p>The first <code>inputLen</code> bytes in the <code>input</code>
433      * buffer, starting at <code>inputOffset</code>, and any input bytes that
434      * may have been buffered during a previous <code>update</code> operation,
435      * are processed, with padding (if requested) being applied.
436      * The result is stored in the <code>output</code> buffer, starting at
437      * <code>outputOffset</code>.
438      *
439      * <p>The cipher is reset to its initial state (uninitialized) after this
440      * call.
441      *
442      * @param input the input buffer
443      * @param inputOffset the offset in <code>input</code> where the input
444      * starts
445      * @param inputLen the input length
446      * @param output the buffer for the result
447      * @param outputOffset the offset in <code>output</code> where the result
448      * is stored
449      *
450      * @return the number of bytes stored in <code>output</code>
451      *
452      * @exception IllegalBlockSizeException if this cipher is a block cipher,
453      * no padding has been requested (only in encryption mode), and the total
454      * input length of the data processed by this cipher is not a multiple of
455      * block size
456      * @exception ShortBufferException if the given output buffer is too small
457      * to hold the result
458      * @exception BadPaddingException if decrypting and padding is chosen,
459      * but the last input data does not have proper padding bytes.
460      */
doFinal(byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset)461     int doFinal(byte[] input, int inputOffset, int inputLen,
462                 byte[] output, int outputOffset)
463         throws ShortBufferException, IllegalBlockSizeException,
464                BadPaddingException {
465         return cipher.doFinal(input, inputOffset, inputLen,
466                                     output, outputOffset);
467     }
468 
469     /**
470      * Wrap a key.
471      *
472      * @param key the key to be wrapped.
473      *
474      * @return the wrapped key.
475      *
476      * @exception IllegalBlockSizeException if this cipher is a block
477      * cipher, no padding has been requested, and the length of the
478      * encoding of the key to be wrapped is not a
479      * multiple of the block size.
480      *
481      * @exception InvalidKeyException if it is impossible or unsafe to
482      * wrap the key with this cipher (e.g., a hardware protected key is
483      * being passed to a software only cipher).
484      */
wrap(Key key)485     byte[] wrap(Key key)
486         throws IllegalBlockSizeException, InvalidKeyException {
487         byte[] result = null;
488         byte[] encodedKey = null;
489         try {
490             encodedKey = key.getEncoded();
491             if ((encodedKey == null) || (encodedKey.length == 0)) {
492                 throw new InvalidKeyException("Cannot get an encoding of " +
493                                               "the key to be wrapped");
494             }
495 
496             result = doFinal(encodedKey, 0, encodedKey.length);
497         } catch (BadPaddingException e) {
498             // Should never happen
499         } finally {
500             if (encodedKey != null) Arrays.fill(encodedKey, (byte)0x00);
501         }
502 
503         return result;
504     }
505 
506     /**
507      * Unwrap a previously wrapped key.
508      *
509      * @param wrappedKey the key to be unwrapped.
510      *
511      * @param wrappedKeyAlgorithm the algorithm the wrapped key is for.
512      *
513      * @param wrappedKeyType the type of the wrapped key.
514      * This is one of <code>Cipher.SECRET_KEY</code>,
515      * <code>Cipher.PRIVATE_KEY</code>, or <code>Cipher.PUBLIC_KEY</code>.
516      *
517      * @return the unwrapped key.
518      *
519      * @exception NoSuchAlgorithmException if no installed providers
520      * can create keys of type <code>wrappedKeyType</code> for the
521      * <code>wrappedKeyAlgorithm</code>.
522      *
523      * @exception InvalidKeyException if <code>wrappedKey</code> does not
524      * represent a wrapped key of type <code>wrappedKeyType</code> for
525      * the <code>wrappedKeyAlgorithm</code>.
526      */
unwrap(byte[] wrappedKey, String wrappedKeyAlgorithm, int wrappedKeyType)527     Key unwrap(byte[] wrappedKey,
528                String wrappedKeyAlgorithm,
529                int wrappedKeyType)
530         throws InvalidKeyException, NoSuchAlgorithmException {
531         byte[] encodedKey;
532         try {
533             encodedKey = doFinal(wrappedKey, 0, wrappedKey.length);
534         } catch (BadPaddingException ePadding) {
535             throw new InvalidKeyException("The wrapped key is not padded " +
536                                           "correctly");
537         } catch (IllegalBlockSizeException eBlockSize) {
538             throw new InvalidKeyException("The wrapped key does not have " +
539                                           "the correct length");
540         }
541         return ConstructKeys.constructKey(encodedKey, wrappedKeyAlgorithm,
542                                           wrappedKeyType);
543     }
544 }
545