25 |
25 |
26 package com.sun.crypto.provider; |
26 package com.sun.crypto.provider; |
27 |
27 |
28 import java.security.*; |
28 import java.security.*; |
29 import java.security.spec.*; |
29 import java.security.spec.*; |
|
30 import java.util.Arrays; |
30 import javax.crypto.*; |
31 import javax.crypto.*; |
31 import javax.crypto.spec.*; |
32 import javax.crypto.spec.*; |
32 |
33 |
33 /** |
34 /** |
34 * This class represents password-based encryption as defined by the PKCS #5 |
35 * This class represents password-based encryption as defined by the PKCS #5 |
211 if (((opmode == Cipher.DECRYPT_MODE) || |
212 if (((opmode == Cipher.DECRYPT_MODE) || |
212 (opmode == Cipher.UNWRAP_MODE)) && (params == null)) { |
213 (opmode == Cipher.UNWRAP_MODE)) && (params == null)) { |
213 throw new InvalidAlgorithmParameterException("Parameters " |
214 throw new InvalidAlgorithmParameterException("Parameters " |
214 + "missing"); |
215 + "missing"); |
215 } |
216 } |
216 if ((key == null) || |
217 if (key == null) { |
217 (key.getEncoded() == null) || |
218 throw new InvalidKeyException("Null key"); |
218 !(key.getAlgorithm().regionMatches(true, 0, "PBE", 0, 3))) { |
219 } |
219 throw new InvalidKeyException("Missing password"); |
220 |
220 } |
221 byte[] derivedKey; |
221 |
222 byte[] passwdBytes = key.getEncoded(); |
222 if (params == null) { |
223 try { |
223 // create random salt and use default iteration count |
224 if ((passwdBytes == null) || |
224 salt = new byte[8]; |
225 !(key.getAlgorithm().regionMatches(true, 0, "PBE", 0, 3))) { |
225 random.nextBytes(salt); |
226 throw new InvalidKeyException("Missing password"); |
226 } else { |
227 } |
227 if (!(params instanceof PBEParameterSpec)) { |
228 |
228 throw new InvalidAlgorithmParameterException |
229 if (params == null) { |
229 ("Wrong parameter type: PBE expected"); |
230 // create random salt and use default iteration count |
230 } |
231 salt = new byte[8]; |
231 salt = ((PBEParameterSpec) params).getSalt(); |
232 random.nextBytes(salt); |
232 // salt must be 8 bytes long (by definition) |
233 } else { |
233 if (salt.length != 8) { |
234 if (!(params instanceof PBEParameterSpec)) { |
234 throw new InvalidAlgorithmParameterException |
235 throw new InvalidAlgorithmParameterException |
235 ("Salt must be 8 bytes long"); |
236 ("Wrong parameter type: PBE expected"); |
236 } |
237 } |
237 iCount = ((PBEParameterSpec) params).getIterationCount(); |
238 salt = ((PBEParameterSpec) params).getSalt(); |
238 if (iCount <= 0) { |
239 // salt must be 8 bytes long (by definition) |
239 throw new InvalidAlgorithmParameterException |
240 if (salt.length != 8) { |
240 ("IterationCount must be a positive number"); |
241 throw new InvalidAlgorithmParameterException |
241 } |
242 ("Salt must be 8 bytes long"); |
242 } |
243 } |
243 |
244 iCount = ((PBEParameterSpec) params).getIterationCount(); |
244 byte[] derivedKey = deriveCipherKey(key); |
245 if (iCount <= 0) { |
|
246 throw new InvalidAlgorithmParameterException |
|
247 ("IterationCount must be a positive number"); |
|
248 } |
|
249 } |
|
250 derivedKey = deriveCipherKey(passwdBytes); |
|
251 } finally { |
|
252 if (passwdBytes != null) Arrays.fill(passwdBytes, (byte) 0x00); |
|
253 } |
245 // use all but the last 8 bytes as the key value |
254 // use all but the last 8 bytes as the key value |
246 SecretKeySpec cipherKey = new SecretKeySpec(derivedKey, 0, |
255 SecretKeySpec cipherKey = new SecretKeySpec(derivedKey, 0, |
247 derivedKey.length-8, algo); |
256 derivedKey.length-8, algo); |
248 // use the last 8 bytes as the IV |
257 // use the last 8 bytes as the IV |
249 IvParameterSpec ivSpec = new IvParameterSpec(derivedKey, |
258 IvParameterSpec ivSpec = new IvParameterSpec(derivedKey, |
251 8); |
260 8); |
252 // initialize the underlying cipher |
261 // initialize the underlying cipher |
253 cipher.init(opmode, cipherKey, ivSpec, random); |
262 cipher.init(opmode, cipherKey, ivSpec, random); |
254 } |
263 } |
255 |
264 |
256 private byte[] deriveCipherKey(Key key) { |
265 private byte[] deriveCipherKey(byte[] passwdBytes) { |
257 |
266 |
258 byte[] result = null; |
267 byte[] result = null; |
259 byte[] passwdBytes = key.getEncoded(); |
|
260 |
268 |
261 if (algo.equals("DES")) { |
269 if (algo.equals("DES")) { |
262 // P || S (password concatenated with salt) |
270 // P || S (password concatenated with salt) |
263 byte[] concat = new byte[Math.addExact(passwdBytes.length, salt.length)]; |
271 byte[] concat = new byte[Math.addExact(passwdBytes.length, salt.length)]; |
264 System.arraycopy(passwdBytes, 0, concat, 0, passwdBytes.length); |
272 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); |
273 System.arraycopy(salt, 0, concat, passwdBytes.length, salt.length); |
267 |
274 |
268 // digest P || S with c iterations |
275 // digest P || S with c iterations |
269 byte[] toBeHashed = concat; |
276 byte[] toBeHashed = concat; |
270 for (int i = 0; i < iCount; i++) { |
277 for (int i = 0; i < iCount; i++) { |
271 md.update(toBeHashed); |
278 md.update(toBeHashed); |
272 toBeHashed = md.digest(); // this resets the digest |
279 toBeHashed = md.digest(); // this resets the digest |
273 } |
280 } |
274 java.util.Arrays.fill(concat, (byte)0x00); |
281 Arrays.fill(concat, (byte)0x00); |
275 result = toBeHashed; |
282 result = toBeHashed; |
276 } else if (algo.equals("DESede")) { |
283 } else if (algo.equals("DESede")) { |
277 // if the 2 salt halves are the same, invert one of them |
284 // if the 2 salt halves are the same, invert one of them |
278 int i; |
285 int i; |
279 for (i=0; i<4; i++) { |
286 for (i=0; i<4; i++) { |
292 // half, go through the loop as many times as specified by the |
299 // half, go through the loop as many times as specified by the |
293 // iteration count parameter (inner for loop). |
300 // iteration count parameter (inner for loop). |
294 // Concatenate the output from each digest round with the |
301 // Concatenate the output from each digest round with the |
295 // password, and use the result as the input to the next digest |
302 // password, and use the result as the input to the next digest |
296 // operation. |
303 // operation. |
297 byte[] kBytes = null; |
|
298 IvParameterSpec iv = null; |
|
299 byte[] toBeHashed = null; |
304 byte[] toBeHashed = null; |
300 result = new byte[DESedeKeySpec.DES_EDE_KEY_LEN + |
305 result = new byte[DESedeKeySpec.DES_EDE_KEY_LEN + |
301 DESConstants.DES_BLOCK_SIZE]; |
306 DESConstants.DES_BLOCK_SIZE]; |
302 for (i = 0; i < 2; i++) { |
307 for (i = 0; i < 2; i++) { |
303 toBeHashed = new byte[salt.length/2]; |
308 toBeHashed = new byte[salt.length/2]; |
304 System.arraycopy(salt, i*(salt.length/2), toBeHashed, 0, |
309 System.arraycopy(salt, i*(salt.length/2), toBeHashed, 0, |
305 toBeHashed.length); |
310 toBeHashed.length); |
306 for (int j=0; j < iCount; j++) { |
311 for (int j=0; j < iCount; j++) { |
307 md.update(toBeHashed); |
312 md.update(toBeHashed); |
308 md.update(passwdBytes); |
313 md.update(passwdBytes); |
309 toBeHashed = md.digest(); // this resets the digest |
314 toBeHashed = md.digest(); |
310 } |
315 } |
311 System.arraycopy(toBeHashed, 0, result, i*16, |
316 System.arraycopy(toBeHashed, 0, result, i*16, |
312 toBeHashed.length); |
317 toBeHashed.length); |
313 } |
318 } |
314 } |
319 } |
|
320 // clear data used in message |
|
321 md.reset(); |
315 return result; |
322 return result; |
316 } |
323 } |
317 |
324 |
318 void init(int opmode, Key key, AlgorithmParameters params, |
325 void init(int opmode, Key key, AlgorithmParameters params, |
319 SecureRandom random) |
326 SecureRandom random) |
476 * being passed to a software only cipher). |
483 * being passed to a software only cipher). |
477 */ |
484 */ |
478 byte[] wrap(Key key) |
485 byte[] wrap(Key key) |
479 throws IllegalBlockSizeException, InvalidKeyException { |
486 throws IllegalBlockSizeException, InvalidKeyException { |
480 byte[] result = null; |
487 byte[] result = null; |
481 |
488 byte[] encodedKey = null; |
482 try { |
489 try { |
483 byte[] encodedKey = key.getEncoded(); |
490 encodedKey = key.getEncoded(); |
484 if ((encodedKey == null) || (encodedKey.length == 0)) { |
491 if ((encodedKey == null) || (encodedKey.length == 0)) { |
485 throw new InvalidKeyException("Cannot get an encoding of " + |
492 throw new InvalidKeyException("Cannot get an encoding of " + |
486 "the key to be wrapped"); |
493 "the key to be wrapped"); |
487 } |
494 } |
488 |
495 |
489 result = doFinal(encodedKey, 0, encodedKey.length); |
496 result = doFinal(encodedKey, 0, encodedKey.length); |
490 } catch (BadPaddingException e) { |
497 } catch (BadPaddingException e) { |
491 // Should never happen |
498 // Should never happen |
|
499 } finally { |
|
500 if (encodedKey != null) Arrays.fill(encodedKey, (byte)0x00); |
492 } |
501 } |
493 |
502 |
494 return result; |
503 return result; |
495 } |
504 } |
496 |
505 |