1 /* 2 * Copyright (c) 2002, 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 represents password-based encryption as defined by the PKCS #5 35 * standard. 36 * 37 * @author Jan Luehe 38 * 39 * 40 * @see javax.crypto.Cipher 41 */ 42 final class PBES1Core { 43 44 // the encapsulated DES cipher 45 private CipherCore cipher; 46 private MessageDigest md; 47 private int blkSize; 48 private String algo = null; 49 private byte[] salt = null; 50 private int iCount = 10; 51 52 /** 53 * Creates an instance of PBE Cipher using the specified CipherSpi 54 * instance. 55 * 56 */ 57 PBES1Core(String cipherAlg) throws NoSuchAlgorithmException, 58 NoSuchPaddingException { 59 algo = cipherAlg; 60 if (algo.equals("DES")) { 61 cipher = new CipherCore(new DESCrypt(), 62 DESConstants.DES_BLOCK_SIZE); 63 } else if (algo.equals("DESede")) { 64 65 cipher = new CipherCore(new DESedeCrypt(), 66 DESConstants.DES_BLOCK_SIZE); 67 } else { 68 throw new NoSuchAlgorithmException("No Cipher implementation " + 69 "for PBEWithMD5And" + algo); 70 } 71 cipher.setMode("CBC"); 72 cipher.setPadding("PKCS5Padding"); 73 // get instance of MD5 74 md = MessageDigest.getInstance("MD5"); 75 } 76 77 /** 78 * Sets the mode of this cipher. This algorithm can only be run in CBC 79 * mode. 80 * 81 * @param mode the cipher mode 82 * 83 * @exception NoSuchAlgorithmException if the requested cipher mode is 84 * invalid 85 */ 86 void setMode(String mode) throws NoSuchAlgorithmException { 87 cipher.setMode(mode); 88 } 89 90 /** 91 * Sets the padding mechanism of this cipher. This algorithm only uses 92 * PKCS #5 padding. 93 * 94 * @param padding the padding mechanism 95 * 96 * @exception NoSuchPaddingException if the requested padding mechanism 97 * is invalid 98 */ 99 void setPadding(String paddingScheme) throws NoSuchPaddingException { 100 cipher.setPadding(paddingScheme); 101 } 102 103 /** 104 * Returns the block size (in bytes). 105 * 106 * @return the block size (in bytes) 107 */ 108 int getBlockSize() { 109 return DESConstants.DES_BLOCK_SIZE; 110 } 111 112 /** 113 * Returns the length in bytes that an output buffer would need to be in 114 * order to hold the result of the next <code>update</code> or 115 * <code>doFinal</code> operation, given the input length 116 * <code>inputLen</code> (in bytes). 117 * 118 * <p>This call takes into account any unprocessed (buffered) data from a 119 * previous <code>update</code> call, and padding. 120 * 121 * <p>The actual output length of the next <code>update</code> or 122 * <code>doFinal</code> call may be smaller than the length returned by 123 * this method. 124 * 125 * @param inputLen the input length (in bytes) 126 * 127 * @return the required output buffer size (in bytes) 128 * 129 */ 130 int getOutputSize(int inputLen) { 131 return cipher.getOutputSize(inputLen); 132 } 133 134 /** 135 * Returns the initialization vector (IV) in a new buffer. 136 * 137 * <p> This is useful in the case where a random IV has been created 138 * (see <a href = "#init">init</a>), 139 * or in the context of password-based encryption or 140 * decryption, where the IV is derived from a user-supplied password. 141 * 142 * @return the initialization vector in a new buffer, or null if the 143 * underlying algorithm does not use an IV, or if the IV has not yet 144 * been set. 145 */ 146 byte[] getIV() { 147 return cipher.getIV(); 148 } 149 150 /** 151 * Returns the parameters used with this cipher. 152 * 153 * <p>The returned parameters may be the same that were used to initialize 154 * this cipher, or may contain the default set of parameters or a set of 155 * randomly generated parameters used by the underlying cipher 156 * implementation (provided that the underlying cipher implementation 157 * uses a default set of parameters or creates new parameters if it needs 158 * parameters but was not initialized with any). 159 * 160 * @return the parameters used with this cipher, or null if this cipher 161 * does not use any parameters. 162 */ 163 AlgorithmParameters getParameters() { 164 AlgorithmParameters params = null; 165 if (salt == null) { 166 salt = new byte[8]; 167 SunJCE.getRandom().nextBytes(salt); 168 } 169 PBEParameterSpec pbeSpec = new PBEParameterSpec(salt, iCount); 170 try { 171 params = AlgorithmParameters.getInstance("PBEWithMD5And" + 172 (algo.equalsIgnoreCase("DES")? "DES":"TripleDES"), 173 SunJCE.getInstance()); 174 params.init(pbeSpec); 175 } catch (NoSuchAlgorithmException nsae) { 176 // should never happen 177 throw new RuntimeException("SunJCE called, but not configured"); 178 } catch (InvalidParameterSpecException ipse) { 179 // should never happen 180 throw new RuntimeException("PBEParameterSpec not supported"); 181 } 182 return params; 183 } 184 185 /** 186 * Initializes this cipher with a key, a set of 187 * algorithm parameters, and a source of randomness. 188 * The cipher is initialized for one of the following four operations: 189 * encryption, decryption, key wrapping or key unwrapping, depending on 190 * the value of <code>opmode</code>. 191 * 192 * <p>If this cipher (including its underlying feedback or padding scheme) 193 * requires any random bytes, it will get them from <code>random</code>. 194 * 195 * @param opmode the operation mode of this cipher (this is one of 196 * the following: 197 * <code>ENCRYPT_MODE</code>, <code>DECRYPT_MODE</code>), 198 * <code>WRAP_MODE</code> or <code>UNWRAP_MODE</code>) 199 * @param key the encryption key 200 * @param params the algorithm parameters 201 * @param random the source of randomness 202 * 203 * @exception InvalidKeyException if the given key is inappropriate for 204 * initializing this cipher 205 * @exception InvalidAlgorithmParameterException if the given algorithm 206 * parameters are inappropriate for this cipher 207 */ 208 void init(int opmode, Key key, AlgorithmParameterSpec params, 209 SecureRandom random) 210 throws InvalidKeyException, InvalidAlgorithmParameterException { 211 if (((opmode == Cipher.DECRYPT_MODE) || 212 (opmode == Cipher.UNWRAP_MODE)) && (params == null)) { 213 throw new InvalidAlgorithmParameterException("Parameters " 214 + "missing"); 215 } 216 if ((key == null) || 217 (key.getEncoded() == null) || 218 !(key.getAlgorithm().regionMatches(true, 0, "PBE", 0, 3))) { 219 throw new InvalidKeyException("Missing password"); 220 } 221 222 if (params == null) { 223 // create random salt and use default iteration count 224 salt = new byte[8]; 225 random.nextBytes(salt); 226 } else { 227 if (!(params instanceof PBEParameterSpec)) { 228 throw new InvalidAlgorithmParameterException 229 ("Wrong parameter type: PBE expected"); 230 } 231 salt = ((PBEParameterSpec) params).getSalt(); 232 // salt must be 8 bytes long (by definition) 233 if (salt.length != 8) { 234 throw new InvalidAlgorithmParameterException 235 ("Salt must be 8 bytes long"); 236 } 237 iCount = ((PBEParameterSpec) params).getIterationCount(); 238 if (iCount <= 0) { 239 throw new InvalidAlgorithmParameterException 240 ("IterationCount must be a positive number"); 241 } 242 } 243 244 byte[] derivedKey = deriveCipherKey(key); 245 // use all but the last 8 bytes as the key value 246 SecretKeySpec cipherKey = new SecretKeySpec(derivedKey, 0, 247 derivedKey.length-8, algo); 248 // use the last 8 bytes as the IV 249 IvParameterSpec ivSpec = new IvParameterSpec(derivedKey, 250 derivedKey.length-8, 251 8); 252 // initialize the underlying cipher 253 cipher.init(opmode, cipherKey, ivSpec, random); 254 } 255 256 private byte[] deriveCipherKey(Key key) { 257 258 byte[] result = null; 259 byte[] passwdBytes = key.getEncoded(); 260 261 if (algo.equals("DES")) { 262 // P || S (password concatenated with salt) 263 byte[] concat = new byte[Math.addExact(passwdBytes.length, salt.length)]; 264 System.arraycopy(passwdBytes, 0, concat, 0, passwdBytes.length); 265 java.util.Arrays.fill(passwdBytes, (byte)0x00); 266 System.arraycopy(salt, 0, concat, passwdBytes.length, salt.length); 267 268 // digest P || S with c iterations 269 byte[] toBeHashed = concat; 270 for (int i = 0; i < iCount; i++) { 271 md.update(toBeHashed); 272 toBeHashed = md.digest(); // this resets the digest 273 } 274 java.util.Arrays.fill(concat, (byte)0x00); 275 result = toBeHashed; 276 } else if (algo.equals("DESede")) { 277 // if the 2 salt halves are the same, invert one of them 278 int i; 279 for (i=0; i<4; i++) { 280 if (salt[i] != salt[i+4]) 281 break; 282 } 283 if (i==4) { // same, invert 1st half 284 for (i=0; i<2; i++) { 285 byte tmp = salt[i]; 286 salt[i] = salt[3-i]; 287 salt[3-i] = tmp; 288 } 289 } 290 291 // Now digest each half (concatenated with password). For each 292 // half, go through the loop as many times as specified by the 293 // iteration count parameter (inner for loop). 294 // Concatenate the output from each digest round with the 295 // password, and use the result as the input to the next digest 296 // operation. 297 byte[] kBytes = null; 298 IvParameterSpec iv = null; 299 byte[] toBeHashed = null; 300 result = new byte[DESedeKeySpec.DES_EDE_KEY_LEN + 301 DESConstants.DES_BLOCK_SIZE]; 302 for (i = 0; i < 2; i++) { 303 toBeHashed = new byte[salt.length/2]; 304 System.arraycopy(salt, i*(salt.length/2), toBeHashed, 0, 305 toBeHashed.length); 306 for (int j=0; j < iCount; j++) { 307 md.update(toBeHashed); 308 md.update(passwdBytes); 309 toBeHashed = md.digest(); // this resets the digest 310 } 311 System.arraycopy(toBeHashed, 0, result, i*16, 312 toBeHashed.length); 313 } 314 } 315 return result; 316 } 317 318 void init(int opmode, Key key, AlgorithmParameters params, 319 SecureRandom random) 320 throws InvalidKeyException, InvalidAlgorithmParameterException { 321 PBEParameterSpec pbeSpec = null; 322 if (params != null) { 323 try { 324 pbeSpec = params.getParameterSpec(PBEParameterSpec.class); 325 } catch (InvalidParameterSpecException ipse) { 326 throw new InvalidAlgorithmParameterException("Wrong parameter " 327 + "type: PBE " 328 + "expected"); 329 } 330 } 331 init(opmode, key, pbeSpec, random); 332 } 333 334 /** 335 * Continues a multiple-part encryption or decryption operation 336 * (depending on how this cipher was initialized), processing another data 337 * part. 338 * 339 * <p>The first <code>inputLen</code> bytes in the <code>input</code> 340 * buffer, starting at <code>inputOffset</code>, are processed, and the 341 * result is stored in a new buffer. 342 * 343 * @param input the input buffer 344 * @param inputOffset the offset in <code>input</code> where the input 345 * starts 346 * @param inputLen the input length 347 * 348 * @return the new buffer with the result 349 * 350 */ 351 byte[] update(byte[] input, int inputOffset, int inputLen) { 352 return cipher.update(input, inputOffset, inputLen); 353 } 354 355 /** 356 * Continues a multiple-part encryption or decryption operation 357 * (depending on how this cipher was initialized), processing another data 358 * part. 359 * 360 * <p>The first <code>inputLen</code> bytes in the <code>input</code> 361 * buffer, starting at <code>inputOffset</code>, are processed, and the 362 * result is stored in the <code>output</code> buffer, starting at 363 * <code>outputOffset</code>. 364 * 365 * @param input the input buffer 366 * @param inputOffset the offset in <code>input</code> where the input 367 * starts 368 * @param inputLen the input length 369 * @param output the buffer for the result 370 * @param outputOffset the offset in <code>output</code> where the result 371 * is stored 372 * 373 * @return the number of bytes stored in <code>output</code> 374 * 375 * @exception ShortBufferException if the given output buffer is too small 376 * to hold the result 377 */ 378 int update(byte[] input, int inputOffset, int inputLen, 379 byte[] output, int outputOffset) 380 throws ShortBufferException { 381 return cipher.update(input, inputOffset, inputLen, 382 output, outputOffset); 383 } 384 385 /** 386 * Encrypts or decrypts data in a single-part operation, 387 * or finishes a multiple-part operation. 388 * The data is encrypted or decrypted, depending on how this cipher was 389 * initialized. 390 * 391 * <p>The first <code>inputLen</code> bytes in the <code>input</code> 392 * buffer, starting at <code>inputOffset</code>, and any input bytes that 393 * may have been buffered during a previous <code>update</code> operation, 394 * are processed, with padding (if requested) being applied. 395 * The result is stored in a new buffer. 396 * 397 * <p>The cipher is reset to its initial state (uninitialized) after this 398 * call. 399 * 400 * @param input the input buffer 401 * @param inputOffset the offset in <code>input</code> where the input 402 * starts 403 * @param inputLen the input length 404 * 405 * @return the new buffer with the result 406 * 407 * @exception IllegalBlockSizeException if this cipher is a block cipher, 408 * no padding has been requested (only in encryption mode), and the total 409 * input length of the data processed by this cipher is not a multiple of 410 * block size 411 * @exception BadPaddingException if decrypting and padding is chosen, 412 * but the last input data does not have proper padding bytes. 413 */ 414 byte[] doFinal(byte[] input, int inputOffset, int inputLen) 415 throws IllegalBlockSizeException, BadPaddingException { 416 return cipher.doFinal(input, inputOffset, inputLen); 417 } 418 419 /** 420 * Encrypts or decrypts data in a single-part operation, 421 * or finishes a multiple-part operation. 422 * The data is encrypted or decrypted, depending on how this cipher was 423 * initialized. 424 * 425 * <p>The first <code>inputLen</code> bytes in the <code>input</code> 426 * buffer, starting at <code>inputOffset</code>, and any input bytes that 427 * may have been buffered during a previous <code>update</code> operation, 428 * are processed, with padding (if requested) being applied. 429 * The result is stored in the <code>output</code> buffer, starting at 430 * <code>outputOffset</code>. 431 * 432 * <p>The cipher is reset to its initial state (uninitialized) after this 433 * call. 434 * 435 * @param input the input buffer 436 * @param inputOffset the offset in <code>input</code> where the input 437 * starts 438 * @param inputLen the input length 439 * @param output the buffer for the result 440 * @param outputOffset the offset in <code>output</code> where the result 441 * is stored 442 * 443 * @return the number of bytes stored in <code>output</code> 444 * 445 * @exception IllegalBlockSizeException if this cipher is a block cipher, 446 * no padding has been requested (only in encryption mode), and the total 447 * input length of the data processed by this cipher is not a multiple of 448 * block size 449 * @exception ShortBufferException if the given output buffer is too small 450 * to hold the result 451 * @exception BadPaddingException if decrypting and padding is chosen, 452 * but the last input data does not have proper padding bytes. 453 */ 454 int doFinal(byte[] input, int inputOffset, int inputLen, 455 byte[] output, int outputOffset) 456 throws ShortBufferException, IllegalBlockSizeException, 457 BadPaddingException { 458 return cipher.doFinal(input, inputOffset, inputLen, 459 output, outputOffset); 460 } 461 462 /** 463 * Wrap a key. 464 * 465 * @param key the key to be wrapped. 466 * 467 * @return the wrapped key. 468 * 469 * @exception IllegalBlockSizeException if this cipher is a block 470 * cipher, no padding has been requested, and the length of the 471 * encoding of the key to be wrapped is not a 472 * multiple of the block size. 473 * 474 * @exception InvalidKeyException if it is impossible or unsafe to 475 * wrap the key with this cipher (e.g., a hardware protected key is 476 * being passed to a software only cipher). 477 */ 478 byte[] wrap(Key key) 479 throws IllegalBlockSizeException, InvalidKeyException { 480 byte[] result = null; 481 482 try { 483 byte[] encodedKey = key.getEncoded(); 484 if ((encodedKey == null) || (encodedKey.length == 0)) { 485 throw new InvalidKeyException("Cannot get an encoding of " + 486 "the key to be wrapped"); 487 } 488 489 result = doFinal(encodedKey, 0, encodedKey.length); 490 } catch (BadPaddingException e) { 491 // Should never happen 492 } 493 494 return result; 495 } 496 497 /** 498 * Unwrap a previously wrapped key. 499 * 500 * @param wrappedKey the key to be unwrapped. 501 * 502 * @param wrappedKeyAlgorithm the algorithm the wrapped key is for. 503 * 504 * @param wrappedKeyType the type of the wrapped key. 505 * This is one of <code>Cipher.SECRET_KEY</code>, 506 * <code>Cipher.PRIVATE_KEY</code>, or <code>Cipher.PUBLIC_KEY</code>. 507 * 508 * @return the unwrapped key. 509 * 510 * @exception NoSuchAlgorithmException if no installed providers 511 * can create keys of type <code>wrappedKeyType</code> for the 512 * <code>wrappedKeyAlgorithm</code>. 513 * 514 * @exception InvalidKeyException if <code>wrappedKey</code> does not 515 * represent a wrapped key of type <code>wrappedKeyType</code> for 516 * the <code>wrappedKeyAlgorithm</code>. 517 */ 518 Key unwrap(byte[] wrappedKey, 519 String wrappedKeyAlgorithm, 520 int wrappedKeyType) 521 throws InvalidKeyException, NoSuchAlgorithmException { 522 byte[] encodedKey; 523 try { 524 encodedKey = doFinal(wrappedKey, 0, wrappedKey.length); 525 } catch (BadPaddingException ePadding) { 526 throw new InvalidKeyException("The wrapped key is not padded " + 527 "correctly"); 528 } catch (IllegalBlockSizeException eBlockSize) { 529 throw new InvalidKeyException("The wrapped key does not have " + 530 "the correct length"); 531 } 532 return ConstructKeys.constructKey(encodedKey, wrappedKeyAlgorithm, 533 wrappedKeyType); 534 } 535 }