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