JarFileTest.java revision a3d40b4064c0f4de77122eff9253da242877c938
1/* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17package org.apache.harmony.tests.java.util.jar; 18 19 20import java.io.ByteArrayOutputStream; 21import java.io.File; 22import java.io.FileOutputStream; 23import java.io.IOException; 24import java.io.InputStream; 25import java.net.URL; 26import java.security.CodeSigner; 27import java.security.Permission; 28import java.security.cert.Certificate; 29import java.util.Arrays; 30import java.util.Enumeration; 31import java.util.Vector; 32import java.util.jar.Attributes; 33import java.util.jar.JarEntry; 34import java.util.jar.JarFile; 35import java.util.jar.JarOutputStream; 36import java.util.jar.Manifest; 37import java.util.zip.ZipEntry; 38import java.util.zip.ZipException; 39import java.util.zip.ZipFile; 40import junit.framework.TestCase; 41import tests.support.resource.Support_Resources; 42 43 44public class JarFileTest extends TestCase { 45 46 // BEGIN android-added 47 public byte[] getAllBytesFromStream(InputStream is) throws IOException { 48 ByteArrayOutputStream bs = new ByteArrayOutputStream(); 49 byte[] buf = new byte[666]; 50 int iRead; 51 int off; 52 while (is.available() > 0) { 53 iRead = is.read(buf, 0, buf.length); 54 if (iRead > 0) bs.write(buf, 0, iRead); 55 } 56 return bs.toByteArray(); 57 } 58 59 // END android-added 60 61 private final String jarName = "hyts_patch.jar"; // a 'normal' jar file 62 63 private final String jarName2 = "hyts_patch2.jar"; 64 65 private final String jarName3 = "hyts_manifest1.jar"; 66 67 private final String jarName4 = "hyts_signed.jar"; 68 69 private final String jarName5 = "hyts_signed_inc.jar"; 70 71 private final String jarName6 = "hyts_signed_sha256withrsa.jar"; 72 73 private final String jarName7 = "hyts_signed_sha256digest_sha256withrsa.jar"; 74 75 private final String jarName8 = "hyts_signed_sha512digest_sha512withecdsa.jar"; 76 77 private final String jarName9 = "hyts_signed_sha256digest_sha256withecdsa.jar"; 78 79 private final String authAttrsJar = "hyts_signed_authAttrs.jar"; 80 81 private final String entryName = "foo/bar/A.class"; 82 83 private final String entryName3 = "coucou/FileAccess.class"; 84 85 private final String integrateJar = "Integrate.jar"; 86 87 private final String integrateJarEntry = "Test.class"; 88 89 private final String emptyEntryJar = "EmptyEntries_signed.jar"; 90 91 private final String emptyEntry1 = "subfolder/internalSubset01.js"; 92 93 private final String emptyEntry2 = "svgtest.js"; 94 95 private final String emptyEntry3 = "svgunit.js"; 96 97 private static final String VALID_CHAIN_JAR = "hyts_signed_validChain.jar"; 98 99 private static final String INVALID_CHAIN_JAR = "hyts_signed_invalidChain.jar"; 100 101 private static final String AMBIGUOUS_SIGNERS_JAR = "hyts_signed_ambiguousSignerArray.jar"; 102 103 private File resources; 104 105 // custom security manager 106 SecurityManager sm = new SecurityManager() { 107 final String forbidenPermissionName = "user.dir"; 108 109 public void checkPermission(Permission perm) { 110 if (perm.getName().equals(forbidenPermissionName)) { 111 throw new SecurityException(); 112 } 113 } 114 }; 115 116 @Override 117 protected void setUp() { 118 resources = Support_Resources.createTempFolder(); 119 } 120 121 /** 122 * java.util.jar.JarFile#JarFile(java.io.File) 123 */ 124 public void test_ConstructorLjava_io_File() { 125 try { 126 JarFile jarFile = new JarFile(new File("Wrong.file")); 127 fail("Should throw IOException"); 128 } catch (IOException e) { 129 // expected 130 } 131 132 try { 133 Support_Resources.copyFile(resources, null, jarName); 134 JarFile jarFile = new JarFile(new File(resources, jarName)); 135 } catch (IOException e) { 136 fail("Should not throw IOException"); 137 } 138 } 139 140 /** 141 * java.util.jar.JarFile#JarFile(java.lang.String) 142 */ 143 public void test_ConstructorLjava_lang_String() { 144 try { 145 JarFile jarFile = new JarFile("Wrong.file"); 146 fail("Should throw IOException"); 147 } catch (IOException e) { 148 // expected 149 } 150 151 try { 152 Support_Resources.copyFile(resources, null, jarName); 153 String fileName = (new File(resources, jarName)).getCanonicalPath(); 154 JarFile jarFile = new JarFile(fileName); 155 } catch (IOException e) { 156 fail("Should not throw IOException"); 157 } 158 } 159 160 /** 161 * java.util.jar.JarFile#JarFile(java.lang.String, boolean) 162 */ 163 public void test_ConstructorLjava_lang_StringZ() { 164 try { 165 JarFile jarFile = new JarFile("Wrong.file", false); 166 fail("Should throw IOException"); 167 } catch (IOException e) { 168 // expected 169 } 170 171 try { 172 Support_Resources.copyFile(resources, null, jarName); 173 String fileName = (new File(resources, jarName)).getCanonicalPath(); 174 JarFile jarFile = new JarFile(fileName, true); 175 } catch (IOException e) { 176 fail("Should not throw IOException"); 177 } 178 } 179 180 /** 181 * java.util.jar.JarFile#JarFile(java.io.File, boolean) 182 */ 183 public void test_ConstructorLjava_io_FileZ() { 184 try { 185 JarFile jarFile = new JarFile(new File("Wrong.file"), true); 186 fail("Should throw IOException"); 187 } catch (IOException e) { 188 // expected 189 } 190 191 try { 192 Support_Resources.copyFile(resources, null, jarName); 193 JarFile jarFile = new JarFile(new File(resources, jarName), false); 194 } catch (IOException e) { 195 fail("Should not throw IOException"); 196 } 197 } 198 199 /** 200 * java.util.jar.JarFile#JarFile(java.io.File, boolean, int) 201 */ 202 public void test_ConstructorLjava_io_FileZI() { 203 try { 204 JarFile jarFile = new JarFile(new File("Wrong.file"), true, 205 ZipFile.OPEN_READ); 206 fail("Should throw IOException"); 207 } catch (IOException e) { 208 // expected 209 } 210 211 try { 212 Support_Resources.copyFile(resources, null, jarName); 213 JarFile jarFile = new JarFile(new File(resources, jarName), false, 214 ZipFile.OPEN_READ); 215 } catch (IOException e) { 216 fail("Should not throw IOException"); 217 } 218 219 try { 220 Support_Resources.copyFile(resources, null, jarName); 221 JarFile jarFile = new JarFile(new File(resources, jarName), false, 222 ZipFile.OPEN_READ | ZipFile.OPEN_DELETE + 33); 223 fail("Should throw IllegalArgumentException"); 224 } catch (IOException e) { 225 fail("Should not throw IOException"); 226 } catch (IllegalArgumentException e) { 227 // expected 228 } 229 } 230 231 /** 232 * Constructs JarFile object. 233 * 234 * java.util.jar.JarFile#JarFile(java.io.File) 235 * java.util.jar.JarFile#JarFile(java.lang.String) 236 */ 237 public void testConstructor_file() throws IOException { 238 File f = new File(resources, jarName); 239 Support_Resources.copyFile(resources, null, jarName); 240 assertTrue(new JarFile(f).getEntry(entryName).getName().equals( 241 entryName)); 242 assertTrue(new JarFile(f.getPath()).getEntry(entryName).getName() 243 .equals(entryName)); 244 } 245 246 /** 247 * java.util.jar.JarFile#entries() 248 */ 249 public void test_entries() throws Exception { 250 /* 251 * Note only (and all of) the following should be contained in the file 252 * META-INF/ META-INF/MANIFEST.MF foo/ foo/bar/ foo/bar/A.class Blah.txt 253 */ 254 Support_Resources.copyFile(resources, null, jarName); 255 JarFile jarFile = new JarFile(new File(resources, jarName)); 256 Enumeration<JarEntry> e = jarFile.entries(); 257 int i; 258 for (i = 0; e.hasMoreElements(); i++) { 259 e.nextElement(); 260 } 261 assertEquals(jarFile.size(), i); 262 jarFile.close(); 263 assertEquals(6, i); 264 } 265 266 public void test_entries2() throws Exception { 267 Support_Resources.copyFile(resources, null, jarName); 268 JarFile jarFile = new JarFile(new File(resources, jarName)); 269 Enumeration<JarEntry> enumeration = jarFile.entries(); 270 jarFile.close(); 271 try { 272 enumeration.hasMoreElements(); 273 fail("hasMoreElements() did not detect a closed jar file"); 274 } catch (IllegalStateException e) { 275 } 276 Support_Resources.copyFile(resources, null, jarName); 277 jarFile = new JarFile(new File(resources, jarName)); 278 enumeration = jarFile.entries(); 279 jarFile.close(); 280 try { 281 enumeration.nextElement(); 282 fail("nextElement() did not detect closed jar file"); 283 } catch (IllegalStateException e) { 284 } 285 } 286 287 /** 288 * @throws IOException 289 * java.util.jar.JarFile#getJarEntry(java.lang.String) 290 */ 291 public void test_getEntryLjava_lang_String() throws IOException { 292 try { 293 Support_Resources.copyFile(resources, null, jarName); 294 JarFile jarFile = new JarFile(new File(resources, jarName)); 295 assertEquals("Error in returned entry", 311, jarFile.getEntry( 296 entryName).getSize()); 297 jarFile.close(); 298 } catch (Exception e) { 299 fail("Exception during test: " + e.toString()); 300 } 301 302 Support_Resources.copyFile(resources, null, jarName); 303 JarFile jarFile = new JarFile(new File(resources, jarName)); 304 Enumeration<JarEntry> enumeration = jarFile.entries(); 305 assertTrue(enumeration.hasMoreElements()); 306 while (enumeration.hasMoreElements()) { 307 JarEntry je = enumeration.nextElement(); 308 jarFile.getEntry(je.getName()); 309 } 310 311 enumeration = jarFile.entries(); 312 assertTrue(enumeration.hasMoreElements()); 313 JarEntry je = enumeration.nextElement(); 314 try { 315 jarFile.close(); 316 jarFile.getEntry(je.getName()); 317 // fail("IllegalStateException expected."); 318 } catch (IllegalStateException ee) { // Per documentation exception 319 // may be thrown. 320 // expected 321 } 322 } 323 324 /** 325 * @throws IOException 326 * java.util.jar.JarFile#getJarEntry(java.lang.String) 327 */ 328 public void test_getJarEntryLjava_lang_String() throws IOException { 329 try { 330 Support_Resources.copyFile(resources, null, jarName); 331 JarFile jarFile = new JarFile(new File(resources, jarName)); 332 assertEquals("Error in returned entry", 311, jarFile.getJarEntry( 333 entryName).getSize()); 334 jarFile.close(); 335 } catch (Exception e) { 336 fail("Exception during test: " + e.toString()); 337 } 338 339 Support_Resources.copyFile(resources, null, jarName); 340 JarFile jarFile = new JarFile(new File(resources, jarName)); 341 Enumeration<JarEntry> enumeration = jarFile.entries(); 342 assertTrue(enumeration.hasMoreElements()); 343 while (enumeration.hasMoreElements()) { 344 JarEntry je = enumeration.nextElement(); 345 jarFile.getJarEntry(je.getName()); 346 } 347 348 enumeration = jarFile.entries(); 349 assertTrue(enumeration.hasMoreElements()); 350 JarEntry je = enumeration.nextElement(); 351 try { 352 jarFile.close(); 353 jarFile.getJarEntry(je.getName()); 354 // fail("IllegalStateException expected."); 355 } catch (IllegalStateException ee) { // Per documentation exception 356 // may be thrown. 357 // expected 358 } 359 } 360 361 362 /** 363 * java.util.jar.JarFile#getJarEntry(java.lang.String) 364 */ 365 public void testGetJarEntry() throws Exception { 366 Support_Resources.copyFile(resources, null, jarName); 367 JarFile jarFile = new JarFile(new File(resources, jarName)); 368 assertEquals("Error in returned entry", 311, jarFile.getEntry( 369 entryName).getSize()); 370 jarFile.close(); 371 372 // tests for signed jars 373 // test all signed jars in the /Testres/Internal/SignedJars directory 374 String jarDirUrl = Support_Resources 375 .getResourceURL("/../internalres/signedjars"); 376 Vector<String> signedJars = new Vector<String>(); 377 try { 378 InputStream is = new URL(jarDirUrl + "/jarlist.txt").openStream(); 379 while (is.available() > 0) { 380 StringBuilder linebuff = new StringBuilder(80); // Typical line 381 // length 382 done: while (true) { 383 int nextByte = is.read(); 384 switch (nextByte) { 385 case -1: 386 break done; 387 case (byte) '\r': 388 if (linebuff.length() == 0) { 389 // ignore 390 } 391 break done; 392 case (byte) '\n': 393 if (linebuff.length() == 0) { 394 // ignore 395 } 396 break done; 397 default: 398 linebuff.append((char) nextByte); 399 } 400 } 401 if (linebuff.length() == 0) { 402 break; 403 } 404 String line = linebuff.toString(); 405 signedJars.add(line); 406 } 407 is.close(); 408 } catch (IOException e) { 409 // no list of jars found 410 } 411 412 for (int i = 0; i < signedJars.size(); i++) { 413 String jarName = signedJars.get(i); 414 try { 415 File file = Support_Resources.getExternalLocalFile(jarDirUrl 416 + "/" + jarName); 417 jarFile = new JarFile(file, true); 418 boolean foundCerts = false; 419 Enumeration<JarEntry> e = jarFile.entries(); 420 while (e.hasMoreElements()) { 421 JarEntry entry = e.nextElement(); 422 InputStream is = jarFile.getInputStream(entry); 423 is.skip(100000); 424 is.close(); 425 Certificate[] certs = entry.getCertificates(); 426 if (certs != null && certs.length > 0) { 427 foundCerts = true; 428 break; 429 } 430 } 431 assertTrue( 432 "No certificates found during signed jar test for jar \"" 433 + jarName + "\"", foundCerts); 434 } catch (IOException e) { 435 fail("Exception during signed jar test for jar \"" + jarName 436 + "\": " + e.toString()); 437 } 438 } 439 } 440 441 /** 442 * java.util.jar.JarFile#getManifest() 443 */ 444 public void test_getManifest() { 445 // Test for method java.util.jar.Manifest 446 // java.util.jar.JarFile.getManifest() 447 try { 448 Support_Resources.copyFile(resources, null, jarName); 449 JarFile jarFile = new JarFile(new File(resources, jarName)); 450 assertNotNull("Error--Manifest not returned", jarFile.getManifest()); 451 jarFile.close(); 452 } catch (Exception e) { 453 fail("Exception during 1st test: " + e.toString()); 454 } 455 try { 456 Support_Resources.copyFile(resources, null, jarName2); 457 JarFile jarFile = new JarFile(new File(resources, jarName2)); 458 assertNull("Error--should have returned null", jarFile 459 .getManifest()); 460 jarFile.close(); 461 } catch (Exception e) { 462 fail("Exception during 2nd test: " + e.toString()); 463 } 464 465 try { 466 // jarName3 was created using the following test 467 Support_Resources.copyFile(resources, null, jarName3); 468 JarFile jarFile = new JarFile(new File(resources, jarName3)); 469 assertNotNull("Should find manifest without verifying", jarFile 470 .getManifest()); 471 jarFile.close(); 472 } catch (Exception e) { 473 fail("Exception during 3rd test: " + e.toString()); 474 } 475 476 try { 477 // this is used to create jarName3 used in the previous test 478 Manifest manifest = new Manifest(); 479 Attributes attributes = manifest.getMainAttributes(); 480 attributes.put(new Attributes.Name("Manifest-Version"), "1.0"); 481 ByteArrayOutputStream manOut = new ByteArrayOutputStream(); 482 manifest.write(manOut); 483 byte[] manBytes = manOut.toByteArray(); 484 File file = File.createTempFile("hyts_manifest1", ".jar"); 485 JarOutputStream jarOut = new JarOutputStream(new FileOutputStream( 486 file.getAbsolutePath())); 487 ZipEntry entry = new ZipEntry("META-INF/"); 488 entry.setSize(0); 489 jarOut.putNextEntry(entry); 490 entry = new ZipEntry(JarFile.MANIFEST_NAME); 491 entry.setSize(manBytes.length); 492 jarOut.putNextEntry(entry); 493 jarOut.write(manBytes); 494 entry = new ZipEntry("myfile"); 495 entry.setSize(1); 496 jarOut.putNextEntry(entry); 497 jarOut.write(65); 498 jarOut.close(); 499 JarFile jar = new JarFile(file.getAbsolutePath(), false); 500 assertNotNull("Should find manifest without verifying", jar 501 .getManifest()); 502 jar.close(); 503 file.delete(); 504 } catch (IOException e) { 505 fail("IOException 3"); 506 } 507 try { 508 Support_Resources.copyFile(resources, null, jarName2); 509 JarFile jF = new JarFile(new File(resources, jarName2)); 510 jF.close(); 511 jF.getManifest(); 512 fail("FAILED: expected IllegalStateException"); 513 } catch (IllegalStateException ise) { 514 // expected; 515 } catch (Exception e) { 516 fail("Exception during 4th test: " + e.toString()); 517 } 518 519 Support_Resources.copyFile(resources, null, "Broken_manifest.jar"); 520 JarFile jf; 521 try { 522 jf = new JarFile(new File(resources, "Broken_manifest.jar")); 523 jf.getManifest(); 524 fail("IOException expected."); 525 } catch (IOException e) { 526 // expected. 527 } 528 } 529 530 /** 531 * java.util.jar.JarFile#getInputStream(java.util.zip.ZipEntry) 532 */ 533 // This test doesn't pass on RI. If entry size is set up incorrectly, 534 // SecurityException is thrown. But SecurityException is thrown on RI only 535 // if jar file is signed incorrectly. 536 public void test_getInputStreamLjava_util_jar_JarEntry_subtest0() throws Exception { 537 File signedFile = null; 538 try { 539 Support_Resources.copyFile(resources, null, jarName4); 540 signedFile = new File(resources, jarName4); 541 } catch (Exception e) { 542 fail("Failed to create local file 2: " + e); 543 } 544 545 try { 546 JarFile jar = new JarFile(signedFile); 547 JarEntry entry = new JarEntry(entryName3); 548 InputStream in = jar.getInputStream(entry); 549 in.read(); 550 } catch (Exception e) { 551 fail("Exception during test 3: " + e); 552 } 553 554 try { 555 JarFile jar = new JarFile(signedFile); 556 JarEntry entry = new JarEntry(entryName3); 557 InputStream in = jar.getInputStream(entry); 558 // BEGIN android-added 559 byte[] dummy = getAllBytesFromStream(in); 560 // END android-added 561 assertNull("found certificates", entry.getCertificates()); 562 } catch (Exception e) { 563 fail("Exception during test 4: " + e); 564 } 565 566 try { 567 JarFile jar = new JarFile(signedFile); 568 JarEntry entry = new JarEntry(entryName3); 569 entry.setSize(1076); 570 InputStream in = jar.getInputStream(entry); 571 // BEGIN android-added 572 byte[] dummy = getAllBytesFromStream(in); 573 // END android-added 574 fail("SecurityException should be thrown."); 575 } catch (SecurityException e) { 576 // expected 577 } catch (Exception e) { 578 fail("Exception during test 5: " + e); 579 } 580 581 try { 582 Support_Resources.copyFile(resources, null, jarName5); 583 signedFile = new File(resources, jarName5); 584 } catch (Exception e) { 585 fail("Failed to create local file 5: " + e); 586 } 587 588 try { 589 JarFile jar = new JarFile(signedFile); 590 JarEntry entry = new JarEntry(entryName3); 591 InputStream in = jar.getInputStream(entry); 592 fail("SecurityException should be thrown."); 593 } catch (SecurityException e) { 594 // expected 595 } catch (Exception e) { 596 fail("Exception during test 5: " + e); 597 } 598 599 // SHA1 digest, SHA256withRSA signed JAR 600 checkSignedJar(jarName6); 601 602 // SHA-256 digest, SHA256withRSA signed JAR 603 checkSignedJar(jarName7); 604 605 // SHA-512 digest, SHA512withECDSA signed JAR 606 checkSignedJar(jarName8); 607 608 // JAR with a signature that has PKCS#7 Authenticated Attributes 609 checkSignedJar(authAttrsJar); 610 } 611 612 /** 613 * This test uses a jar file signed with an algorithm that has its own OID 614 * that is valid as a signature type. SHA256withECDSA is an algorithm that 615 * isn't combined as DigestAlgorithm + "with" + DigestEncryptionAlgorithm 616 * like RSAEncryption needs to be. 617 */ 618 public void testJarFile_Signed_Valid_DigestEncryptionAlgorithm() throws Exception { 619 checkSignedJar(jarName9); 620 } 621 622 private void checkSignedJar(String jarName) throws Exception { 623 Support_Resources.copyFile(resources, null, jarName); 624 625 File file = new File(resources, jarName); 626 boolean foundCerts = false; 627 628 JarFile jarFile = new JarFile(file, true); 629 try { 630 631 Enumeration<JarEntry> e = jarFile.entries(); 632 while (e.hasMoreElements()) { 633 JarEntry entry = e.nextElement(); 634 InputStream is = jarFile.getInputStream(entry); 635 is.skip(100000); 636 is.close(); 637 Certificate[] certs = entry.getCertificates(); 638 if (certs != null && certs.length > 0) { 639 foundCerts = true; 640 break; 641 } 642 } 643 } finally { 644 jarFile.close(); 645 } 646 647 assertTrue( 648 "No certificates found during signed jar test for jar \"" 649 + jarName + "\"", foundCerts); 650 } 651 652 private static class Results { 653 public Certificate[] certificates; 654 public CodeSigner[] signers; 655 } 656 657 private Results getSignedJarCerts(String jarName, boolean chainCheck) throws Exception { 658 Support_Resources.copyFile(resources, null, jarName); 659 660 File file = new File(resources, jarName); 661 Results results = new Results(); 662 663 JarFile jarFile = new JarFile(file, true, ZipFile.OPEN_READ, chainCheck); 664 try { 665 666 Enumeration<JarEntry> e = jarFile.entries(); 667 while (e.hasMoreElements()) { 668 JarEntry entry = e.nextElement(); 669 InputStream is = jarFile.getInputStream(entry); 670 // Skip bytes because we have to read the entire file for it to read signatures. 671 is.skip(entry.getSize()); 672 is.close(); 673 Certificate[] certs = entry.getCertificates(); 674 CodeSigner[] signers = entry.getCodeSigners(); 675 if (certs != null && certs.length > 0) { 676 results.certificates = certs; 677 results.signers = signers; 678 break; 679 } 680 } 681 } finally { 682 jarFile.close(); 683 } 684 685 return results; 686 } 687 688 public void testJarFile_Signed_ValidChain_NoCheck() throws Exception { 689 Results result = getSignedJarCerts(VALID_CHAIN_JAR, false); 690 assertNotNull(result); 691 assertEquals(Arrays.deepToString(result.certificates), 2, result.certificates.length); 692 assertEquals(Arrays.deepToString(result.signers), 1, result.signers.length); 693 assertEquals(2, result.signers[0].getSignerCertPath().getCertificates().size()); 694 } 695 696 public void testJarFile_Signed_ValidChain_Check() throws Exception { 697 Results result = getSignedJarCerts(VALID_CHAIN_JAR, true); 698 assertNotNull(result); 699 assertEquals(Arrays.deepToString(result.certificates), 2, result.certificates.length); 700 assertEquals(Arrays.deepToString(result.signers), 1, result.signers.length); 701 assertEquals(2, result.signers[0].getSignerCertPath().getCertificates().size()); 702 } 703 704 public void testJarFile_Signed_InvalidChain_NoCheck() throws Exception { 705 Results result = getSignedJarCerts(INVALID_CHAIN_JAR, false); 706 assertNotNull(result); 707 assertEquals(Arrays.deepToString(result.certificates), 2, result.certificates.length); 708 assertEquals(Arrays.deepToString(result.signers), 1, result.signers.length); 709 assertEquals(2, result.signers[0].getSignerCertPath().getCertificates().size()); 710 } 711 712 public void testJarFile_Signed_InvalidChain_Check() throws Exception { 713 Results result = getSignedJarCerts(INVALID_CHAIN_JAR, true); 714 assertNotNull(result); 715 assertEquals(Arrays.deepToString(result.certificates), 1, result.certificates.length); 716 assertEquals(Arrays.deepToString(result.signers), 1, result.signers.length); 717 assertEquals(1, result.signers[0].getSignerCertPath().getCertificates().size()); 718 } 719 720 public void testJarFile_Signed_AmbiguousSigners() throws Exception { 721 Results result = getSignedJarCerts(AMBIGUOUS_SIGNERS_JAR, false); 722 assertNotNull(result); 723 assertEquals(Arrays.deepToString(result.certificates), 2, result.certificates.length); 724 assertEquals(Arrays.deepToString(result.signers), 2, result.signers.length); 725 assertEquals(1, result.signers[0].getSignerCertPath().getCertificates().size()); 726 assertEquals(1, result.signers[1].getSignerCertPath().getCertificates().size()); 727 } 728 729 /* 730 * The jar created by 1.4 which does not provide a 731 * algorithm-Digest-Manifest-Main-Attributes entry in .SF file. 732 */ 733 public void test_Jar_created_before_java_5() throws IOException { 734 String modifiedJarName = "Created_by_1_4.jar"; 735 Support_Resources.copyFile(resources, null, modifiedJarName); 736 JarFile jarFile = new JarFile(new File(resources, modifiedJarName), 737 true); 738 Enumeration<JarEntry> entries = jarFile.entries(); 739 while (entries.hasMoreElements()) { 740 ZipEntry zipEntry = entries.nextElement(); 741 jarFile.getInputStream(zipEntry); 742 } 743 } 744 745 /* The jar is intact, then everything is all right. */ 746 public void test_JarFile_Integrate_Jar() throws IOException { 747 String modifiedJarName = "Integrate.jar"; 748 Support_Resources.copyFile(resources, null, modifiedJarName); 749 JarFile jarFile = new JarFile(new File(resources, modifiedJarName), 750 true); 751 Enumeration<JarEntry> entries = jarFile.entries(); 752 while (entries.hasMoreElements()) { 753 ZipEntry zipEntry = entries.nextElement(); 754 jarFile.getInputStream(zipEntry).skip(Long.MAX_VALUE); 755 } 756 } 757 758 /** 759 * The jar is intact, but the entry object is modified. 760 */ 761 public void testJarVerificationModifiedEntry() throws IOException { 762 Support_Resources.copyFile(resources, null, integrateJar); 763 File f = new File(resources, integrateJar); 764 765 JarFile jarFile = new JarFile(f); 766 ZipEntry zipEntry = jarFile.getJarEntry(integrateJarEntry); 767 zipEntry.setSize(zipEntry.getSize() + 1); 768 jarFile.getInputStream(zipEntry).skip(Long.MAX_VALUE); 769 770 jarFile = new JarFile(f); 771 zipEntry = jarFile.getJarEntry(integrateJarEntry); 772 zipEntry.setSize(zipEntry.getSize() - 1); 773 try { 774 //jarFile.getInputStream(zipEntry).skip(Long.MAX_VALUE); 775 jarFile.getInputStream(zipEntry).read(new byte[5000], 0, 5000); 776 fail("SecurityException expected"); 777 } catch (SecurityException e) { 778 // desired 779 } 780 } 781 782 /* 783 * If another entry is inserted into Manifest, no security exception will be 784 * thrown out. 785 */ 786 public void test_JarFile_InsertEntry_in_Manifest_Jar() throws IOException { 787 String modifiedJarName = "Inserted_Entry_Manifest.jar"; 788 Support_Resources.copyFile(resources, null, modifiedJarName); 789 JarFile jarFile = new JarFile(new File(resources, modifiedJarName), 790 true); 791 Enumeration<JarEntry> entries = jarFile.entries(); 792 int count = 0; 793 while (entries.hasMoreElements()) { 794 795 ZipEntry zipEntry = entries.nextElement(); 796 jarFile.getInputStream(zipEntry); 797 count++; 798 } 799 assertEquals(5, count); 800 } 801 802 /* 803 * If another entry is inserted into Manifest, no security exception will be 804 * thrown out. 805 */ 806 public void test_Inserted_Entry_Manifest_with_DigestCode() 807 throws IOException { 808 String modifiedJarName = "Inserted_Entry_Manifest_with_DigestCode.jar"; 809 Support_Resources.copyFile(resources, null, modifiedJarName); 810 JarFile jarFile = new JarFile(new File(resources, modifiedJarName), 811 true); 812 Enumeration<JarEntry> entries = jarFile.entries(); 813 int count = 0; 814 while (entries.hasMoreElements()) { 815 ZipEntry zipEntry = entries.nextElement(); 816 jarFile.getInputStream(zipEntry); 817 count++; 818 } 819 assertEquals(5, count); 820 } 821 822 /* 823 * The content of Test.class is modified, jarFile.getInputStream will not 824 * throw security Exception, but it will anytime before the inputStream got 825 * from getInputStream method has been read to end. 826 */ 827 public void test_JarFile_Modified_Class() throws IOException { 828 String modifiedJarName = "Modified_Class.jar"; 829 Support_Resources.copyFile(resources, null, modifiedJarName); 830 JarFile jarFile = new JarFile(new File(resources, modifiedJarName), 831 true); 832 Enumeration<JarEntry> entries = jarFile.entries(); 833 while (entries.hasMoreElements()) { 834 ZipEntry zipEntry = entries.nextElement(); 835 jarFile.getInputStream(zipEntry); 836 } 837 /* The content of Test.class has been tampered. */ 838 ZipEntry zipEntry = jarFile.getEntry("Test.class"); 839 InputStream in = jarFile.getInputStream(zipEntry); 840 byte[] buffer = new byte[1024]; 841 try { 842 while (in.available() > 0) { 843 in.read(buffer); 844 } 845 fail("SecurityException expected"); 846 } catch (SecurityException e) { 847 // desired 848 } 849 } 850 851 /* 852 * In the Modified.jar, the main attributes of META-INF/MANIFEST.MF is 853 * tampered manually. Hence the RI 5.0 JarFile.getInputStream of any 854 * JarEntry will throw security exception. 855 */ 856 public void test_JarFile_Modified_Manifest_MainAttributes() 857 throws IOException { 858 String modifiedJarName = "Modified_Manifest_MainAttributes.jar"; 859 Support_Resources.copyFile(resources, null, modifiedJarName); 860 JarFile jarFile = new JarFile(new File(resources, modifiedJarName), 861 true); 862 Enumeration<JarEntry> entries = jarFile.entries(); 863 while (entries.hasMoreElements()) { 864 ZipEntry zipEntry = entries.nextElement(); 865 try { 866 jarFile.getInputStream(zipEntry); 867 fail("SecurityException expected"); 868 } catch (SecurityException e) { 869 // desired 870 } 871 } 872 } 873 874 /* 875 * It is all right in our original JarFile. If the Entry Attributes, for 876 * example Test.class in our jar, the jarFile.getInputStream will throw 877 * Security Exception. 878 */ 879 public void test_JarFile_Modified_Manifest_EntryAttributes() 880 throws IOException { 881 String modifiedJarName = "Modified_Manifest_EntryAttributes.jar"; 882 Support_Resources.copyFile(resources, null, modifiedJarName); 883 JarFile jarFile = new JarFile(new File(resources, modifiedJarName), 884 true); 885 Enumeration<JarEntry> entries = jarFile.entries(); 886 while (entries.hasMoreElements()) { 887 ZipEntry zipEntry = entries.nextElement(); 888 try { 889 jarFile.getInputStream(zipEntry); 890 fail("should throw Security Exception"); 891 } catch (SecurityException e) { 892 // desired 893 } 894 } 895 } 896 897 /* 898 * If the content of the .SA file is modified, no matter what it resides, 899 * JarFile.getInputStream of any JarEntry will throw Security Exception. 900 */ 901 public void test_JarFile_Modified_SF_EntryAttributes() throws IOException { 902 String modifiedJarName = "Modified_SF_EntryAttributes.jar"; 903 Support_Resources.copyFile(resources, null, modifiedJarName); 904 JarFile jarFile = new JarFile(new File(resources, modifiedJarName), 905 true); 906 Enumeration<JarEntry> entries = jarFile.entries(); 907 while (entries.hasMoreElements()) { 908 ZipEntry zipEntry = entries.nextElement(); 909 try { 910 jarFile.getInputStream(zipEntry); 911 fail("should throw Security Exception"); 912 } catch (SecurityException e) { 913 // desired 914 } 915 } 916 } 917 918 public void test_close() throws IOException { 919 String modifiedJarName = "Modified_SF_EntryAttributes.jar"; 920 Support_Resources.copyFile(resources, null, modifiedJarName); 921 JarFile jarFile = new JarFile(new File(resources, modifiedJarName), 922 true); 923 Enumeration<JarEntry> entries = jarFile.entries(); 924 925 jarFile.close(); 926 jarFile.close(); 927 928 // Can not check IOException 929 } 930 931 /** 932 * @throws IOException 933 * java.util.jar.JarFile#getInputStream(java.util.zip.ZipEntry) 934 */ 935 public void test_getInputStreamLjava_util_jar_JarEntry() throws IOException { 936 File localFile = null; 937 try { 938 Support_Resources.copyFile(resources, null, jarName); 939 localFile = new File(resources, jarName); 940 } catch (Exception e) { 941 fail("Failed to create local file: " + e); 942 } 943 944 byte[] b = new byte[1024]; 945 try { 946 JarFile jf = new JarFile(localFile); 947 java.io.InputStream is = jf.getInputStream(jf.getEntry(entryName)); 948 // BEGIN android-removed 949 // jf.close(); 950 // END android-removed 951 assertTrue("Returned invalid stream", is.available() > 0); 952 int r = is.read(b, 0, 1024); 953 is.close(); 954 StringBuffer sb = new StringBuffer(r); 955 for (int i = 0; i < r; i++) { 956 sb.append((char) (b[i] & 0xff)); 957 } 958 String contents = sb.toString(); 959 assertTrue("Incorrect stream read", contents.indexOf("bar") > 0); 960 // BEGIN android-added 961 jf.close(); 962 // END android-added 963 } catch (Exception e) { 964 fail("Exception during test: " + e.toString()); 965 } 966 967 try { 968 JarFile jf = new JarFile(localFile); 969 InputStream in = jf.getInputStream(new JarEntry("invalid")); 970 assertNull("Got stream for non-existent entry", in); 971 } catch (Exception e) { 972 fail("Exception during test 2: " + e); 973 } 974 975 try { 976 Support_Resources.copyFile(resources, null, jarName); 977 File signedFile = new File(resources, jarName); 978 JarFile jf = new JarFile(signedFile); 979 JarEntry jre = new JarEntry("foo/bar/A.class"); 980 jf.getInputStream(jre); 981 // InputStream returned in any way, exception can be thrown in case 982 // of reading from this stream only. 983 // fail("Should throw ZipException"); 984 } catch (ZipException ee) { 985 // expected 986 } 987 988 try { 989 Support_Resources.copyFile(resources, null, jarName); 990 File signedFile = new File(resources, jarName); 991 JarFile jf = new JarFile(signedFile); 992 JarEntry jre = new JarEntry("foo/bar/A.class"); 993 jf.close(); 994 jf.getInputStream(jre); 995 // InputStream returned in any way, exception can be thrown in case 996 // of reading from this stream only. 997 // The same for IOException 998 fail("Should throw IllegalStateException"); 999 } catch (IllegalStateException ee) { 1000 // expected 1001 } 1002 } 1003 1004 /** 1005 * The jar is intact, but the entry object is modified. 1006 */ 1007 // Regression test for issue introduced by HARMONY-4569: signed archives containing files with size 0 could not get verified. 1008 public void testJarVerificationEmptyEntry() throws IOException { 1009 Support_Resources.copyFile(resources, null, emptyEntryJar); 1010 File f = new File(resources, emptyEntryJar); 1011 1012 JarFile jarFile = new JarFile(f); 1013 1014 ZipEntry zipEntry = jarFile.getJarEntry(emptyEntry1); 1015 int res = jarFile.getInputStream(zipEntry).read(new byte[100], 0, 100); 1016 assertEquals("Wrong length of empty jar entry", -1, res); 1017 1018 zipEntry = jarFile.getJarEntry(emptyEntry2); 1019 res = jarFile.getInputStream(zipEntry).read(new byte[100], 0, 100); 1020 assertEquals("Wrong length of empty jar entry", -1, res); 1021 1022 zipEntry = jarFile.getJarEntry(emptyEntry3); 1023 res = jarFile.getInputStream(zipEntry).read(); 1024 assertEquals("Wrong length of empty jar entry", -1, res); 1025 } 1026} 1027