1 /*
2  * Copyright (c) 2012, 2015, 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  * These algorithms implement PBE with HmacSHA1/HmacSHA2-family and AES-CBC.
38  * Padding is done as described in PKCS #5.
39  *
40  * @author Jan Luehe
41  *
42  *
43  * @see javax.crypto.Cipher
44  */
45 abstract class PBES2Core extends CipherSpi {
46 
47     private static final int DEFAULT_SALT_LENGTH = 20;
48     private static final int DEFAULT_COUNT = 4096;
49 
50     // the encapsulated cipher
51     private final CipherCore cipher;
52     private final int keyLength; // in bits
53     private final int blkSize; // in bits
54     private final PBKDF2Core kdf;
55     private final String pbeAlgo;
56     private final String cipherAlgo;
57     private int iCount = DEFAULT_COUNT;
58     private byte[] salt = null;
59     private IvParameterSpec ivSpec = null;
60 
61     /**
62      * Creates an instance of PBE Scheme 2 according to the selected
63      * password-based key derivation function and encryption scheme.
64      */
PBES2Core(String kdfAlgo, String cipherAlgo, int keySize)65     PBES2Core(String kdfAlgo, String cipherAlgo, int keySize)
66         throws NoSuchAlgorithmException, NoSuchPaddingException {
67 
68         this.cipherAlgo = cipherAlgo;
69         keyLength = keySize * 8;
70         pbeAlgo = "PBEWith" + kdfAlgo + "And" + cipherAlgo + "_" + keyLength;
71 
72         if (cipherAlgo.equals("AES")) {
73             blkSize = AESConstants.AES_BLOCK_SIZE;
74             cipher = new CipherCore(new AESCrypt(), blkSize);
75 
76             switch(kdfAlgo) {
77             case "HmacSHA1":
78                 kdf = new PBKDF2Core.HmacSHA1();
79                 break;
80             case "HmacSHA224":
81                 kdf = new PBKDF2Core.HmacSHA224();
82                 break;
83             case "HmacSHA256":
84                 kdf = new PBKDF2Core.HmacSHA256();
85                 break;
86             case "HmacSHA384":
87                 kdf = new PBKDF2Core.HmacSHA384();
88                 break;
89             case "HmacSHA512":
90                 kdf = new PBKDF2Core.HmacSHA512();
91                 break;
92             default:
93                 throw new NoSuchAlgorithmException(
94                     "No Cipher implementation for " + kdfAlgo);
95             }
96         } else {
97             throw new NoSuchAlgorithmException("No Cipher implementation for " +
98                                                pbeAlgo);
99         }
100         cipher.setMode("CBC");
101         cipher.setPadding("PKCS5Padding");
102     }
103 
engineSetMode(String mode)104     protected void engineSetMode(String mode) throws NoSuchAlgorithmException {
105         if ((mode != null) && (!mode.equalsIgnoreCase("CBC"))) {
106             throw new NoSuchAlgorithmException("Invalid cipher mode: " + mode);
107         }
108     }
109 
engineSetPadding(String paddingScheme)110     protected void engineSetPadding(String paddingScheme)
111         throws NoSuchPaddingException {
112         if ((paddingScheme != null) &&
113             (!paddingScheme.equalsIgnoreCase("PKCS5Padding"))) {
114             throw new NoSuchPaddingException("Invalid padding scheme: " +
115                                              paddingScheme);
116         }
117     }
118 
engineGetBlockSize()119     protected int engineGetBlockSize() {
120         return blkSize;
121     }
122 
engineGetOutputSize(int inputLen)123     protected int engineGetOutputSize(int inputLen) {
124         return cipher.getOutputSize(inputLen);
125     }
126 
engineGetIV()127     protected byte[] engineGetIV() {
128         return cipher.getIV();
129     }
130 
engineGetParameters()131     protected AlgorithmParameters engineGetParameters() {
132         AlgorithmParameters params = null;
133         if (salt == null) {
134             // generate random salt and use default iteration count
135             salt = new byte[DEFAULT_SALT_LENGTH];
136             SunJCE.getRandom().nextBytes(salt);
137             iCount = DEFAULT_COUNT;
138         }
139         if (ivSpec == null) {
140             // generate random IV
141             byte[] ivBytes = new byte[blkSize];
142             SunJCE.getRandom().nextBytes(ivBytes);
143             ivSpec = new IvParameterSpec(ivBytes);
144         }
145         PBEParameterSpec pbeSpec = new PBEParameterSpec(salt, iCount, ivSpec);
146         try {
147             params = AlgorithmParameters.getInstance(pbeAlgo,
148                 SunJCE.getInstance());
149             params.init(pbeSpec);
150         } catch (NoSuchAlgorithmException nsae) {
151             // should never happen
152             throw new RuntimeException("SunJCE called, but not configured");
153         } catch (InvalidParameterSpecException ipse) {
154             // should never happen
155             throw new RuntimeException("PBEParameterSpec not supported");
156         }
157         return params;
158     }
159 
engineInit(int opmode, Key key, SecureRandom random)160     protected void engineInit(int opmode, Key key, SecureRandom random)
161         throws InvalidKeyException {
162         try {
163             engineInit(opmode, key, (AlgorithmParameterSpec) null, random);
164         } catch (InvalidAlgorithmParameterException ie) {
165             InvalidKeyException ike =
166                 new InvalidKeyException("requires PBE parameters");
167             ike.initCause(ie);
168             throw ike;
169         }
170     }
171 
engineInit(int opmode, Key key, AlgorithmParameterSpec params, SecureRandom random)172     protected void engineInit(int opmode, Key key,
173                               AlgorithmParameterSpec params,
174                               SecureRandom random)
175         throws InvalidKeyException, InvalidAlgorithmParameterException {
176 
177         if (key == null) {
178             throw new InvalidKeyException("Null key");
179         }
180 
181         byte[] passwdBytes = key.getEncoded();
182         char[] passwdChars = null;
183         PBEKeySpec pbeSpec;
184         try {
185             if ((passwdBytes == null) ||
186                     !(key.getAlgorithm().regionMatches(true, 0, "PBE", 0, 3))) {
187                 throw new InvalidKeyException("Missing password");
188             }
189 
190             // TBD: consolidate the salt, ic and IV parameter checks below
191 
192             // Extract salt and iteration count from the key, if present
193             if (key instanceof javax.crypto.interfaces.PBEKey) {
194                 salt = ((javax.crypto.interfaces.PBEKey)key).getSalt();
195                 if (salt != null && salt.length < 8) {
196                     throw new InvalidAlgorithmParameterException(
197                             "Salt must be at least 8 bytes long");
198                 }
199                 iCount = ((javax.crypto.interfaces.PBEKey)key).getIterationCount();
200                 if (iCount == 0) {
201                     iCount = DEFAULT_COUNT;
202                 } else if (iCount < 0) {
203                     throw new InvalidAlgorithmParameterException(
204                             "Iteration count must be a positive number");
205                 }
206             }
207 
208             // Extract salt, iteration count and IV from the params, if present
209             if (params == null) {
210                 if (salt == null) {
211                     // generate random salt and use default iteration count
212                     salt = new byte[DEFAULT_SALT_LENGTH];
213                     random.nextBytes(salt);
214                     iCount = DEFAULT_COUNT;
215                 }
216                 if ((opmode == Cipher.ENCRYPT_MODE) ||
217                         (opmode == Cipher.WRAP_MODE)) {
218                     // generate random IV
219                     byte[] ivBytes = new byte[blkSize];
220                     random.nextBytes(ivBytes);
221                     ivSpec = new IvParameterSpec(ivBytes);
222                 }
223             } else {
224                 if (!(params instanceof PBEParameterSpec)) {
225                     throw new InvalidAlgorithmParameterException
226                             ("Wrong parameter type: PBE expected");
227                 }
228                 // salt and iteration count from the params take precedence
229                 byte[] specSalt = ((PBEParameterSpec) params).getSalt();
230                 if (specSalt != null && specSalt.length < 8) {
231                     throw new InvalidAlgorithmParameterException(
232                             "Salt must be at least 8 bytes long");
233                 }
234                 salt = specSalt;
235                 int specICount = ((PBEParameterSpec) params).getIterationCount();
236                 if (specICount == 0) {
237                     specICount = DEFAULT_COUNT;
238                 } else if (specICount < 0) {
239                     throw new InvalidAlgorithmParameterException(
240                             "Iteration count must be a positive number");
241                 }
242                 iCount = specICount;
243 
244                 AlgorithmParameterSpec specParams =
245                         ((PBEParameterSpec) params).getParameterSpec();
246                 if (specParams != null) {
247                     if (specParams instanceof IvParameterSpec) {
248                         ivSpec = (IvParameterSpec)specParams;
249                     } else {
250                         throw new InvalidAlgorithmParameterException(
251                                 "Wrong parameter type: IV expected");
252                     }
253                 } else if ((opmode == Cipher.ENCRYPT_MODE) ||
254                         (opmode == Cipher.WRAP_MODE)) {
255                     // generate random IV
256                     byte[] ivBytes = new byte[blkSize];
257                     random.nextBytes(ivBytes);
258                     ivSpec = new IvParameterSpec(ivBytes);
259                 } else {
260                     throw new InvalidAlgorithmParameterException(
261                             "Missing parameter type: IV expected");
262                 }
263             }
264 
265             passwdChars = new char[passwdBytes.length];
266             for (int i = 0; i < passwdChars.length; i++)
267                 passwdChars[i] = (char) (passwdBytes[i] & 0x7f);
268 
269             pbeSpec = new PBEKeySpec(passwdChars, salt, iCount, keyLength);
270             // password char[] was cloned in PBEKeySpec constructor,
271             // so we can zero it out here
272         } finally {
273             if (passwdChars != null) Arrays.fill(passwdChars, '\0');
274             if (passwdBytes != null) Arrays.fill(passwdBytes, (byte)0x00);
275         }
276 
277         SecretKey s = null;
278 
279         try {
280             s = kdf.engineGenerateSecret(pbeSpec);
281 
282         } catch (InvalidKeySpecException ikse) {
283             InvalidKeyException ike =
284                 new InvalidKeyException("Cannot construct PBE key");
285             ike.initCause(ikse);
286             throw ike;
287         }
288         byte[] derivedKey = s.getEncoded();
289         SecretKeySpec cipherKey = new SecretKeySpec(derivedKey, cipherAlgo);
290 
291         // initialize the underlying cipher
292         cipher.init(opmode, cipherKey, ivSpec, random);
293     }
294 
engineInit(int opmode, Key key, AlgorithmParameters params, SecureRandom random)295     protected void engineInit(int opmode, Key key, AlgorithmParameters params,
296                               SecureRandom random)
297         throws InvalidKeyException, InvalidAlgorithmParameterException {
298         AlgorithmParameterSpec pbeSpec = null;
299         if (params != null) {
300             try {
301                 pbeSpec = params.getParameterSpec(PBEParameterSpec.class);
302             } catch (InvalidParameterSpecException ipse) {
303                 throw new InvalidAlgorithmParameterException(
304                     "Wrong parameter type: PBE expected");
305             }
306         }
307         engineInit(opmode, key, pbeSpec, random);
308     }
309 
engineUpdate(byte[] input, int inputOffset, int inputLen)310     protected byte[] engineUpdate(byte[] input, int inputOffset, int inputLen) {
311         return cipher.update(input, inputOffset, inputLen);
312     }
313 
engineUpdate(byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset)314     protected int engineUpdate(byte[] input, int inputOffset, int inputLen,
315                                byte[] output, int outputOffset)
316         throws ShortBufferException {
317         return cipher.update(input, inputOffset, inputLen,
318                              output, outputOffset);
319     }
320 
engineDoFinal(byte[] input, int inputOffset, int inputLen)321     protected byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen)
322         throws IllegalBlockSizeException, BadPaddingException {
323         return cipher.doFinal(input, inputOffset, inputLen);
324     }
325 
engineDoFinal(byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset)326     protected int engineDoFinal(byte[] input, int inputOffset, int inputLen,
327                                 byte[] output, int outputOffset)
328         throws ShortBufferException, IllegalBlockSizeException,
329                BadPaddingException {
330         return cipher.doFinal(input, inputOffset, inputLen,
331                               output, outputOffset);
332     }
333 
engineGetKeySize(Key key)334     protected int engineGetKeySize(Key key) throws InvalidKeyException {
335         return keyLength;
336     }
337 
engineWrap(Key key)338     protected byte[] engineWrap(Key key)
339         throws IllegalBlockSizeException, InvalidKeyException {
340         return cipher.wrap(key);
341     }
342 
engineUnwrap(byte[] wrappedKey, String wrappedKeyAlgorithm, int wrappedKeyType)343     protected Key engineUnwrap(byte[] wrappedKey, String wrappedKeyAlgorithm,
344                                int wrappedKeyType)
345         throws InvalidKeyException, NoSuchAlgorithmException {
346         byte[] encodedKey;
347         return cipher.unwrap(wrappedKey, wrappedKeyAlgorithm,
348                              wrappedKeyType);
349     }
350 
351     public static final class HmacSHA1AndAES_128 extends PBES2Core {
HmacSHA1AndAES_128()352         public HmacSHA1AndAES_128()
353             throws NoSuchAlgorithmException, NoSuchPaddingException {
354             super("HmacSHA1", "AES", 16);
355         }
356     }
357 
358     public static final class HmacSHA224AndAES_128 extends PBES2Core {
HmacSHA224AndAES_128()359         public HmacSHA224AndAES_128()
360             throws NoSuchAlgorithmException, NoSuchPaddingException {
361             super("HmacSHA224", "AES", 16);
362         }
363     }
364 
365     public static final class HmacSHA256AndAES_128 extends PBES2Core {
HmacSHA256AndAES_128()366         public HmacSHA256AndAES_128()
367             throws NoSuchAlgorithmException, NoSuchPaddingException {
368             super("HmacSHA256", "AES", 16);
369         }
370     }
371 
372     public static final class HmacSHA384AndAES_128 extends PBES2Core {
HmacSHA384AndAES_128()373         public HmacSHA384AndAES_128()
374             throws NoSuchAlgorithmException, NoSuchPaddingException {
375             super("HmacSHA384", "AES", 16);
376         }
377     }
378 
379     public static final class HmacSHA512AndAES_128 extends PBES2Core {
HmacSHA512AndAES_128()380         public HmacSHA512AndAES_128()
381             throws NoSuchAlgorithmException, NoSuchPaddingException {
382             super("HmacSHA512", "AES", 16);
383         }
384     }
385 
386     public static final class HmacSHA1AndAES_256 extends PBES2Core {
HmacSHA1AndAES_256()387         public HmacSHA1AndAES_256()
388             throws NoSuchAlgorithmException, NoSuchPaddingException {
389             super("HmacSHA1", "AES", 32);
390         }
391     }
392 
393     public static final class HmacSHA224AndAES_256 extends PBES2Core {
HmacSHA224AndAES_256()394         public HmacSHA224AndAES_256()
395             throws NoSuchAlgorithmException, NoSuchPaddingException {
396             super("HmacSHA224", "AES", 32);
397         }
398     }
399 
400     public static final class HmacSHA256AndAES_256 extends PBES2Core {
HmacSHA256AndAES_256()401         public HmacSHA256AndAES_256()
402             throws NoSuchAlgorithmException, NoSuchPaddingException {
403             super("HmacSHA256", "AES", 32);
404         }
405     }
406 
407     public static final class HmacSHA384AndAES_256 extends PBES2Core {
HmacSHA384AndAES_256()408         public HmacSHA384AndAES_256()
409             throws NoSuchAlgorithmException, NoSuchPaddingException {
410             super("HmacSHA384", "AES", 32);
411         }
412     }
413 
414     public static final class HmacSHA512AndAES_256 extends PBES2Core {
HmacSHA512AndAES_256()415         public HmacSHA512AndAES_256()
416             throws NoSuchAlgorithmException, NoSuchPaddingException {
417             super("HmacSHA512", "AES", 32);
418         }
419     }
420 }
421