26 package sun.security.util; |
26 package sun.security.util; |
27 |
27 |
28 import java.io.ByteArrayInputStream; |
28 import java.io.ByteArrayInputStream; |
29 import java.io.IOException; |
29 import java.io.IOException; |
30 import java.security.CodeSigner; |
30 import java.security.CodeSigner; |
31 import java.security.CryptoPrimitive; |
31 import java.security.GeneralSecurityException; |
32 import java.security.MessageDigest; |
32 import java.security.MessageDigest; |
33 import java.security.NoSuchAlgorithmException; |
33 import java.security.NoSuchAlgorithmException; |
34 import java.security.SignatureException; |
34 import java.security.SignatureException; |
|
35 import java.security.Timestamp; |
35 import java.security.cert.CertPath; |
36 import java.security.cert.CertPath; |
36 import java.security.cert.X509Certificate; |
37 import java.security.cert.X509Certificate; |
37 import java.security.cert.CertificateException; |
38 import java.security.cert.CertificateException; |
38 import java.security.cert.CertificateFactory; |
39 import java.security.cert.CertificateFactory; |
39 import java.util.ArrayList; |
40 import java.util.ArrayList; |
40 import java.util.Base64; |
41 import java.util.Base64; |
41 import java.util.Collections; |
|
42 import java.util.EnumSet; |
|
43 import java.util.HashMap; |
42 import java.util.HashMap; |
44 import java.util.Hashtable; |
43 import java.util.Hashtable; |
45 import java.util.Iterator; |
44 import java.util.Iterator; |
46 import java.util.List; |
45 import java.util.List; |
47 import java.util.Locale; |
46 import java.util.Locale; |
48 import java.util.Map; |
47 import java.util.Map; |
49 import java.util.Set; |
|
50 import java.util.jar.Attributes; |
48 import java.util.jar.Attributes; |
51 import java.util.jar.JarException; |
49 import java.util.jar.JarException; |
52 import java.util.jar.JarFile; |
50 import java.util.jar.JarFile; |
53 import java.util.jar.Manifest; |
51 import java.util.jar.Manifest; |
54 |
52 |
343 // MANIFEST.MF is always regarded as signed |
360 // MANIFEST.MF is always regarded as signed |
344 updateSigners(newSigners, signers, JarFile.MANIFEST_NAME); |
361 updateSigners(newSigners, signers, JarFile.MANIFEST_NAME); |
345 } |
362 } |
346 |
363 |
347 /** |
364 /** |
|
365 * Check if algorithm is permitted using the permittedAlgs Map. |
|
366 * If the algorithm is not in the map, check against disabled algorithms and |
|
367 * store the result. If the algorithm is in the map use that result. |
|
368 * False is returned for weak algorithm, true for good algorithms. |
|
369 */ |
|
370 boolean permittedCheck(String key, String algorithm) { |
|
371 Boolean permitted = permittedAlgs.get(algorithm); |
|
372 if (permitted == null) { |
|
373 try { |
|
374 JAR_DISABLED_CHECK.permits(algorithm, |
|
375 new ConstraintsParameters(timestamp)); |
|
376 } catch(GeneralSecurityException e) { |
|
377 permittedAlgs.put(algorithm, Boolean.FALSE); |
|
378 permittedAlgs.put(key.toUpperCase(), Boolean.FALSE); |
|
379 if (debug != null) { |
|
380 if (e.getMessage() != null) { |
|
381 debug.println(key + ": " + e.getMessage()); |
|
382 } else { |
|
383 debug.println(key + ": " + algorithm + |
|
384 " was disabled, no exception msg given."); |
|
385 e.printStackTrace(); |
|
386 } |
|
387 } |
|
388 return false; |
|
389 } |
|
390 |
|
391 permittedAlgs.put(algorithm, Boolean.TRUE); |
|
392 return true; |
|
393 } |
|
394 |
|
395 // Algorithm has already been checked, return the value from map. |
|
396 return permitted.booleanValue(); |
|
397 } |
|
398 |
|
399 /** |
|
400 * With a given header (*-DIGEST*), return a string that lists all the |
|
401 * algorithms associated with the header. |
|
402 * If there are none, return "Unknown Algorithm". |
|
403 */ |
|
404 String getWeakAlgorithms(String header) { |
|
405 String w = ""; |
|
406 try { |
|
407 for (String key : permittedAlgs.keySet()) { |
|
408 if (key.endsWith(header)) { |
|
409 w += key.substring(0, key.length() - header.length()) + " "; |
|
410 } |
|
411 } |
|
412 } catch (RuntimeException e) { |
|
413 w = "Unknown Algorithm(s). Error processing " + header + ". " + |
|
414 e.getMessage(); |
|
415 } |
|
416 |
|
417 // This means we have an error in finding weak algorithms, run in |
|
418 // debug mode to see permittedAlgs map's values. |
|
419 if (w.length() == 0) { |
|
420 return "Unknown Algorithm(s)"; |
|
421 } |
|
422 |
|
423 return w; |
|
424 } |
|
425 |
|
426 /** |
348 * See if the whole manifest was signed. |
427 * See if the whole manifest was signed. |
349 */ |
428 */ |
350 private boolean verifyManifestHash(Manifest sf, |
429 private boolean verifyManifestHash(Manifest sf, |
351 ManifestDigester md, |
430 ManifestDigester md, |
352 List<Object> manifestDigests) |
431 List<Object> manifestDigests) |
353 throws IOException, SignatureException |
432 throws IOException, SignatureException |
354 { |
433 { |
355 Attributes mattr = sf.getMainAttributes(); |
434 Attributes mattr = sf.getMainAttributes(); |
356 boolean manifestSigned = false; |
435 boolean manifestSigned = false; |
|
436 boolean weakAlgs = true; |
357 |
437 |
358 // go through all the attributes and process *-Digest-Manifest entries |
438 // go through all the attributes and process *-Digest-Manifest entries |
359 for (Map.Entry<Object,Object> se : mattr.entrySet()) { |
439 for (Map.Entry<Object,Object> se : mattr.entrySet()) { |
360 |
440 |
361 String key = se.getKey().toString(); |
441 String key = se.getKey().toString(); |
362 |
442 |
363 if (key.toUpperCase(Locale.ENGLISH).endsWith("-DIGEST-MANIFEST")) { |
443 if (key.toUpperCase(Locale.ENGLISH).endsWith("-DIGEST-MANIFEST")) { |
364 // 16 is length of "-Digest-Manifest" |
444 // 16 is length of "-Digest-Manifest" |
365 String algorithm = key.substring(0, key.length()-16); |
445 String algorithm = key.substring(0, key.length()-16); |
|
446 |
|
447 // Check if this algorithm is permitted, skip if false. |
|
448 if (!permittedCheck(key, algorithm)) { |
|
449 continue; |
|
450 } |
|
451 |
|
452 // A non-weak algorithm was used, any weak algorithms found do |
|
453 // not need to be reported. |
|
454 weakAlgs = false; |
366 |
455 |
367 manifestDigests.add(key); |
456 manifestDigests.add(key); |
368 manifestDigests.add(se.getValue()); |
457 manifestDigests.add(se.getValue()); |
369 MessageDigest digest = getDigest(algorithm); |
458 MessageDigest digest = getDigest(algorithm); |
370 if (digest != null) { |
459 if (digest != null) { |
371 byte[] computedHash = md.manifestDigest(digest); |
460 byte[] computedHash = md.manifestDigest(digest); |
372 byte[] expectedHash = |
461 byte[] expectedHash = |
373 Base64.getMimeDecoder().decode((String)se.getValue()); |
462 Base64.getMimeDecoder().decode((String)se.getValue()); |
374 |
463 |
375 if (debug != null) { |
464 if (debug != null) { |
376 debug.println("Signature File: Manifest digest " + |
465 debug.println("Signature File: Manifest digest " + |
377 digest.getAlgorithm()); |
466 algorithm); |
378 debug.println( " sigfile " + toHex(expectedHash)); |
467 debug.println( " sigfile " + toHex(expectedHash)); |
379 debug.println( " computed " + toHex(computedHash)); |
468 debug.println( " computed " + toHex(computedHash)); |
380 debug.println(); |
469 debug.println(); |
381 } |
470 } |
382 |
471 |
383 if (MessageDigest.isEqual(computedHash, |
472 if (MessageDigest.isEqual(computedHash, expectedHash)) { |
384 expectedHash)) { |
|
385 manifestSigned = true; |
473 manifestSigned = true; |
386 } else { |
474 } else { |
387 //XXX: we will continue and verify each section |
475 //XXX: we will continue and verify each section |
388 } |
476 } |
389 } |
477 } |
390 } |
478 } |
391 } |
479 } |
|
480 |
|
481 if (debug != null) { |
|
482 debug.println("PermittedAlgs mapping: "); |
|
483 for (String key : permittedAlgs.keySet()) { |
|
484 debug.println(key + " : " + |
|
485 permittedAlgs.get(key).toString()); |
|
486 } |
|
487 } |
|
488 |
|
489 // If there were only weak algorithms used, throw an exception. |
|
490 if (weakAlgs) { |
|
491 String weakAlgorithms = getWeakAlgorithms("-DIGEST-MANIFEST"); |
|
492 throw new SignatureException("Manifest hash check failed " + |
|
493 "(DIGEST-MANIFEST). Disabled algorithm(s) used: " + |
|
494 weakAlgorithms); |
|
495 } |
392 return manifestSigned; |
496 return manifestSigned; |
393 } |
497 } |
394 |
498 |
395 private boolean verifyManifestMainAttrs(Manifest sf, |
499 private boolean verifyManifestMainAttrs(Manifest sf, ManifestDigester md) |
396 ManifestDigester md) |
|
397 throws IOException, SignatureException |
500 throws IOException, SignatureException |
398 { |
501 { |
399 Attributes mattr = sf.getMainAttributes(); |
502 Attributes mattr = sf.getMainAttributes(); |
400 boolean attrsVerified = true; |
503 boolean attrsVerified = true; |
|
504 boolean weakAlgs = true; |
401 |
505 |
402 // go through all the attributes and process |
506 // go through all the attributes and process |
403 // digest entries for the manifest main attributes |
507 // digest entries for the manifest main attributes |
404 for (Map.Entry<Object,Object> se : mattr.entrySet()) { |
508 for (Map.Entry<Object,Object> se : mattr.entrySet()) { |
405 String key = se.getKey().toString(); |
509 String key = se.getKey().toString(); |
406 |
510 |
407 if (key.toUpperCase(Locale.ENGLISH).endsWith(ATTR_DIGEST)) { |
511 if (key.toUpperCase(Locale.ENGLISH).endsWith(ATTR_DIGEST)) { |
408 String algorithm = |
512 String algorithm = |
409 key.substring(0, key.length() - ATTR_DIGEST.length()); |
513 key.substring(0, key.length() - ATTR_DIGEST.length()); |
|
514 |
|
515 // Check if this algorithm is permitted, skip if false. |
|
516 if (!permittedCheck(key, algorithm)) { |
|
517 continue; |
|
518 } |
|
519 |
|
520 // A non-weak algorithm was used, any weak algorithms found do |
|
521 // not need to be reported. |
|
522 weakAlgs = false; |
410 |
523 |
411 MessageDigest digest = getDigest(algorithm); |
524 MessageDigest digest = getDigest(algorithm); |
412 if (digest != null) { |
525 if (digest != null) { |
413 ManifestDigester.Entry mde = |
526 ManifestDigester.Entry mde = |
414 md.get(ManifestDigester.MF_MAIN_ATTRS, false); |
527 md.get(ManifestDigester.MF_MAIN_ATTRS, false); |
462 ManifestDigester md) |
591 ManifestDigester md) |
463 throws IOException, SignatureException |
592 throws IOException, SignatureException |
464 { |
593 { |
465 boolean oneDigestVerified = false; |
594 boolean oneDigestVerified = false; |
466 ManifestDigester.Entry mde = md.get(name,block.isOldStyle()); |
595 ManifestDigester.Entry mde = md.get(name,block.isOldStyle()); |
|
596 boolean weakAlgs = true; |
467 |
597 |
468 if (mde == null) { |
598 if (mde == null) { |
469 throw new SecurityException( |
599 throw new SecurityException( |
470 "no manifest section for signature file entry "+name); |
600 "no manifest section for signature file entry "+name); |
471 } |
601 } |
472 |
602 |
473 if (sfAttr != null) { |
603 if (sfAttr != null) { |
474 |
|
475 //sun.security.util.HexDumpEncoder hex = new sun.security.util.HexDumpEncoder(); |
604 //sun.security.util.HexDumpEncoder hex = new sun.security.util.HexDumpEncoder(); |
476 //hex.encodeBuffer(data, System.out); |
605 //hex.encodeBuffer(data, System.out); |
477 |
606 |
478 // go through all the attributes and process *-Digest entries |
607 // go through all the attributes and process *-Digest entries |
479 for (Map.Entry<Object,Object> se : sfAttr.entrySet()) { |
608 for (Map.Entry<Object,Object> se : sfAttr.entrySet()) { |
480 String key = se.getKey().toString(); |
609 String key = se.getKey().toString(); |
481 |
610 |
482 if (key.toUpperCase(Locale.ENGLISH).endsWith("-DIGEST")) { |
611 if (key.toUpperCase(Locale.ENGLISH).endsWith("-DIGEST")) { |
483 // 7 is length of "-Digest" |
612 // 7 is length of "-Digest" |
484 String algorithm = key.substring(0, key.length()-7); |
613 String algorithm = key.substring(0, key.length()-7); |
|
614 |
|
615 // Check if this algorithm is permitted, skip if false. |
|
616 if (!permittedCheck(key, algorithm)) { |
|
617 continue; |
|
618 } |
|
619 |
|
620 // A non-weak algorithm was used, any weak algorithms found do |
|
621 // not need to be reported. |
|
622 weakAlgs = false; |
485 |
623 |
486 MessageDigest digest = getDigest(algorithm); |
624 MessageDigest digest = getDigest(algorithm); |
487 |
625 |
488 if (digest != null) { |
626 if (digest != null) { |
489 boolean ok = false; |
627 boolean ok = false; |