1 /* 2 * Copyright (c) 2004, 2017, 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></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 */ 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 */ DESedeWrapCipher()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 */ engineSetMode(String mode)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 */ engineSetPadding(String padding)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 */ engineGetBlockSize()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 */ engineGetOutputSize(int inputLen)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 = Math.addExact(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 */ engineGetIV()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 */ engineInit(int opmode, Key key, SecureRandom random)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 */ engineInit(int opmode, Key key, AlgorithmParameterSpec params, SecureRandom random)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 */ engineInit(int opmode, Key key, AlgorithmParameters params, SecureRandom random)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 */ engineUpdate(byte[] in, int inOffset, int inLen)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 */ engineUpdate(byte[] in, int inOffset, int inLen, byte[] out, int outOffset)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 */ engineDoFinal(byte[] in, int inOffset, int inLen)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 */ engineDoFinal(byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset)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 */ engineGetParameters()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 */ engineGetKeySize(Key key)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 */ engineWrap(Key key)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[Math.addExact(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[Math.addExact(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 } catch (InvalidAlgorithmParameterException iape) { 474 // should never happen 475 throw new RuntimeException("Internal cipher IV is invalid"); 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 } catch (InvalidAlgorithmParameterException iape) { 488 // should never happen 489 throw new RuntimeException("Internal cipher IV is invalid"); 490 } 491 return out2; 492 } 493 494 /** 495 * Unwrap a previously wrapped key. 496 * 497 * @param wrappedKey the key to be unwrapped. 498 * 499 * @param wrappedKeyAlgorithm the algorithm the wrapped key is for. 500 * 501 * @param wrappedKeyType the type of the wrapped key. 502 * This is one of <code>Cipher.SECRET_KEY</code>, 503 * <code>Cipher.PRIVATE_KEY</code>, or <code>Cipher.PUBLIC_KEY</code>. 504 * 505 * @return the unwrapped key. 506 * 507 * @exception NoSuchAlgorithmException if no installed providers 508 * can create keys of type <code>wrappedKeyType</code> for the 509 * <code>wrappedKeyAlgorithm</code>. 510 * 511 * @exception InvalidKeyException if <code>wrappedKey</code> does not 512 * represent a wrapped key of type <code>wrappedKeyType</code> for 513 * the <code>wrappedKeyAlgorithm</code>. 514 */ engineUnwrap(byte[] wrappedKey, String wrappedKeyAlgorithm, int wrappedKeyType)515 protected Key engineUnwrap(byte[] wrappedKey, 516 String wrappedKeyAlgorithm, 517 int wrappedKeyType) 518 throws InvalidKeyException, NoSuchAlgorithmException { 519 if (wrappedKey.length == 0) { 520 throw new InvalidKeyException("The wrapped key is empty"); 521 } 522 byte[] buffer = new byte[wrappedKey.length]; 523 cipher.decrypt(wrappedKey, 0, wrappedKey.length, buffer, 0); 524 525 // reverse array content 526 for (int i = 0; i < buffer.length/2; i++) { 527 byte temp = buffer[i]; 528 buffer[i] = buffer[buffer.length-1-i]; 529 buffer[buffer.length-1-i] = temp; 530 } 531 iv = new byte[IV_LEN]; 532 System.arraycopy(buffer, 0, iv, 0, iv.length); 533 try { 534 cipher.init(true, cipherKey.getAlgorithm(), cipherKey.getEncoded(), 535 iv); 536 } catch (InvalidAlgorithmParameterException iape) { 537 throw new InvalidKeyException("IV in wrapped key is invalid"); 538 } 539 byte[] buffer2 = new byte[buffer.length - iv.length]; 540 cipher.decrypt(buffer, iv.length, buffer2.length, 541 buffer2, 0); 542 int keyValLen = buffer2.length - CHECKSUM_LEN; 543 byte[] cks = getChecksum(buffer2, 0, keyValLen); 544 int offset = keyValLen; 545 for (int i = 0; i < CHECKSUM_LEN; i++) { 546 if (buffer2[offset + i] != cks[i]) { 547 throw new InvalidKeyException("Checksum comparison failed"); 548 } 549 } 550 // restore cipher state to prior to this call 551 try { 552 cipher.init(decrypting, cipherKey.getAlgorithm(), 553 cipherKey.getEncoded(), IV2); 554 } catch (InvalidAlgorithmParameterException iape) { 555 throw new InvalidKeyException("IV in wrapped key is invalid"); 556 } 557 byte[] out = new byte[keyValLen]; 558 System.arraycopy(buffer2, 0, out, 0, keyValLen); 559 return ConstructKeys.constructKey(out, wrappedKeyAlgorithm, 560 wrappedKeyType); 561 } 562 getChecksum(byte[] in)563 private static final byte[] getChecksum(byte[] in) { 564 return getChecksum(in, 0, in.length); 565 } getChecksum(byte[] in, int offset, int len)566 private static final byte[] getChecksum(byte[] in, int offset, int len) { 567 MessageDigest md = null; 568 try { 569 md = MessageDigest.getInstance("SHA1"); 570 } catch (NoSuchAlgorithmException nsae) { 571 throw new RuntimeException("SHA1 message digest not available"); 572 } 573 md.update(in, offset, len); 574 byte[] cks = new byte[CHECKSUM_LEN]; 575 System.arraycopy(md.digest(), 0, cks, 0, cks.length); 576 return cks; 577 } 578 } 579