blob: 48cbb1edbc3d423df127497dead459be5b21c577 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2004-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.util.Arrays;
29import java.security.*;
30import java.security.spec.*;
31import javax.crypto.*;
32import javax.crypto.spec.*;
33
34/**
35 * This class implements the CMS DESede KeyWrap algorithm as defined
36 * in <a href=http://www.w3.org/TR/xmlenc-core/#sec-Alg-SymmetricKeyWrap>
37 * "XML Encryption Syntax and Processing" section 5.6.2
38 * "CMS Triple DES Key Wrap".
39 * Note: only <code>CBC</code> mode and <code>NoPadding</code> padding
40 * scheme can be used for this algorithm.
41 *
42 * @author Valerie Peng
43 *
44 *
45 * @see DESedeCipher
46 */
47public final class DESedeWrapCipher extends CipherSpi {
48
49 private static final byte[] IV2 = {
50 (byte) 0x4a, (byte) 0xdd, (byte) 0xa2, (byte) 0x2c,
51 (byte) 0x79, (byte) 0xe8, (byte) 0x21, (byte) 0x05
52 };
53
54 /*
55 * internal cipher object which does the real work.
56 */
57 private FeedbackCipher cipher;
58
59 /*
60 * iv for (re-)initializing the internal cipher object.
61 */
62 private byte[] iv = null;
63
64 /*
65 * key for re-initializing the internal cipher object.
66 */
67 private Key cipherKey = null;
68
69 /*
70 * are we encrypting or decrypting?
71 */
72 private boolean decrypting = false;
73
74 /**
75 * Creates an instance of CMS DESede KeyWrap cipher with default
76 * mode, i.e. "CBC" and padding scheme, i.e. "NoPadding".
77 *
78 * @exception SecurityException if this constructor fails to verify
79 * its own integrity.
80 */
81 public DESedeWrapCipher() {
82 SunJCE.ensureIntegrity(getClass());
83 cipher = new CipherBlockChaining(new DESedeCrypt());
84 }
85
86 /**
87 * Sets the mode of this cipher. Only "CBC" mode is accepted for this
88 * cipher.
89 *
90 * @param mode the cipher mode.
91 *
92 * @exception NoSuchAlgorithmException if the requested cipher mode
93 * is not "CBC".
94 */
95 protected void engineSetMode(String mode)
96 throws NoSuchAlgorithmException {
97 if (!mode.equalsIgnoreCase("CBC")) {
98 throw new NoSuchAlgorithmException(mode + " cannot be used");
99 }
100 }
101
102 /**
103 * Sets the padding mechanism of this cipher. Only "NoPadding" schmem
104 * is accepted for this cipher.
105 *
106 * @param padding the padding mechanism.
107 *
108 * @exception NoSuchPaddingException if the requested padding mechanism
109 * is not "NoPadding".
110 */
111 protected void engineSetPadding(String padding)
112 throws NoSuchPaddingException {
113 if (!padding.equalsIgnoreCase("NoPadding")) {
114 throw new NoSuchPaddingException(padding + " cannot be used");
115 }
116 }
117
118 /**
119 * Returns the block size (in bytes), i.e. 8 bytes.
120 *
121 * @return the block size (in bytes), i.e. 8 bytes.
122 */
123 protected int engineGetBlockSize() {
124 return DESConstants.DES_BLOCK_SIZE;
125 }
126
127 /**
128 * Returns the length in bytes that an output buffer would need to be
129 * given the input length <code>inputLen</code> (in bytes).
130 *
131 * <p>The actual output length of the next <code>update</code> or
132 * <code>doFinal</code> call may be smaller than the length returned
133 * by this method.
134 *
135 * @param inputLen the input length (in bytes).
136 *
137 * @return the required output buffer size (in bytes).
138 */
139 protected int engineGetOutputSize(int inputLen) {
140 // can only return an upper-limit if not initialized yet.
141 int result = 0;
142 if (decrypting) {
143 result = inputLen - 16;
144 } else {
145 result = inputLen + 16;
146 }
147 return (result < 0? 0:result);
148 }
149
150 /**
151 * Returns the initialization vector (IV) in a new buffer.
152 *
153 * @return the initialization vector, or null if the underlying
154 * algorithm does not use an IV, or if the IV has not yet
155 * been set.
156 */
157 protected byte[] engineGetIV() {
158 return (iv == null? null:(byte[]) iv.clone());
159 }
160
161 /**
162 * Initializes this cipher with a key and a source of randomness.
163 *
164 * <p>The cipher only supports the following two operation modes:<b>
165 * Cipher.WRAP_MODE, and <b>
166 * Cipher.UNWRAP_MODE.
167 * <p>For modes other than the above two, UnsupportedOperationException
168 * will be thrown.
169 * <p>If this cipher requires an initialization vector (IV), it will get
170 * it from <code>random</code>.
171 *
172 * @param opmode the operation mode of this cipher. Only
173 * <code>WRAP_MODE</code> or <code>UNWRAP_MODE</code>) are accepted.
174 * @param key the secret key.
175 * @param random the source of randomness.
176 *
177 * @exception InvalidKeyException if the given key is inappropriate
178 * or if parameters are required but not supplied.
179 */
180 protected void engineInit(int opmode, Key key, SecureRandom random)
181 throws InvalidKeyException {
182 try {
183 engineInit(opmode, key, (AlgorithmParameterSpec) null, random);
184 } catch (InvalidAlgorithmParameterException iape) {
185 // should never happen
186 InvalidKeyException ike =
187 new InvalidKeyException("Parameters required");
188 ike.initCause(iape);
189 throw ike;
190 }
191 }
192
193 /**
194 * Initializes this cipher with a key, a set of algorithm parameters,
195 * and a source of randomness.
196 *
197 * <p>The cipher only supports the following two operation modes:<b>
198 * Cipher.WRAP_MODE, and <b>
199 * Cipher.UNWRAP_MODE.
200 * <p>For modes other than the above two, UnsupportedOperationException
201 * will be thrown.
202 * <p>If this cipher requires an initialization vector (IV), it will get
203 * it from <code>random</code>.
204 *
205 * @param opmode the operation mode of this cipher. Only
206 * <code>WRAP_MODE</code> or <code>UNWRAP_MODE</code>) are accepted.
207 * @param key the secret key.
208 * @param params the algorithm parameters.
209 * @param random the source of randomness.
210 *
211 * @exception InvalidKeyException if the given key is inappropriate.
212 * @exception InvalidAlgorithmParameterException if the given algorithm
213 * parameters are inappropriate for this cipher.
214 */
215 protected void engineInit(int opmode, Key key,
216 AlgorithmParameterSpec params,
217 SecureRandom random)
218 throws InvalidKeyException, InvalidAlgorithmParameterException {
219 byte[] currIv = null;
220 if (opmode == Cipher.WRAP_MODE) {
221 decrypting = false;
222 if (params == null) {
223 iv = new byte[8];
224 if (random == null) {
225 random = SunJCE.RANDOM;
226 }
227 random.nextBytes(iv);
228 }
229 else if (params instanceof IvParameterSpec) {
230 iv = ((IvParameterSpec) params).getIV();
231 } else {
232 throw new InvalidAlgorithmParameterException
233 ("Wrong parameter type: IV expected");
234 }
235 currIv = iv;
236 } else if (opmode == Cipher.UNWRAP_MODE) {
237 if (params != null) {
238 throw new InvalidAlgorithmParameterException
239 ("No parameter accepted for unwrapping keys");
240 }
241 iv = null;
242 decrypting = true;
243 currIv = IV2;
244 } else {
245 throw new UnsupportedOperationException("This cipher can " +
246 "only be used for key wrapping and unwrapping");
247 }
248 cipher.init(decrypting, key.getAlgorithm(), key.getEncoded(),
249 currIv);
250 cipherKey = key;
251 }
252
253 /**
254 * Initializes this cipher with a key, a set of algorithm parameters,
255 * and a source of randomness.
256 *
257 * <p>The cipher only supports the following two operation modes:<b>
258 * Cipher.WRAP_MODE, and <b>
259 * Cipher.UNWRAP_MODE.
260 * <p>For modes other than the above two, UnsupportedOperationException
261 * will be thrown.
262 * <p>If this cipher requires an initialization vector (IV), it will get
263 * it from <code>random</code>.
264 *
265 * @param opmode the operation mode of this cipher. Only
266 * <code>WRAP_MODE</code> or <code>UNWRAP_MODE</code>) are accepted.
267 * @param key the secret key.
268 * @param params the algorithm parameters.
269 * @param random the source of randomness.
270 *
271 * @exception InvalidKeyException if the given key is inappropriate.
272 * @exception InvalidAlgorithmParameterException if the given algorithm
273 * parameters are inappropriate for this cipher.
274 */
275 protected void engineInit(int opmode, Key key,
276 AlgorithmParameters params,
277 SecureRandom random)
278 throws InvalidKeyException, InvalidAlgorithmParameterException {
279 IvParameterSpec ivSpec = null;
280 if (params != null) {
281 try {
282 DESedeParameters paramsEng = new DESedeParameters();
283 paramsEng.engineInit(params.getEncoded());
284 ivSpec = (IvParameterSpec)
285 paramsEng.engineGetParameterSpec(IvParameterSpec.class);
286 } catch (Exception ex) {
287 InvalidAlgorithmParameterException iape =
288 new InvalidAlgorithmParameterException
289 ("Wrong parameter type: IV expected");
290 iape.initCause(ex);
291 throw iape;
292 }
293 }
294 engineInit(opmode, key, ivSpec, random);
295 }
296
297 /**
298 * This operation is not supported by this cipher.
299 * Since it's impossible to initialize this cipher given the
300 * current Cipher.engineInit(...) implementation,
301 * IllegalStateException will always be thrown upon invocation.
302 *
303 * @param in the input buffer.
304 * @param inOffset the offset in <code>in</code> where the input
305 * starts.
306 * @param inLen the input length.
307 *
308 * @return n/a.
309 *
310 * @exception IllegalStateException upon invocation of this method.
311 */
312 protected byte[] engineUpdate(byte[] in, int inOffset, int inLen) {
313 throw new IllegalStateException("Cipher has not been initialized");
314 }
315
316 /**
317 * This operation is not supported by this cipher.
318 * Since it's impossible to initialize this cipher given the
319 * current Cipher.engineInit(...) implementation,
320 * IllegalStateException will always be thrown upon invocation.
321 *
322 * @param in the input buffer.
323 * @param inOffset the offset in <code>in</code> where the input
324 * starts.
325 * @param inLen the input length.
326 * @param out the buffer for the result.
327 * @param outOffset the offset in <code>out</code> where the result
328 * is stored.
329 *
330 * @return n/a.
331 *
332 * @exception IllegalStateException upon invocation of this method.
333 */
334 protected int engineUpdate(byte[] in, int inOffset, int inLen,
335 byte[] out, int outOffset)
336 throws ShortBufferException {
337 throw new IllegalStateException("Cipher has not been initialized");
338 }
339
340 /**
341 * This operation is not supported by this cipher.
342 * Since it's impossible to initialize this cipher given the
343 * current Cipher.engineInit(...) implementation,
344 * IllegalStateException will always be thrown upon invocation.
345 *
346 * @param in the input buffer.
347 * @param inOffset the offset in <code>in</code> where the input
348 * starts.
349 * @param inLen the input length.
350 *
351 * @return the new buffer with the result.
352 *
353 * @exception IllegalStateException upon invocation of this method.
354 */
355 protected byte[] engineDoFinal(byte[] in, int inOffset, int inLen)
356 throws IllegalBlockSizeException, BadPaddingException {
357 throw new IllegalStateException("Cipher has not been initialized");
358 }
359
360 /**
361 * This operation is not supported by this cipher.
362 * Since it's impossible to initialize this cipher given the
363 * current Cipher.engineInit(...) implementation,
364 * IllegalStateException will always be thrown upon invocation.
365 *
366 * @param in the input buffer.
367 * @param inOffset the offset in <code>in</code> where the input
368 * starts.
369 * @param inLen the input length.
370 * @param out the buffer for the result.
371 * @param outOffset the ofset in <code>out</code> where the result
372 * is stored.
373 *
374 * @return the number of bytes stored in <code>out</code>.
375 *
376 * @exception IllegalStateException upon invocation of this method.
377 */
378 protected int engineDoFinal(byte[] input, int inputOffset, int inputLen,
379 byte[] output, int outputOffset)
380 throws IllegalBlockSizeException, ShortBufferException,
381 BadPaddingException {
382 throw new IllegalStateException("Cipher has not been initialized");
383 }
384
385 /**
386 * Returns the parameters used with this cipher.
387 * Note that null maybe returned if this cipher does not use any
388 * parameters or when it has not be set, e.g. initialized with
389 * UNWRAP_MODE but wrapped key data has not been given.
390 *
391 * @return the parameters used with this cipher; can be null.
392 */
393 protected AlgorithmParameters engineGetParameters() {
394 AlgorithmParameters params = null;
395 if (iv != null) {
396 String algo = cipherKey.getAlgorithm();
397 try {
398 params = AlgorithmParameters.getInstance(algo, "SunJCE");
399 } catch (NoSuchAlgorithmException nsae) {
400 // should never happen
401 throw new RuntimeException("Cannot find " + algo +
402 " AlgorithmParameters implementation in SunJCE provider");
403 } catch (NoSuchProviderException nspe) {
404 // should never happen
405 throw new RuntimeException("Cannot find SunJCE provider");
406 }
407 try {
408 params.init(new IvParameterSpec(iv));
409 } catch (InvalidParameterSpecException ipse) {
410 // should never happen
411 throw new RuntimeException("IvParameterSpec not supported");
412 }
413 }
414 return params;
415 }
416
417 /**
418 * Returns the key size of the given key object in number of bits.
419 * This cipher always return the same key size as the DESede ciphers.
420 *
421 * @param key the key object.
422 *
423 * @return the "effective" key size of the given key object.
424 *
425 * @exception InvalidKeyException if <code>key</code> is invalid.
426 */
427 protected int engineGetKeySize(Key key) throws InvalidKeyException {
428 byte[] encoded = key.getEncoded();
429 if (encoded.length != 24) {
430 throw new InvalidKeyException("Invalid key length: " +
431 encoded.length + " bytes");
432 }
433 // Return the effective key length
434 return 112;
435 }
436
437 /**
438 * Wrap a key.
439 *
440 * @param key the key to be wrapped.
441 *
442 * @return the wrapped key.
443 *
444 * @exception IllegalBlockSizeException if this cipher is a block
445 * cipher, no padding has been requested, and the length of the
446 * encoding of the key to be wrapped is not a
447 * multiple of the block size.
448 *
449 * @exception InvalidKeyException if it is impossible or unsafe to
450 * wrap the key with this cipher (e.g., a hardware protected key is
451 * being passed to a software only cipher).
452 */
453 protected byte[] engineWrap(Key key)
454 throws IllegalBlockSizeException, InvalidKeyException {
455 byte[] keyVal = key.getEncoded();
456 if ((keyVal == null) || (keyVal.length == 0)) {
457 throw new InvalidKeyException("Cannot get an encoding of " +
458 "the key to be wrapped");
459 }
460
461 byte[] cks = getChecksum(keyVal);
462 byte[] out = new byte[iv.length + keyVal.length + cks.length];
463
464 System.arraycopy(keyVal, 0, out, iv.length, keyVal.length);
465 System.arraycopy(cks, 0, out, iv.length+keyVal.length, cks.length);
466 cipher.encrypt(out, iv.length, keyVal.length+cks.length,
467 out, iv.length);
468
469 System.arraycopy(iv, 0, out, 0, iv.length);
470 // reverse the array content
471 for (int i = 0; i < out.length/2; i++) {
472 byte temp = out[i];
473 out[i] = out[out.length-1-i];
474 out[out.length-1-i] = temp;
475 }
476 try {
477 cipher.init(false, cipherKey.getAlgorithm(),
478 cipherKey.getEncoded(), IV2);
479 } catch (InvalidKeyException ike) {
480 // should never happen
481 throw new RuntimeException("Internal cipher key is corrupted");
482 }
483 cipher.encrypt(out, 0, out.length, out, 0);
484
485 // restore cipher state to prior to this call
486 try {
487 cipher.init(decrypting, cipherKey.getAlgorithm(),
488 cipherKey.getEncoded(), iv);
489 } catch (InvalidKeyException ike) {
490 // should never happen
491 throw new RuntimeException("Internal cipher key is corrupted");
492 }
493 return out;
494 }
495
496 /**
497 * Unwrap a previously wrapped key.
498 *
499 * @param wrappedKey the key to be unwrapped.
500 *
501 * @param wrappedKeyAlgorithm the algorithm the wrapped key is for.
502 *
503 * @param wrappedKeyType the type of the wrapped key.
504 * This is one of <code>Cipher.SECRET_KEY</code>,
505 * <code>Cipher.PRIVATE_KEY</code>, or <code>Cipher.PUBLIC_KEY</code>.
506 *
507 * @return the unwrapped key.
508 *
509 * @exception NoSuchAlgorithmException if no installed providers
510 * can create keys of type <code>wrappedKeyType</code> for the
511 * <code>wrappedKeyAlgorithm</code>.
512 *
513 * @exception InvalidKeyException if <code>wrappedKey</code> does not
514 * represent a wrapped key of type <code>wrappedKeyType</code> for
515 * the <code>wrappedKeyAlgorithm</code>.
516 */
517 protected Key engineUnwrap(byte[] wrappedKey,
518 String wrappedKeyAlgorithm,
519 int wrappedKeyType)
520 throws InvalidKeyException, NoSuchAlgorithmException {
521 if (wrappedKey.length == 0) {
522 throw new InvalidKeyException("The wrapped key is empty");
523 }
524 byte[] buffer = new byte[wrappedKey.length];
525 cipher.decrypt(wrappedKey, 0, wrappedKey.length, buffer, 0);
526
527 // reverse array content
528 for (int i = 0; i < buffer.length/2; i++) {
529 byte temp = buffer[i];
530 buffer[i] = buffer[buffer.length-1-i];
531 buffer[buffer.length-1-i] = temp;
532 }
533 iv = new byte[IV2.length];
534 System.arraycopy(buffer, 0, iv, 0, iv.length);
535 cipher.init(true, cipherKey.getAlgorithm(), cipherKey.getEncoded(),
536 iv);
537 cipher.decrypt(buffer, iv.length, buffer.length-iv.length,
538 buffer, iv.length);
539 int origLen = buffer.length - iv.length - 8;
540 byte[] cks = getChecksum(buffer, iv.length, origLen);
541 int offset = iv.length + origLen;
542 for (int i = 0; i < cks.length; i++) {
543 if (buffer[offset + i] != cks[i]) {
544 throw new InvalidKeyException("Checksum comparison failed");
545 }
546 }
547 // restore cipher state to prior to this call
548 cipher.init(decrypting, cipherKey.getAlgorithm(),
549 cipherKey.getEncoded(), IV2);
550 byte[] out = new byte[origLen];
551 System.arraycopy(buffer, iv.length, out, 0, out.length);
552 return ConstructKeys.constructKey(out, wrappedKeyAlgorithm,
553 wrappedKeyType);
554 }
555
556 private static final byte[] getChecksum(byte[] in) {
557 return getChecksum(in, 0, in.length);
558 }
559 private static final byte[] getChecksum(byte[] in, int offset, int len) {
560 MessageDigest md = null;
561 try {
562 md = MessageDigest.getInstance("SHA1");
563 } catch (NoSuchAlgorithmException nsae) {
564 throw new RuntimeException("SHA1 message digest not available");
565 }
566 md.update(in, offset, len);
567 byte[] cks = new byte[8];
568 System.arraycopy(md.digest(), 0, cks, 0, cks.length);
569 return cks;
570 }
571}