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 26 package com.sun.crypto.provider; 27 28 import java.security.*; 29 import java.security.spec.*; 30 import javax.crypto.*; 31 import 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> 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 */ 46 public 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:<b> 163 * Cipher.WRAP_MODE, and <b> 164 * Cipher.UNWRAP_MODE. 165 * <p>For modes other than the above two, UnsupportedOperationException 166 * will be thrown. 167 * <p>If this cipher requires an initialization vector (IV), it will get 168 * it from <code>random</code>. 169 * 170 * @param opmode the operation mode of this cipher. Only 171 * <code>WRAP_MODE</code> or <code>UNWRAP_MODE</code>) are accepted. 172 * @param key the secret key. 173 * @param random the source of randomness. 174 * 175 * @exception InvalidKeyException if the given key is inappropriate 176 * or if parameters are required but not supplied. 177 */ 178 protected void engineInit(int opmode, Key key, SecureRandom random) 179 throws InvalidKeyException { 180 try { 181 engineInit(opmode, key, (AlgorithmParameterSpec) null, random); 182 } catch (InvalidAlgorithmParameterException iape) { 183 // should never happen 184 InvalidKeyException ike = 185 new InvalidKeyException("Parameters required"); 186 ike.initCause(iape); 187 throw ike; 188 } 189 } 190 191 /** 192 * Initializes this cipher with a key, a set of algorithm parameters, 193 * and a source of randomness. 194 * 195 * <p>The cipher only supports the following two operation modes:<b> 196 * Cipher.WRAP_MODE, and <b> 197 * Cipher.UNWRAP_MODE. 198 * <p>For modes other than the above two, UnsupportedOperationException 199 * will be thrown. 200 * <p>If this cipher requires an initialization vector (IV), it will get 201 * it from <code>random</code>. 202 * 203 * @param opmode the operation mode of this cipher. Only 204 * <code>WRAP_MODE</code> or <code>UNWRAP_MODE</code>) are accepted. 205 * @param key the secret key. 206 * @param params the algorithm parameters. 207 * @param random the source of randomness. 208 * 209 * @exception InvalidKeyException if the given key is inappropriate. 210 * @exception InvalidAlgorithmParameterException if the given algorithm 211 * parameters are inappropriate for this cipher. 212 */ 213 protected void engineInit(int opmode, Key key, 214 AlgorithmParameterSpec params, 215 SecureRandom random) 216 throws InvalidKeyException, InvalidAlgorithmParameterException { 217 byte[] currIv = null; 218 if (opmode == Cipher.WRAP_MODE) { 219 decrypting = false; 220 if (params == null) { 221 iv = new byte[IV_LEN]; 222 if (random == null) { 223 random = SunJCE.getRandom(); 224 } 225 random.nextBytes(iv); 226 } 227 else if (params instanceof IvParameterSpec) { 228 iv = ((IvParameterSpec) params).getIV(); 229 } else { 230 throw new InvalidAlgorithmParameterException 231 ("Wrong parameter type: IV expected"); 232 } 233 currIv = iv; 234 } else if (opmode == Cipher.UNWRAP_MODE) { 235 if (params != null) { 236 throw new InvalidAlgorithmParameterException 237 ("No parameter accepted for unwrapping keys"); 238 } 239 iv = null; 240 decrypting = true; 241 currIv = IV2; 242 } else { 243 throw new UnsupportedOperationException("This cipher can " + 244 "only be used for key wrapping and unwrapping"); 245 } 246 cipher.init(decrypting, key.getAlgorithm(), key.getEncoded(), 247 currIv); 248 cipherKey = key; 249 } 250 251 /** 252 * Initializes this cipher with a key, a set of algorithm parameters, 253 * and a source of randomness. 254 * 255 * <p>The cipher only supports the following two operation modes:<b> 256 * Cipher.WRAP_MODE, and <b> 257 * Cipher.UNWRAP_MODE. 258 * <p>For modes other than the above two, UnsupportedOperationException 259 * will be thrown. 260 * <p>If this cipher requires an initialization vector (IV), it will get 261 * it from <code>random</code>. 262 * 263 * @param opmode the operation mode of this cipher. Only 264 * <code>WRAP_MODE</code> or <code>UNWRAP_MODE</code>) are accepted. 265 * @param key the secret key. 266 * @param params the algorithm parameters. 267 * @param random the source of randomness. 268 * 269 * @exception InvalidKeyException if the given key is inappropriate. 270 * @exception InvalidAlgorithmParameterException if the given algorithm 271 * parameters are inappropriate for this cipher. 272 */ 273 protected void engineInit(int opmode, Key key, 274 AlgorithmParameters params, 275 SecureRandom random) 276 throws InvalidKeyException, InvalidAlgorithmParameterException { 277 IvParameterSpec ivSpec = null; 278 if (params != null) { 279 try { 280 DESedeParameters paramsEng = new DESedeParameters(); 281 paramsEng.engineInit(params.getEncoded()); 282 ivSpec = paramsEng.engineGetParameterSpec(IvParameterSpec.class); 283 } catch (Exception ex) { 284 InvalidAlgorithmParameterException iape = 285 new InvalidAlgorithmParameterException 286 ("Wrong parameter type: IV expected"); 287 iape.initCause(ex); 288 throw iape; 289 } 290 } 291 engineInit(opmode, key, ivSpec, random); 292 } 293 294 /** 295 * This operation is not supported by this cipher. 296 * Since it's impossible to initialize this cipher given the 297 * current Cipher.engineInit(...) implementation, 298 * IllegalStateException will always be thrown upon invocation. 299 * 300 * @param in the input buffer. 301 * @param inOffset the offset in <code>in</code> where the input 302 * starts. 303 * @param inLen the input length. 304 * 305 * @return n/a. 306 * 307 * @exception IllegalStateException upon invocation of this method. 308 */ 309 protected byte[] engineUpdate(byte[] in, int inOffset, int inLen) { 310 throw new IllegalStateException("Cipher has not been initialized"); 311 } 312 313 /** 314 * This operation is not supported by this cipher. 315 * Since it's impossible to initialize this cipher given the 316 * current Cipher.engineInit(...) implementation, 317 * IllegalStateException will always be thrown upon invocation. 318 * 319 * @param in the input buffer. 320 * @param inOffset the offset in <code>in</code> where the input 321 * starts. 322 * @param inLen the input length. 323 * @param out the buffer for the result. 324 * @param outOffset the offset in <code>out</code> where the result 325 * is stored. 326 * 327 * @return n/a. 328 * 329 * @exception IllegalStateException upon invocation of this method. 330 */ 331 protected int engineUpdate(byte[] in, int inOffset, int inLen, 332 byte[] out, int outOffset) 333 throws ShortBufferException { 334 throw new IllegalStateException("Cipher has not been initialized"); 335 } 336 337 /** 338 * This operation is not supported by this cipher. 339 * Since it's impossible to initialize this cipher given the 340 * current Cipher.engineInit(...) implementation, 341 * IllegalStateException will always be thrown upon invocation. 342 * 343 * @param in the input buffer. 344 * @param inOffset the offset in <code>in</code> where the input 345 * starts. 346 * @param inLen the input length. 347 * 348 * @return the new buffer with the result. 349 * 350 * @exception IllegalStateException upon invocation of this method. 351 */ 352 protected byte[] engineDoFinal(byte[] in, int inOffset, int inLen) 353 throws IllegalBlockSizeException, BadPaddingException { 354 throw new IllegalStateException("Cipher has not been initialized"); 355 } 356 357 /** 358 * This operation is not supported by this cipher. 359 * Since it's impossible to initialize this cipher given the 360 * current Cipher.engineInit(...) implementation, 361 * IllegalStateException will always be thrown upon invocation. 362 * 363 * @param in the input buffer. 364 * @param inOffset the offset in <code>in</code> where the input 365 * starts. 366 * @param inLen the input length. 367 * @param out the buffer for the result. 368 * @param outOffset the ofset in <code>out</code> where the result 369 * is stored. 370 * 371 * @return the number of bytes stored in <code>out</code>. 372 * 373 * @exception IllegalStateException upon invocation of this method. 374 */ 375 protected int engineDoFinal(byte[] input, int inputOffset, int inputLen, 376 byte[] output, int outputOffset) 377 throws IllegalBlockSizeException, ShortBufferException, 378 BadPaddingException { 379 throw new IllegalStateException("Cipher has not been initialized"); 380 } 381 382 /** 383 * Returns the parameters used with this cipher. 384 * Note that null maybe returned if this cipher does not use any 385 * parameters or when it has not be set, e.g. initialized with 386 * UNWRAP_MODE but wrapped key data has not been given. 387 * 388 * @return the parameters used with this cipher; can be null. 389 */ 390 protected AlgorithmParameters engineGetParameters() { 391 AlgorithmParameters params = null; 392 if (iv != null) { 393 String algo = cipherKey.getAlgorithm(); 394 try { 395 params = AlgorithmParameters.getInstance(algo, 396 SunJCE.getInstance()); 397 params.init(new IvParameterSpec(iv)); 398 } catch (NoSuchAlgorithmException nsae) { 399 // should never happen 400 throw new RuntimeException("Cannot find " + algo + 401 " AlgorithmParameters implementation in SunJCE provider"); 402 } catch (InvalidParameterSpecException ipse) { 403 // should never happen 404 throw new RuntimeException("IvParameterSpec not supported"); 405 } 406 } 407 return params; 408 } 409 410 /** 411 * Returns the key size of the given key object in number of bits. 412 * This cipher always return the same key size as the DESede ciphers. 413 * 414 * @param key the key object. 415 * 416 * @return the "effective" key size of the given key object. 417 * 418 * @exception InvalidKeyException if <code>key</code> is invalid. 419 */ 420 protected int engineGetKeySize(Key key) throws InvalidKeyException { 421 byte[] encoded = key.getEncoded(); 422 if (encoded.length != 24) { 423 throw new InvalidKeyException("Invalid key length: " + 424 encoded.length + " bytes"); 425 } 426 // Return the effective key length 427 return 112; 428 } 429 430 /** 431 * Wrap a key. 432 * 433 * @param key the key to be wrapped. 434 * 435 * @return the wrapped key. 436 * 437 * @exception IllegalBlockSizeException if this cipher is a block 438 * cipher, no padding has been requested, and the length of the 439 * encoding of the key to be wrapped is not a 440 * multiple of the block size. 441 * 442 * @exception InvalidKeyException if it is impossible or unsafe to 443 * wrap the key with this cipher (e.g., a hardware protected key is 444 * being passed to a software only cipher). 445 */ 446 protected byte[] engineWrap(Key key) 447 throws IllegalBlockSizeException, InvalidKeyException { 448 byte[] keyVal = key.getEncoded(); 449 if ((keyVal == null) || (keyVal.length == 0)) { 450 throw new InvalidKeyException("Cannot get an encoding of " + 451 "the key to be wrapped"); 452 } 453 454 byte[] cks = getChecksum(keyVal); 455 byte[] in = new byte[keyVal.length + CHECKSUM_LEN]; 456 System.arraycopy(keyVal, 0, in, 0, keyVal.length); 457 System.arraycopy(cks, 0, in, keyVal.length, CHECKSUM_LEN); 458 459 byte[] out = new byte[iv.length + in.length]; 460 System.arraycopy(iv, 0, out, 0, iv.length); 461 462 cipher.encrypt(in, 0, in.length, out, iv.length); 463 464 // reverse the array content 465 for (int i = 0; i < out.length/2; i++) { 466 byte temp = out[i]; 467 out[i] = out[out.length-1-i]; 468 out[out.length-1-i] = temp; 469 } 470 try { 471 cipher.init(false, cipherKey.getAlgorithm(), 472 cipherKey.getEncoded(), IV2); 473 } catch (InvalidKeyException ike) { 474 // should never happen 475 throw new RuntimeException("Internal cipher key is corrupted"); 476 } 477 byte[] out2 = new byte[out.length]; 478 cipher.encrypt(out, 0, out.length, out2, 0); 479 480 // restore cipher state to prior to this call 481 try { 482 cipher.init(decrypting, cipherKey.getAlgorithm(), 483 cipherKey.getEncoded(), iv); 484 } catch (InvalidKeyException ike) { 485 // should never happen 486 throw new RuntimeException("Internal cipher key is corrupted"); 487 } 488 return out2; 489 } 490 491 /** 492 * Unwrap a previously wrapped key. 493 * 494 * @param wrappedKey the key to be unwrapped. 495 * 496 * @param wrappedKeyAlgorithm the algorithm the wrapped key is for. 497 * 498 * @param wrappedKeyType the type of the wrapped key. 499 * This is one of <code>Cipher.SECRET_KEY</code>, 500 * <code>Cipher.PRIVATE_KEY</code>, or <code>Cipher.PUBLIC_KEY</code>. 501 * 502 * @return the unwrapped key. 503 * 504 * @exception NoSuchAlgorithmException if no installed providers 505 * can create keys of type <code>wrappedKeyType</code> for the 506 * <code>wrappedKeyAlgorithm</code>. 507 * 508 * @exception InvalidKeyException if <code>wrappedKey</code> does not 509 * represent a wrapped key of type <code>wrappedKeyType</code> for 510 * the <code>wrappedKeyAlgorithm</code>. 511 */ 512 protected Key engineUnwrap(byte[] wrappedKey, 513 String wrappedKeyAlgorithm, 514 int wrappedKeyType) 515 throws InvalidKeyException, NoSuchAlgorithmException { 516 if (wrappedKey.length == 0) { 517 throw new InvalidKeyException("The wrapped key is empty"); 518 } 519 byte[] buffer = new byte[wrappedKey.length]; 520 cipher.decrypt(wrappedKey, 0, wrappedKey.length, buffer, 0); 521 522 // reverse array content 523 for (int i = 0; i < buffer.length/2; i++) { 524 byte temp = buffer[i]; 525 buffer[i] = buffer[buffer.length-1-i]; 526 buffer[buffer.length-1-i] = temp; 527 } 528 iv = new byte[IV_LEN]; 529 System.arraycopy(buffer, 0, iv, 0, iv.length); 530 cipher.init(true, cipherKey.getAlgorithm(), cipherKey.getEncoded(), 531 iv); 532 byte[] buffer2 = new byte[buffer.length - iv.length]; 533 cipher.decrypt(buffer, iv.length, buffer2.length, 534 buffer2, 0); 535 int keyValLen = buffer2.length - CHECKSUM_LEN; 536 byte[] cks = getChecksum(buffer2, 0, keyValLen); 537 int offset = keyValLen; 538 for (int i = 0; i < CHECKSUM_LEN; i++) { 539 if (buffer2[offset + i] != cks[i]) { 540 throw new InvalidKeyException("Checksum comparison failed"); 541 } 542 } 543 // restore cipher state to prior to this call 544 cipher.init(decrypting, cipherKey.getAlgorithm(), 545 cipherKey.getEncoded(), IV2); 546 byte[] out = new byte[keyValLen]; 547 System.arraycopy(buffer2, 0, out, 0, keyValLen); 548 return ConstructKeys.constructKey(out, wrappedKeyAlgorithm, 549 wrappedKeyType); 550 } 551 552 private static final byte[] getChecksum(byte[] in) { 553 return getChecksum(in, 0, in.length); 554 } 555 private static final byte[] getChecksum(byte[] in, int offset, int len) { 556 MessageDigest md = null; 557 try { 558 md = MessageDigest.getInstance("SHA1"); 559 } catch (NoSuchAlgorithmException nsae) { 560 throw new RuntimeException("SHA1 message digest not available"); 561 } 562 md.update(in, offset, len); 563 byte[] cks = new byte[CHECKSUM_LEN]; 564 System.arraycopy(md.digest(), 0, cks, 0, cks.length); 565 return cks; 566 } 567 }