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