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