blob: 1c27e2ec1a4a74ed1bbd056f0121242b1b58b1d4 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2002-2007 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package com.sun.crypto.provider;
27
28import java.io.UnsupportedEncodingException;
29import java.security.*;
30import java.security.spec.*;
31import javax.crypto.*;
32import 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 */
43final class PBECipherCore {
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 */
58 PBECipherCore(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 */
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 */
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 */
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 */
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 */
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 */
164 AlgorithmParameters getParameters() {
165 AlgorithmParameters params = null;
166 if (salt == null) {
167 salt = new byte[8];
168 SunJCE.RANDOM.nextBytes(salt);
169 }
170 PBEParameterSpec pbeSpec = new PBEParameterSpec(salt, iCount);
171 try {
172 params = AlgorithmParameters.getInstance("PBEWithMD5And" +
173 (algo.equalsIgnoreCase("DES")? "DES":"TripleDES"), "SunJCE");
174 } catch (NoSuchAlgorithmException nsae) {
175 // should never happen
176 throw new RuntimeException("SunJCE called, but not configured");
177 } catch (NoSuchProviderException nspe) {
178 // should never happen
179 throw new RuntimeException("SunJCE called, but not configured");
180 }
181 try {
182 params.init(pbeSpec);
183 } catch (InvalidParameterSpecException ipse) {
184 // should never happen
185 throw new RuntimeException("PBEParameterSpec not supported");
186 }
187 return params;
188 }
189
190 /**
191 * Initializes this cipher with a key, a set of
192 * algorithm parameters, and a source of randomness.
193 * The cipher is initialized for one of the following four operations:
194 * encryption, decryption, key wrapping or key unwrapping, depending on
195 * the value of <code>opmode</code>.
196 *
197 * <p>If this cipher (including its underlying feedback or padding scheme)
198 * requires any random bytes, it will get them from <code>random</code>.
199 *
200 * @param opmode the operation mode of this cipher (this is one of
201 * the following:
202 * <code>ENCRYPT_MODE</code>, <code>DECRYPT_MODE</code>),
203 * <code>WRAP_MODE</code> or <code>UNWRAP_MODE</code>)
204 * @param key the encryption key
205 * @param params the algorithm parameters
206 * @param random the source of randomness
207 *
208 * @exception InvalidKeyException if the given key is inappropriate for
209 * initializing this cipher
210 * @exception InvalidAlgorithmParameterException if the given algorithm
211 * parameters are inappropriate for this cipher
212 */
213 void init(int opmode, Key key, AlgorithmParameterSpec params,
214 SecureRandom random)
215 throws InvalidKeyException, InvalidAlgorithmParameterException {
216 if (((opmode == Cipher.DECRYPT_MODE) ||
217 (opmode == Cipher.UNWRAP_MODE)) && (params == null)) {
218 throw new InvalidAlgorithmParameterException("Parameters "
219 + "missing");
220 }
221 if ((key == null) ||
222 (key.getEncoded() == null) ||
223 !(key.getAlgorithm().regionMatches(true, 0, "PBE", 0, 3))) {
224 throw new InvalidKeyException("Missing password");
225 }
226
227 if (params == null) {
228 // create random salt and use default iteration count
229 salt = new byte[8];
230 random.nextBytes(salt);
231 } else {
232 if (!(params instanceof PBEParameterSpec)) {
233 throw new InvalidAlgorithmParameterException
234 ("Wrong parameter type: PBE expected");
235 }
236 salt = ((PBEParameterSpec) params).getSalt();
237 // salt must be 8 bytes long (by definition)
238 if (salt.length != 8) {
239 throw new InvalidAlgorithmParameterException
240 ("Salt must be 8 bytes long");
241 }
242 iCount = ((PBEParameterSpec) params).getIterationCount();
243 if (iCount <= 0) {
244 throw new InvalidAlgorithmParameterException
245 ("IterationCount must be a positive number");
246 }
247 }
248
249 byte[] derivedKey = deriveCipherKey(key);
250 // use all but the last 8 bytes as the key value
251 SecretKeySpec cipherKey = new SecretKeySpec(derivedKey, 0,
252 derivedKey.length-8, algo);
253 // use the last 8 bytes as the IV
254 IvParameterSpec ivSpec = new IvParameterSpec(derivedKey,
255 derivedKey.length-8,
256 8);
257 // initialize the underlying cipher
258 cipher.init(opmode, cipherKey, ivSpec, random);
259 }
260
261 private byte[] deriveCipherKey(Key key) {
262
263 byte[] result = null;
264 byte[] passwdBytes = key.getEncoded();
265
266 if (algo.equals("DES")) {
267 // P || S (password concatenated with salt)
268 byte[] concat = new byte[passwdBytes.length + salt.length];
269 System.arraycopy(passwdBytes, 0, concat, 0, passwdBytes.length);
270 java.util.Arrays.fill(passwdBytes, (byte)0x00);
271 System.arraycopy(salt, 0, concat, passwdBytes.length, salt.length);
272
273 // digest P || S with c iterations
274 byte[] toBeHashed = concat;
275 for (int i = 0; i < iCount; i++) {
276 md.update(toBeHashed);
277 toBeHashed = md.digest(); // this resets the digest
278 }
279 java.util.Arrays.fill(concat, (byte)0x00);
280 result = toBeHashed;
281 } else if (algo.equals("DESede")) {
282 // if the 2 salt halves are the same, invert one of them
283 int i;
284 for (i=0; i<4; i++) {
285 if (salt[i] != salt[i+4])
286 break;
287 }
288 if (i==4) { // same, invert 1st half
289 for (i=0; i<2; i++) {
290 byte tmp = salt[i];
291 salt[i] = salt[3-i];
292 salt[3-1] = tmp;
293 }
294 }
295
296 // Now digest each half (concatenated with password). For each
297 // half, go through the loop as many times as specified by the
298 // iteration count parameter (inner for loop).
299 // Concatenate the output from each digest round with the
300 // password, and use the result as the input to the next digest
301 // operation.
302 byte[] kBytes = null;
303 IvParameterSpec iv = null;
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(); // this resets the digest
315 }
316 System.arraycopy(toBeHashed, 0, result, i*16,
317 toBeHashed.length);
318 }
319 }
320 return result;
321 }
322
323 void init(int opmode, Key key, AlgorithmParameters params,
324 SecureRandom random)
325 throws InvalidKeyException, InvalidAlgorithmParameterException {
326 PBEParameterSpec pbeSpec = null;
327 if (params != null) {
328 try {
329 pbeSpec = (PBEParameterSpec) params.getParameterSpec
330 (PBEParameterSpec.class);
331 } catch (InvalidParameterSpecException ipse) {
332 throw new InvalidAlgorithmParameterException("Wrong parameter "
333 + "type: PBE "
334 + "expected");
335 }
336 }
337 init(opmode, key, pbeSpec, random);
338 }
339
340 /**
341 * Continues a multiple-part encryption or decryption operation
342 * (depending on how this cipher was initialized), processing another data
343 * part.
344 *
345 * <p>The first <code>inputLen</code> bytes in the <code>input</code>
346 * buffer, starting at <code>inputOffset</code>, are processed, and the
347 * result is stored in a new buffer.
348 *
349 * @param input the input buffer
350 * @param inputOffset the offset in <code>input</code> where the input
351 * starts
352 * @param inputLen the input length
353 *
354 * @return the new buffer with the result
355 *
356 */
357 byte[] update(byte[] input, int inputOffset, int inputLen) {
358 return cipher.update(input, inputOffset, inputLen);
359 }
360
361 /**
362 * Continues a multiple-part encryption or decryption operation
363 * (depending on how this cipher was initialized), processing another data
364 * part.
365 *
366 * <p>The first <code>inputLen</code> bytes in the <code>input</code>
367 * buffer, starting at <code>inputOffset</code>, are processed, and the
368 * result is stored in the <code>output</code> buffer, starting at
369 * <code>outputOffset</code>.
370 *
371 * @param input the input buffer
372 * @param inputOffset the offset in <code>input</code> where the input
373 * starts
374 * @param inputLen the input length
375 * @param output the buffer for the result
376 * @param outputOffset the offset in <code>output</code> where the result
377 * is stored
378 *
379 * @return the number of bytes stored in <code>output</code>
380 *
381 * @exception ShortBufferException if the given output buffer is too small
382 * to hold the result
383 */
384 int update(byte[] input, int inputOffset, int inputLen,
385 byte[] output, int outputOffset)
386 throws ShortBufferException {
387 return cipher.update(input, inputOffset, inputLen,
388 output, outputOffset);
389 }
390
391 /**
392 * Encrypts or decrypts data in a single-part operation,
393 * or finishes a multiple-part operation.
394 * The data is encrypted or decrypted, depending on how this cipher was
395 * initialized.
396 *
397 * <p>The first <code>inputLen</code> bytes in the <code>input</code>
398 * buffer, starting at <code>inputOffset</code>, and any input bytes that
399 * may have been buffered during a previous <code>update</code> operation,
400 * are processed, with padding (if requested) being applied.
401 * The result is stored in a new buffer.
402 *
403 * <p>The cipher is reset to its initial state (uninitialized) after this
404 * call.
405 *
406 * @param input the input buffer
407 * @param inputOffset the offset in <code>input</code> where the input
408 * starts
409 * @param inputLen the input length
410 *
411 * @return the new buffer with the result
412 *
413 * @exception IllegalBlockSizeException if this cipher is a block cipher,
414 * no padding has been requested (only in encryption mode), and the total
415 * input length of the data processed by this cipher is not a multiple of
416 * block size
417 * @exception BadPaddingException if decrypting and padding is choosen,
418 * but the last input data does not have proper padding bytes.
419 */
420 byte[] doFinal(byte[] input, int inputOffset, int inputLen)
421 throws IllegalBlockSizeException, BadPaddingException {
422 return cipher.doFinal(input, inputOffset, inputLen);
423 }
424
425 /**
426 * Encrypts or decrypts data in a single-part operation,
427 * or finishes a multiple-part operation.
428 * The data is encrypted or decrypted, depending on how this cipher was
429 * initialized.
430 *
431 * <p>The first <code>inputLen</code> bytes in the <code>input</code>
432 * buffer, starting at <code>inputOffset</code>, and any input bytes that
433 * may have been buffered during a previous <code>update</code> operation,
434 * are processed, with padding (if requested) being applied.
435 * The result is stored in the <code>output</code> buffer, starting at
436 * <code>outputOffset</code>.
437 *
438 * <p>The cipher is reset to its initial state (uninitialized) after this
439 * call.
440 *
441 * @param input the input buffer
442 * @param inputOffset the offset in <code>input</code> where the input
443 * starts
444 * @param inputLen the input length
445 * @param output the buffer for the result
446 * @param outputOffset the offset in <code>output</code> where the result
447 * is stored
448 *
449 * @return the number of bytes stored in <code>output</code>
450 *
451 * @exception IllegalBlockSizeException if this cipher is a block cipher,
452 * no padding has been requested (only in encryption mode), and the total
453 * input length of the data processed by this cipher is not a multiple of
454 * block size
455 * @exception ShortBufferException if the given output buffer is too small
456 * to hold the result
457 * @exception BadPaddingException if decrypting and padding is choosen,
458 * but the last input data does not have proper padding bytes.
459 */
460 int doFinal(byte[] input, int inputOffset, int inputLen,
461 byte[] output, int outputOffset)
462 throws ShortBufferException, IllegalBlockSizeException,
463 BadPaddingException {
464 return cipher.doFinal(input, inputOffset, inputLen,
465 output, outputOffset);
466 }
467
468 /**
469 * Wrap a key.
470 *
471 * @param key the key to be wrapped.
472 *
473 * @return the wrapped key.
474 *
475 * @exception IllegalBlockSizeException if this cipher is a block
476 * cipher, no padding has been requested, and the length of the
477 * encoding of the key to be wrapped is not a
478 * multiple of the block size.
479 *
480 * @exception InvalidKeyException if it is impossible or unsafe to
481 * wrap the key with this cipher (e.g., a hardware protected key is
482 * being passed to a software only cipher).
483 */
484 byte[] wrap(Key key)
485 throws IllegalBlockSizeException, InvalidKeyException {
486 byte[] result = null;
487
488 try {
489 byte[] encodedKey = key.getEncoded();
490 if ((encodedKey == null) || (encodedKey.length == 0)) {
491 throw new InvalidKeyException("Cannot get an encoding of " +
492 "the key to be wrapped");
493 }
494
495 result = doFinal(encodedKey, 0, encodedKey.length);
496 } catch (BadPaddingException e) {
497 // Should never happen
498 }
499
500 return result;
501 }
502
503 /**
504 * Unwrap a previously wrapped key.
505 *
506 * @param wrappedKey the key to be unwrapped.
507 *
508 * @param wrappedKeyAlgorithm the algorithm the wrapped key is for.
509 *
510 * @param wrappedKeyType the type of the wrapped key.
511 * This is one of <code>Cipher.SECRET_KEY</code>,
512 * <code>Cipher.PRIVATE_KEY</code>, or <code>Cipher.PUBLIC_KEY</code>.
513 *
514 * @return the unwrapped key.
515 *
516 * @exception NoSuchAlgorithmException if no installed providers
517 * can create keys of type <code>wrappedKeyType</code> for the
518 * <code>wrappedKeyAlgorithm</code>.
519 *
520 * @exception InvalidKeyException if <code>wrappedKey</code> does not
521 * represent a wrapped key of type <code>wrappedKeyType</code> for
522 * the <code>wrappedKeyAlgorithm</code>.
523 */
524 Key unwrap(byte[] wrappedKey,
525 String wrappedKeyAlgorithm,
526 int wrappedKeyType)
527 throws InvalidKeyException, NoSuchAlgorithmException {
528 byte[] encodedKey;
529 try {
530 encodedKey = doFinal(wrappedKey, 0, wrappedKey.length);
531 } catch (BadPaddingException ePadding) {
532 throw new InvalidKeyException("The wrapped key is not padded " +
533 "correctly");
534 } catch (IllegalBlockSizeException eBlockSize) {
535 throw new InvalidKeyException("The wrapped key does not have " +
536 "the correct length");
537 }
538 return ConstructKeys.constructKey(encodedKey, wrappedKeyAlgorithm,
539 wrappedKeyType);
540 }
541}