17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package java.util.jar;
27
28 import java.io.*;
29 import java.lang.ref.SoftReference;
30 import java.net.URL;
31 import java.util.*;
32 import java.util.stream.Stream;
33 import java.util.stream.StreamSupport;
34 import java.util.zip.*;
35 import java.security.CodeSigner;
36 import java.security.cert.Certificate;
37 import java.security.AccessController;
38 import java.security.CodeSource;
39 import sun.misc.IOUtils;
40 import sun.security.action.GetPropertyAction;
41 import sun.security.util.ManifestEntryVerifier;
42 import sun.misc.SharedSecrets;
43 import sun.security.util.SignatureFileVerifier;
44
45 /**
46 * The <code>JarFile</code> class is used to read the contents of a jar file
47 * from any file that can be opened with <code>java.io.RandomAccessFile</code>.
48 * It extends the class <code>java.util.zip.ZipFile</code> with support
49 * for reading an optional <code>Manifest</code> entry. The
50 * <code>Manifest</code> can be used to specify meta-information about the
51 * jar file and its entries.
52 *
53 * <p> Unless otherwise noted, passing a <tt>null</tt> argument to a constructor
54 * or method in this class will cause a {@link NullPointerException} to be
55 * thrown.
56 *
57 * If the verify flag is on when opening a signed jar file, the content of the
58 * file is verified against its signature embedded inside the file. Please note
59 * that the verification process does not include validating the signer's
60 * certificate. A caller should inspect the return value of
68 * @since 1.2
69 */
70 public
71 class JarFile extends ZipFile {
72 private SoftReference<Manifest> manRef;
73 private JarEntry manEntry;
74 private JarVerifier jv;
75 private boolean jvInitialized;
76 private boolean verify;
77
78 // indicates if Class-Path attribute present (only valid if hasCheckedSpecialAttributes true)
79 private boolean hasClassPathAttribute;
80 // true if manifest checked for special attributes
81 private volatile boolean hasCheckedSpecialAttributes;
82
83 // Set up JavaUtilJarAccess in SharedSecrets
84 static {
85 SharedSecrets.setJavaUtilJarAccess(new JavaUtilJarAccessImpl());
86 }
87
88 /**
89 * The JAR manifest file name.
90 */
91 public static final String MANIFEST_NAME = "META-INF/MANIFEST.MF";
92
93 /**
94 * Creates a new <code>JarFile</code> to read from the specified
95 * file <code>name</code>. The <code>JarFile</code> will be verified if
96 * it is signed.
97 * @param name the name of the jar file to be opened for reading
98 * @throws IOException if an I/O error has occurred
99 * @throws SecurityException if access to the file is denied
100 * by the SecurityManager
101 */
102 public JarFile(String name) throws IOException {
103 this(new File(name), true, ZipFile.OPEN_READ);
104 }
105
106 /**
107 * Creates a new <code>JarFile</code> to read from the specified
168 }
169
170 /**
171 * Returns the jar file manifest, or <code>null</code> if none.
172 *
173 * @return the jar file manifest, or <code>null</code> if none
174 *
175 * @throws IllegalStateException
176 * may be thrown if the jar file has been closed
177 * @throws IOException if an I/O error has occurred
178 */
179 public Manifest getManifest() throws IOException {
180 return getManifestFromReference();
181 }
182
183 private Manifest getManifestFromReference() throws IOException {
184 Manifest man = manRef != null ? manRef.get() : null;
185
186 if (man == null) {
187
188 JarEntry manEntry = getManEntry();
189
190 // If found then load the manifest
191 if (manEntry != null) {
192 if (verify) {
193 byte[] b = getBytes(manEntry);
194 man = new Manifest(new ByteArrayInputStream(b));
195 if (!jvInitialized) {
196 jv = new JarVerifier(b);
197 }
198 } else {
199 man = new Manifest(super.getInputStream(manEntry));
200 }
201 manRef = new SoftReference<>(man);
202 }
203 }
204 return man;
205 }
206
207 private native String[] getMetaInfEntryNames();
208
209 /**
210 * Returns the <code>JarEntry</code> for the given entry name or
211 * <code>null</code> if not found.
212 *
213 * @param name the jar file entry name
214 * @return the <code>JarEntry</code> for the given entry name or
215 * <code>null</code> if not found.
216 *
217 * @throws IllegalStateException
218 * may be thrown if the jar file has been closed
219 *
220 * @see java.util.jar.JarEntry
359 */
360 private void initializeVerifier() {
361 ManifestEntryVerifier mev = null;
362
363 // Verify "META-INF/" entries...
364 try {
365 String[] names = getMetaInfEntryNames();
366 if (names != null) {
367 for (String name : names) {
368 String uname = name.toUpperCase(Locale.ENGLISH);
369 if (MANIFEST_NAME.equals(uname)
370 || SignatureFileVerifier.isBlockOrSF(uname)) {
371 JarEntry e = getJarEntry(name);
372 if (e == null) {
373 throw new JarException("corrupted jar file");
374 }
375 if (mev == null) {
376 mev = new ManifestEntryVerifier
377 (getManifestFromReference());
378 }
379 byte[] b = getBytes(e);
380 if (b != null && b.length > 0) {
381 jv.beginEntry(e, mev);
382 jv.update(b.length, b, 0, b.length, mev);
383 jv.update(-1, null, 0, 0, mev);
384 }
385 }
386 }
387 }
388 } catch (IOException ex) {
389 // if we had an error parsing any blocks, just
390 // treat the jar file as being unsigned
391 jv = null;
392 verify = false;
393 if (JarVerifier.debug != null) {
394 JarVerifier.debug.println("jarfile parsing error!");
395 ex.printStackTrace();
396 }
397 }
398
399 // if after initializing the verifier we have nothing
400 // signed, we null it out.
401
402 if (jv != null) {
403
404 jv.doneWithMeta();
405 if (JarVerifier.debug != null) {
406 JarVerifier.debug.println("done with meta!");
407 }
408
409 if (jv.nothingToVerify()) {
410 if (JarVerifier.debug != null) {
411 JarVerifier.debug.println("nothing to verify!");
412 }
413 jv = null;
414 verify = false;
415 }
416 }
417 }
418
419 /*
420 * Reads all the bytes for a given entry. Used to process the
421 * META-INF files.
422 */
423 private byte[] getBytes(ZipEntry ze) throws IOException {
424 try (InputStream is = super.getInputStream(ze)) {
425 return IOUtils.readFully(is, (int)ze.getSize(), true);
426 }
427 }
428
429 /**
430 * Returns an input stream for reading the contents of the specified
431 * zip file entry.
432 * @param ze the zip file entry
433 * @return an input stream for reading the contents of the specified
434 * zip file entry
435 * @throws ZipException if a zip file format error has occurred
436 * @throws IOException if an I/O error has occurred
437 * @throws SecurityException if any of the jar file entries
438 * are incorrectly signed.
439 * @throws IllegalStateException
440 * may be thrown if the jar file has been closed
441 */
442 public synchronized InputStream getInputStream(ZipEntry ze)
443 throws IOException
444 {
445 maybeInstantiateVerifier();
446 if (jv == null) {
447 return super.getInputStream(ze);
448 }
531 while (i<=last) {
532 for (int j=(len-1); j>=0; j--) {
533 char c = (char) b[i+j];
534 c = (((c-'A')|('Z'-c)) >= 0) ? (char)(c + 32) : c;
535 if (c != src[j]) {
536 i += Math.max(j + 1 - lastOcc[c&0x7F], optoSft[j]);
537 continue next;
538 }
539 }
540 return true;
541 }
542 return false;
543 }
544
545 /**
546 * On first invocation, check if the JAR file has the Class-Path
547 * attribute. A no-op on subsequent calls.
548 */
549 private void checkForSpecialAttributes() throws IOException {
550 if (hasCheckedSpecialAttributes) return;
551 JarEntry manEntry = getManEntry();
552 if (manEntry != null) {
553 byte[] b = getBytes(manEntry);
554 if (match(CLASSPATH_CHARS, b, CLASSPATH_LASTOCC, CLASSPATH_OPTOSFT))
555 hasClassPathAttribute = true;
556 }
557 hasCheckedSpecialAttributes = true;
558 }
559
560 private synchronized void ensureInitialization() {
561 try {
562 maybeInstantiateVerifier();
563 } catch (IOException e) {
564 throw new RuntimeException(e);
565 }
566 if (jv != null && !jvInitialized) {
567 initializeVerifier();
568 jvInitialized = true;
569 }
570 }
571
572 JarEntry newEntry(ZipEntry ze) {
573 return new JarFileEntry(ze);
|
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package java.util.jar;
27
28 import java.io.*;
29 import java.lang.ref.SoftReference;
30 import java.net.URL;
31 import java.util.*;
32 import java.util.stream.Stream;
33 import java.util.stream.StreamSupport;
34 import java.util.zip.*;
35 import java.security.CodeSigner;
36 import java.security.cert.Certificate;
37 import java.security.CodeSource;
38 import sun.security.util.ManifestEntryVerifier;
39 import sun.misc.SharedSecrets;
40 import sun.security.util.SignatureFileVerifier;
41
42 /**
43 * The <code>JarFile</code> class is used to read the contents of a jar file
44 * from any file that can be opened with <code>java.io.RandomAccessFile</code>.
45 * It extends the class <code>java.util.zip.ZipFile</code> with support
46 * for reading an optional <code>Manifest</code> entry. The
47 * <code>Manifest</code> can be used to specify meta-information about the
48 * jar file and its entries.
49 *
50 * <p> Unless otherwise noted, passing a <tt>null</tt> argument to a constructor
51 * or method in this class will cause a {@link NullPointerException} to be
52 * thrown.
53 *
54 * If the verify flag is on when opening a signed jar file, the content of the
55 * file is verified against its signature embedded inside the file. Please note
56 * that the verification process does not include validating the signer's
57 * certificate. A caller should inspect the return value of
65 * @since 1.2
66 */
67 public
68 class JarFile extends ZipFile {
69 private SoftReference<Manifest> manRef;
70 private JarEntry manEntry;
71 private JarVerifier jv;
72 private boolean jvInitialized;
73 private boolean verify;
74
75 // indicates if Class-Path attribute present (only valid if hasCheckedSpecialAttributes true)
76 private boolean hasClassPathAttribute;
77 // true if manifest checked for special attributes
78 private volatile boolean hasCheckedSpecialAttributes;
79
80 // Set up JavaUtilJarAccess in SharedSecrets
81 static {
82 SharedSecrets.setJavaUtilJarAccess(new JavaUtilJarAccessImpl());
83 }
84
85 private static final sun.misc.JavaUtilZipFileAccess zipAccess
86 = sun.misc.SharedSecrets.getJavaUtilZipFileAccess();
87
88 /**
89 * The JAR manifest file name.
90 */
91 public static final String MANIFEST_NAME = "META-INF/MANIFEST.MF";
92
93 /**
94 * Creates a new <code>JarFile</code> to read from the specified
95 * file <code>name</code>. The <code>JarFile</code> will be verified if
96 * it is signed.
97 * @param name the name of the jar file to be opened for reading
98 * @throws IOException if an I/O error has occurred
99 * @throws SecurityException if access to the file is denied
100 * by the SecurityManager
101 */
102 public JarFile(String name) throws IOException {
103 this(new File(name), true, ZipFile.OPEN_READ);
104 }
105
106 /**
107 * Creates a new <code>JarFile</code> to read from the specified
168 }
169
170 /**
171 * Returns the jar file manifest, or <code>null</code> if none.
172 *
173 * @return the jar file manifest, or <code>null</code> if none
174 *
175 * @throws IllegalStateException
176 * may be thrown if the jar file has been closed
177 * @throws IOException if an I/O error has occurred
178 */
179 public Manifest getManifest() throws IOException {
180 return getManifestFromReference();
181 }
182
183 private Manifest getManifestFromReference() throws IOException {
184 Manifest man = manRef != null ? manRef.get() : null;
185
186 if (man == null) {
187
188 JarEntry entry = getManEntry();
189
190 // If found then load the manifest
191 if (entry != null) {
192 byte[] b = zipAccess.getBytes(this, entry);
193 man = new Manifest(new ByteArrayInputStream(b));
194 if (verify && !jvInitialized) {
195 jv = new JarVerifier(b);
196 }
197 manRef = new SoftReference<>(man);
198 }
199 }
200 return man;
201 }
202
203 private native String[] getMetaInfEntryNames();
204
205 /**
206 * Returns the <code>JarEntry</code> for the given entry name or
207 * <code>null</code> if not found.
208 *
209 * @param name the jar file entry name
210 * @return the <code>JarEntry</code> for the given entry name or
211 * <code>null</code> if not found.
212 *
213 * @throws IllegalStateException
214 * may be thrown if the jar file has been closed
215 *
216 * @see java.util.jar.JarEntry
355 */
356 private void initializeVerifier() {
357 ManifestEntryVerifier mev = null;
358
359 // Verify "META-INF/" entries...
360 try {
361 String[] names = getMetaInfEntryNames();
362 if (names != null) {
363 for (String name : names) {
364 String uname = name.toUpperCase(Locale.ENGLISH);
365 if (MANIFEST_NAME.equals(uname)
366 || SignatureFileVerifier.isBlockOrSF(uname)) {
367 JarEntry e = getJarEntry(name);
368 if (e == null) {
369 throw new JarException("corrupted jar file");
370 }
371 if (mev == null) {
372 mev = new ManifestEntryVerifier
373 (getManifestFromReference());
374 }
375 byte[] b = zipAccess.getBytes(this, e);
376 if (b != null && b.length > 0) {
377 jv.beginEntry(e, mev);
378 jv.update(b.length, b, 0, b.length, mev);
379 jv.update(-1, null, 0, 0, mev);
380 }
381 }
382 }
383 }
384 } catch (IOException ex) {
385 // if we had an error parsing any blocks, just
386 // treat the jar file as being unsigned
387 jv = null;
388 verify = false;
389 if (JarVerifier.debug != null) {
390 JarVerifier.debug.println("jarfile parsing error!");
391 ex.printStackTrace();
392 }
393 }
394
395 // if after initializing the verifier we have nothing
396 // signed, we null it out.
397
398 if (jv != null) {
399
400 jv.doneWithMeta();
401 if (JarVerifier.debug != null) {
402 JarVerifier.debug.println("done with meta!");
403 }
404
405 if (jv.nothingToVerify()) {
406 if (JarVerifier.debug != null) {
407 JarVerifier.debug.println("nothing to verify!");
408 }
409 jv = null;
410 verify = false;
411 }
412 }
413 }
414
415 /**
416 * Returns an input stream for reading the contents of the specified
417 * zip file entry.
418 * @param ze the zip file entry
419 * @return an input stream for reading the contents of the specified
420 * zip file entry
421 * @throws ZipException if a zip file format error has occurred
422 * @throws IOException if an I/O error has occurred
423 * @throws SecurityException if any of the jar file entries
424 * are incorrectly signed.
425 * @throws IllegalStateException
426 * may be thrown if the jar file has been closed
427 */
428 public synchronized InputStream getInputStream(ZipEntry ze)
429 throws IOException
430 {
431 maybeInstantiateVerifier();
432 if (jv == null) {
433 return super.getInputStream(ze);
434 }
517 while (i<=last) {
518 for (int j=(len-1); j>=0; j--) {
519 char c = (char) b[i+j];
520 c = (((c-'A')|('Z'-c)) >= 0) ? (char)(c + 32) : c;
521 if (c != src[j]) {
522 i += Math.max(j + 1 - lastOcc[c&0x7F], optoSft[j]);
523 continue next;
524 }
525 }
526 return true;
527 }
528 return false;
529 }
530
531 /**
532 * On first invocation, check if the JAR file has the Class-Path
533 * attribute. A no-op on subsequent calls.
534 */
535 private void checkForSpecialAttributes() throws IOException {
536 if (hasCheckedSpecialAttributes) return;
537 JarEntry entry = getManEntry();
538 if (entry != null) {
539 byte[] b = zipAccess.getBytes(this, entry);
540 if (match(CLASSPATH_CHARS, b, CLASSPATH_LASTOCC, CLASSPATH_OPTOSFT))
541 hasClassPathAttribute = true;
542 }
543 hasCheckedSpecialAttributes = true;
544 }
545
546 private synchronized void ensureInitialization() {
547 try {
548 maybeInstantiateVerifier();
549 } catch (IOException e) {
550 throw new RuntimeException(e);
551 }
552 if (jv != null && !jvInitialized) {
553 initializeVerifier();
554 jvInitialized = true;
555 }
556 }
557
558 JarEntry newEntry(ZipEntry ze) {
559 return new JarFileEntry(ze);
|