1 /*
   2  * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  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 jdk.internal.misc.SharedSecrets;
  39 import sun.security.action.GetPropertyAction;
  40 import sun.security.util.ManifestEntryVerifier;
  41 import sun.security.util.SignatureFileVerifier;
  42 
  43 /**
  44  * The {@code JarFile} class is used to read the contents of a jar file
  45  * from any file that can be opened with {@code java.io.RandomAccessFile}.
  46  * It extends the class {@code java.util.zip.ZipFile} with support
  47  * for reading an optional {@code Manifest} entry, and support for
  48  * processing multi-release jar files.  The {@code Manifest} can be used
  49  * to specify meta-information about the jar file and its entries.
  50  *
  51  * <p>A multi-release jar file is a jar file that contains
  52  * a manifest with a main attribute named "Multi-Release",
  53  * a set of "base" entries, some of which are public classes with public
  54  * or protected methods that comprise the public interface of the jar file,
  55  * and a set of "versioned" entries contained in subdirectories of the
  56  * "META-INF/versions" directory.  The versioned entries are partitioned by the
  57  * major version of the Java platform.  A versioned entry, with a version
  58  * {@code n}, {@code 8 < n}, in the "META-INF/versions/{n}" directory overrides
  59  * the base entry as well as any entry with a version number {@code i} where
  60  * {@code 8 < i < n}.
  61  *
  62  * <p>By default, a {@code JarFile} for a multi-release jar file is configured
  63  * to process the multi-release jar file as if it were a plain (unversioned) jar
  64  * file, and as such an entry name is associated with at most one base entry.
  65  * The {@code JarFile} may be configured to process a multi-release jar file by
  66  * creating the {@code JarFile} with the
  67  * {@link JarFile#JarFile(File, boolean, int, Runtime.Version)} constructor.  The
  68  * {@code Runtime.Version} object sets a maximum version used when searching for
  69  * versioned entries.  When so configured, an entry name
  70  * can correspond with at most one base entry and zero or more versioned
  71  * entries. A search is required to associate the entry name with the latest
  72  * versioned entry whose version is less than or equal to the maximum version
  73  * (see {@link #getEntry(String)}).
  74  *
  75  * <p>Class loaders that utilize {@code JarFile} to load classes from the
  76  * contents of {@code JarFile} entries should construct the {@code JarFile}
  77  * by invoking the {@link JarFile#JarFile(File, boolean, int, Runtime.Version)}
  78  * constructor with the value {@code Runtime.version()} assigned to the last
  79  * argument.  This assures that classes compatible with the major
  80  * version of the running JVM are loaded from multi-release jar files.
  81  *
  82  * <p>If the verify flag is on when opening a signed jar file, the content of
  83  * the file is verified against its signature embedded inside the file. Please
  84  * note that the verification process does not include validating the signer's
  85  * certificate. A caller should inspect the return value of
  86  * {@link JarEntry#getCodeSigners()} to further determine if the signature
  87  * can be trusted.
  88  *
  89  * <p> Unless otherwise noted, passing a {@code null} argument to a constructor
  90  * or method in this class will cause a {@link NullPointerException} to be
  91  * thrown.
  92  *
  93  * @implNote
  94  * <div class="block">
  95  * If the API can not be used to configure a {@code JarFile} (e.g. to override
  96  * the configuration of a compiled application or library), two {@code System}
  97  * properties are available.
  98  * <ul>
  99  * <li>
 100  * {@code jdk.util.jar.version} can be assigned a value that is the
 101  * {@code String} representation of a non-negative integer
 102  * {@code <= Runtime.version().major()}.  The value is used to set the effective
 103  * runtime version to something other than the default value obtained by
 104  * evaluating {@code Runtime.version().major()}. The effective runtime version
 105  * is the version that the {@link JarFile#JarFile(File, boolean, int, Runtime.Version)}
 106  * constructor uses when the value of the last argument is
 107  * {@code JarFile.runtimeVersion()}.
 108  * </li>
 109  * <li>
 110  * {@code jdk.util.jar.enableMultiRelease} can be assigned one of the three
 111  * {@code String} values <em>true</em>, <em>false</em>, or <em>force</em>.  The
 112  * value <em>true</em>, the default value, enables multi-release jar file
 113  * processing.  The value <em>false</em> disables multi-release jar processing,
 114  * ignoring the "Multi-Release" manifest attribute, and the versioned
 115  * directories in a multi-release jar file if they exist.  Furthermore,
 116  * the method {@link JarFile#isMultiRelease()} returns <em>false</em>. The value
 117  * <em>force</em> causes the {@code JarFile} to be initialized to runtime
 118  * versioning after construction.  It effectively does the same as this code:
 119  * {@code (new JarFile(File, boolean, int, JarFile.runtimeVersion())}.
 120  * </li>
 121  * </ul>
 122  * </div>
 123  *
 124  * @author  David Connelly
 125  * @see     Manifest
 126  * @see     java.util.zip.ZipFile
 127  * @see     java.util.jar.JarEntry
 128  * @since   1.2
 129  */
 130 public
 131 class JarFile extends ZipFile {
 132     private final static Runtime.Version BASE_VERSION;
 133     private final static int BASE_VERSION_MAJOR;
 134     private final static Runtime.Version RUNTIME_VERSION;
 135     private final static boolean MULTI_RELEASE_ENABLED;
 136     private final static boolean MULTI_RELEASE_FORCED;
 137     private SoftReference<Manifest> manRef;
 138     private JarEntry manEntry;
 139     private JarVerifier jv;
 140     private boolean jvInitialized;
 141     private boolean verify;
 142     private final Runtime.Version version;  // current version
 143     private final int versionMajor;         // version.major()
 144     private boolean isMultiRelease;         // is jar multi-release?
 145 
 146     // indicates if Class-Path attribute present
 147     private boolean hasClassPathAttribute;
 148     // true if manifest checked for special attributes
 149     private volatile boolean hasCheckedSpecialAttributes;
 150 
 151     static {
 152         // Set up JavaUtilJarAccess in SharedSecrets
 153         SharedSecrets.setJavaUtilJarAccess(new JavaUtilJarAccessImpl());
 154         // multi-release jar file versions >= 9
 155         BASE_VERSION = Runtime.Version.parse(Integer.toString(8));
 156         BASE_VERSION_MAJOR = BASE_VERSION.major();
 157         String jarVersion = GetPropertyAction.privilegedGetProperty("jdk.util.jar.version");
 158         int runtimeVersion = Runtime.version().major();
 159         if (jarVersion != null) {
 160             int jarVer = Integer.parseInt(jarVersion);
 161             runtimeVersion = (jarVer > runtimeVersion)
 162                     ? runtimeVersion
 163                     : Math.max(jarVer, BASE_VERSION_MAJOR);
 164         }
 165         RUNTIME_VERSION = Runtime.Version.parse(Integer.toString(runtimeVersion));
 166         String enableMultiRelease = GetPropertyAction
 167                 .privilegedGetProperty("jdk.util.jar.enableMultiRelease", "true");
 168         switch (enableMultiRelease) {
 169             case "true":
 170             default:
 171                 MULTI_RELEASE_ENABLED = true;
 172                 MULTI_RELEASE_FORCED = false;
 173                 break;
 174             case "false":
 175                 MULTI_RELEASE_ENABLED = false;
 176                 MULTI_RELEASE_FORCED = false;
 177                 break;
 178             case "force":
 179                 MULTI_RELEASE_ENABLED = true;
 180                 MULTI_RELEASE_FORCED = true;
 181                 break;
 182         }
 183     }
 184 
 185     private static final String META_INF = "META-INF/";
 186 
 187     private static final String META_INF_VERSIONS = META_INF + "versions/";
 188 
 189     /**
 190      * The JAR manifest file name.
 191      */
 192     public static final String MANIFEST_NAME = META_INF + "MANIFEST.MF";
 193 
 194     /**
 195      * The version that represents the unversioned configuration of a multi-release jar file.
 196      *
 197      * @return Runtime.Version that represents the unversioned configuration
 198      *
 199      * @since 9
 200      */
 201     public static Runtime.Version baseVersion() {
 202         return BASE_VERSION;
 203     }
 204 
 205     /**
 206      * The version that represents the effective runtime versioned configuration of a
 207      * multi-release jar file.  In most cases, {@code runtimeVersion()} is equal to
 208      * {@code Runtime.version()}.  However, if the {@code jdk.util.jar.version} property is set,
 209      * {@code runtimeVersion()} is derived from that property and may not be equal to
 210      * {@code Runtime.version()}.
 211      *
 212      * @return Runtime.Version that represents the runtime versioned configuration
 213      *
 214      * @since 9
 215      */
 216     public static Runtime.Version runtimeVersion() {
 217         return RUNTIME_VERSION;
 218     }
 219 
 220     /**
 221      * Creates a new {@code JarFile} to read from the specified
 222      * file {@code name}. The {@code JarFile} will be verified if
 223      * it is signed.
 224      * @param name the name of the jar file to be opened for reading
 225      * @throws IOException if an I/O error has occurred
 226      * @throws SecurityException if access to the file is denied
 227      *         by the SecurityManager
 228      */
 229     public JarFile(String name) throws IOException {
 230         this(new File(name), true, ZipFile.OPEN_READ);
 231     }
 232 
 233     /**
 234      * Creates a new {@code JarFile} to read from the specified
 235      * file {@code name}.
 236      * @param name the name of the jar file to be opened for reading
 237      * @param verify whether or not to verify the jar file if
 238      * it is signed.
 239      * @throws IOException if an I/O error has occurred
 240      * @throws SecurityException if access to the file is denied
 241      *         by the SecurityManager
 242      */
 243     public JarFile(String name, boolean verify) throws IOException {
 244         this(new File(name), verify, ZipFile.OPEN_READ);
 245     }
 246 
 247     /**
 248      * Creates a new {@code JarFile} to read from the specified
 249      * {@code File} object. The {@code JarFile} will be verified if
 250      * it is signed.
 251      * @param file the jar file to be opened for reading
 252      * @throws IOException if an I/O error has occurred
 253      * @throws SecurityException if access to the file is denied
 254      *         by the SecurityManager
 255      */
 256     public JarFile(File file) throws IOException {
 257         this(file, true, ZipFile.OPEN_READ);
 258     }
 259 
 260     /**
 261      * Creates a new {@code JarFile} to read from the specified
 262      * {@code File} object.
 263      * @param file the jar file to be opened for reading
 264      * @param verify whether or not to verify the jar file if
 265      * it is signed.
 266      * @throws IOException if an I/O error has occurred
 267      * @throws SecurityException if access to the file is denied
 268      *         by the SecurityManager.
 269      */
 270     public JarFile(File file, boolean verify) throws IOException {
 271         this(file, verify, ZipFile.OPEN_READ);
 272     }
 273 
 274     /**
 275      * Creates a new {@code JarFile} to read from the specified
 276      * {@code File} object in the specified mode.  The mode argument
 277      * must be either {@code OPEN_READ} or {@code OPEN_READ | OPEN_DELETE}.
 278      *
 279      * @param file the jar file to be opened for reading
 280      * @param verify whether or not to verify the jar file if
 281      * it is signed.
 282      * @param mode the mode in which the file is to be opened
 283      * @throws IOException if an I/O error has occurred
 284      * @throws IllegalArgumentException
 285      *         if the {@code mode} argument is invalid
 286      * @throws SecurityException if access to the file is denied
 287      *         by the SecurityManager
 288      * @since 1.3
 289      */
 290     public JarFile(File file, boolean verify, int mode) throws IOException {
 291         this(file, verify, mode, BASE_VERSION);
 292     }
 293 
 294     /**
 295      * Creates a new {@code JarFile} to read from the specified
 296      * {@code File} object in the specified mode.  The mode argument
 297      * must be either {@code OPEN_READ} or {@code OPEN_READ | OPEN_DELETE}.
 298      * The version argument, after being converted to a canonical form, is
 299      * used to configure the {@code JarFile} for processing
 300      * multi-release jar files.
 301      * <p>
 302      * The canonical form derived from the version parameter is
 303      * {@code Runtime.Version.parse(Integer.toString(n))} where {@code n} is
 304      * {@code Math.max(version.major(), JarFile.baseVersion().major())}.
 305      *
 306      * @param file the jar file to be opened for reading
 307      * @param verify whether or not to verify the jar file if
 308      * it is signed.
 309      * @param mode the mode in which the file is to be opened
 310      * @param version specifies the release version for a multi-release jar file
 311      * @throws IOException if an I/O error has occurred
 312      * @throws IllegalArgumentException
 313      *         if the {@code mode} argument is invalid
 314      * @throws SecurityException if access to the file is denied
 315      *         by the SecurityManager
 316      * @throws NullPointerException if {@code version} is {@code null}
 317      * @since 9
 318      */
 319     public JarFile(File file, boolean verify, int mode, Runtime.Version version) throws IOException {
 320         super(file, mode);
 321         this.verify = verify;
 322         Objects.requireNonNull(version);
 323         if (MULTI_RELEASE_FORCED || version.major() == RUNTIME_VERSION.major()) {
 324             // This deals with the common case where the value from JarFile.runtimeVersion() is passed
 325             this.version = RUNTIME_VERSION;
 326         } else if (version.major() <= BASE_VERSION_MAJOR) {
 327             // This also deals with the common case where the value from JarFile.baseVersion() is passed
 328             this.version = BASE_VERSION;
 329         } else {
 330             // Canonicalize
 331             this.version = Runtime.Version.parse(Integer.toString(version.major()));
 332         }
 333         this.versionMajor = this.version.major();
 334     }
 335 
 336     /**
 337      * Returns the maximum version used when searching for versioned entries.
 338      *
 339      * @return the maximum version
 340      * @since 9
 341      */
 342     public final Runtime.Version getVersion() {
 343         return isMultiRelease() ? this.version : BASE_VERSION;
 344     }
 345 
 346     /**
 347      * Indicates whether or not this jar file is a multi-release jar file.
 348      *
 349      * @return true if this JarFile is a multi-release jar file
 350      * @since 9
 351      */
 352     public final boolean isMultiRelease() {
 353         if (isMultiRelease) {
 354             return true;
 355         }
 356         if (MULTI_RELEASE_ENABLED) {
 357             try {
 358                 checkForSpecialAttributes();
 359             } catch (IOException io) {
 360                 isMultiRelease = false;
 361             }
 362         }
 363         return isMultiRelease;
 364     }
 365 
 366     /**
 367      * Returns the jar file manifest, or {@code null} if none.
 368      *
 369      * @return the jar file manifest, or {@code null} if none
 370      *
 371      * @throws IllegalStateException
 372      *         may be thrown if the jar file has been closed
 373      * @throws IOException  if an I/O error has occurred
 374      */
 375     public Manifest getManifest() throws IOException {
 376         return getManifestFromReference();
 377     }
 378 
 379     private Manifest getManifestFromReference() throws IOException {
 380         Manifest man = manRef != null ? manRef.get() : null;
 381 
 382         if (man == null) {
 383 
 384             JarEntry manEntry = getManEntry();
 385 
 386             // If found then load the manifest
 387             if (manEntry != null) {
 388                 if (verify) {
 389                     byte[] b = getBytes(manEntry);
 390                     man = new Manifest(new ByteArrayInputStream(b));
 391                     if (!jvInitialized) {
 392                         jv = new JarVerifier(b);
 393                     }
 394                 } else {
 395                     man = new Manifest(super.getInputStream(manEntry));
 396                 }
 397                 manRef = new SoftReference<>(man);
 398             }
 399         }
 400         return man;
 401     }
 402 
 403     private String[] getMetaInfEntryNames() {
 404         return jdk.internal.misc.SharedSecrets.getJavaUtilZipFileAccess()
 405                                               .getMetaInfEntryNames((ZipFile)this);
 406     }
 407 
 408     /**
 409      * Returns the {@code JarEntry} for the given base entry name or
 410      * {@code null} if not found.
 411      *
 412      * <p>If this {@code JarFile} is a multi-release jar file and is configured
 413      * to be processed as such, then a search is performed to find and return
 414      * a {@code JarEntry} that is the latest versioned entry associated with the
 415      * given entry name.  The returned {@code JarEntry} is the versioned entry
 416      * corresponding to the given base entry name prefixed with the string
 417      * {@code "META-INF/versions/{n}/"}, for the largest value of {@code n} for
 418      * which an entry exists.  If such a versioned entry does not exist, then
 419      * the {@code JarEntry} for the base entry is returned, otherwise
 420      * {@code null} is returned if no entries are found.  The initial value for
 421      * the version {@code n} is the maximum version as returned by the method
 422      * {@link JarFile#getVersion()}.
 423      *
 424      * @param name the jar file entry name
 425      * @return the {@code JarEntry} for the given entry name, or
 426      *         the versioned entry name, or {@code null} if not found
 427      *
 428      * @throws IllegalStateException
 429      *         may be thrown if the jar file has been closed
 430      *
 431      * @see java.util.jar.JarEntry
 432      *
 433      * @implSpec
 434      * <div class="block">
 435      * This implementation invokes {@link JarFile#getEntry(String)}.
 436      * </div>
 437      */
 438     public JarEntry getJarEntry(String name) {
 439         return (JarEntry)getEntry(name);
 440     }
 441 
 442     /**
 443      * Returns the {@code ZipEntry} for the given base entry name or
 444      * {@code null} if not found.
 445      *
 446      * <p>If this {@code JarFile} is a multi-release jar file and is configured
 447      * to be processed as such, then a search is performed to find and return
 448      * a {@code ZipEntry} that is the latest versioned entry associated with the
 449      * given entry name.  The returned {@code ZipEntry} is the versioned entry
 450      * corresponding to the given base entry name prefixed with the string
 451      * {@code "META-INF/versions/{n}/"}, for the largest value of {@code n} for
 452      * which an entry exists.  If such a versioned entry does not exist, then
 453      * the {@code ZipEntry} for the base entry is returned, otherwise
 454      * {@code null} is returned if no entries are found.  The initial value for
 455      * the version {@code n} is the maximum version as returned by the method
 456      * {@link JarFile#getVersion()}.
 457      *
 458      * @param name the jar file entry name
 459      * @return the {@code ZipEntry} for the given entry name or
 460      *         the versioned entry name or {@code null} if not found
 461      *
 462      * @throws IllegalStateException
 463      *         may be thrown if the jar file has been closed
 464      *
 465      * @see java.util.zip.ZipEntry
 466      *
 467      * @implSpec
 468      * <div class="block">
 469      * This implementation may return a versioned entry for the requested name
 470      * even if there is not a corresponding base entry.  This can occur
 471      * if there is a private or package-private versioned entry that matches.
 472      * If a subclass overrides this method, assure that the override method
 473      * invokes {@code super.getEntry(name)} to obtain all versioned entries.
 474      * </div>
 475      */
 476     public ZipEntry getEntry(String name) {
 477         ZipEntry ze = super.getEntry(name);
 478         if (ze != null) {
 479             return new JarFileEntry(ze);
 480         }
 481         // no matching base entry, but maybe there is a versioned entry,
 482         // like a new private class
 483         if (isMultiRelease()) {
 484             ze = new ZipEntry(name);
 485             ZipEntry vze = getVersionedEntry(ze);
 486             if (ze != vze) {
 487                 return new JarFileEntry(name, vze);
 488             }
 489         }
 490         return null;
 491     }
 492 
 493     private class JarEntryIterator implements Enumeration<JarEntry>,
 494             Iterator<JarEntry>
 495     {
 496         final Enumeration<? extends ZipEntry> e = JarFile.super.entries();
 497 
 498         public boolean hasNext() {
 499             return e.hasMoreElements();
 500         }
 501 
 502         public JarEntry next() {
 503             ZipEntry ze = e.nextElement();
 504             return new JarFileEntry(ze.getName(), ze);
 505         }
 506 
 507         public boolean hasMoreElements() {
 508             return hasNext();
 509         }
 510 
 511         public JarEntry nextElement() {
 512             return next();
 513         }
 514 
 515         public Iterator<JarEntry> asIterator() {
 516             return this;
 517         }
 518     }
 519 
 520     /**
 521      * Returns an enumeration of the jar file entries.
 522      *
 523      * @return an enumeration of the jar file entries
 524      * @throws IllegalStateException
 525      *         may be thrown if the jar file has been closed
 526      */
 527     public Enumeration<JarEntry> entries() {
 528         return new JarEntryIterator();
 529     }
 530 
 531     /**
 532      * Returns an ordered {@code Stream} over the jar file entries.
 533      * Entries appear in the {@code Stream} in the order they appear in
 534      * the central directory of the jar file.
 535      *
 536      * @return an ordered {@code Stream} of entries in this jar file
 537      * @throws IllegalStateException if the jar file has been closed
 538      * @since 1.8
 539      *
 540      * @apiNote  A versioned view of the stream obtained from a {@code JarFile}
 541      * configured to process a multi-release jar file can be created with code
 542      * similar to the following:
 543      * <pre>
 544      * {@code
 545      *     Stream<JarEntry> versionedStream(JarFile jf) {
 546      *         return jf.stream().map(JarEntry::getName)
 547      *                  .filter(name -> !name.startsWith("META-INF/versions/"))
 548      *                  .map(jf::getJarEntry);
 549      *     }
 550      * }
 551      * </pre>
 552      */
 553     public Stream<JarEntry> stream() {
 554         return StreamSupport.stream(Spliterators.spliterator(
 555                 new JarEntryIterator(), size(),
 556                 Spliterator.ORDERED | Spliterator.DISTINCT |
 557                         Spliterator.IMMUTABLE | Spliterator.NONNULL), false);
 558     }
 559 
 560     private ZipEntry searchForVersionedEntry(final int version, String name) {
 561         ZipEntry vze = null;
 562         String sname = "/" + name;
 563         int i = version;
 564         while (i > BASE_VERSION_MAJOR) {
 565             vze = super.getEntry(META_INF_VERSIONS + i + sname);
 566             if (vze != null) break;
 567             i--;
 568         }
 569         return vze;
 570     }
 571 
 572     private ZipEntry getVersionedEntry(ZipEntry ze) {
 573         ZipEntry vze = null;
 574         if (BASE_VERSION_MAJOR < versionMajor && !ze.isDirectory()) {
 575             String name = ze.getName();
 576             if (!name.startsWith(META_INF)) {
 577                 vze = searchForVersionedEntry(versionMajor, name);
 578             }
 579         }
 580         return vze == null ? ze : vze;
 581     }
 582 
 583     /**
 584      * Returns the real name of a {@code JarEntry}.  If this {@code JarFile} is
 585      * a multi-release jar file and is configured to be processed as such, the
 586      * name returned by this method is the path name of the versioned entry
 587      * that the {@code JarEntry} represents, rather than the path name of the
 588      * base entry that {@link JarEntry#getName()} returns.  If the
 589      * {@code JarEntry} does not represent a versioned entry, or the
 590      * jar file is not a multi-release jar file or {@code JarFile} is not
 591      * configured for processing a multi-release jar file, this method returns
 592      * the same name that {@link JarEntry#getName()} returns.
 593      *
 594      * @param entry the JarEntry
 595      * @return the real name of the JarEntry
 596      * @since 9
 597      */
 598     String getRealName(JarEntry entry) {
 599         if (entry instanceof JarFileEntry) {
 600             return ((JarFileEntry)entry).realName();
 601         }
 602         return entry.getName();
 603     }
 604 
 605     private class JarFileEntry extends JarEntry {
 606         final private String name;
 607 
 608         JarFileEntry(ZipEntry ze) {
 609             super(isMultiRelease() ? getVersionedEntry(ze) : ze);
 610             this.name = ze.getName();
 611         }
 612         JarFileEntry(String name, ZipEntry vze) {
 613             super(vze);
 614             this.name = name;
 615         }
 616         public Attributes getAttributes() throws IOException {
 617             Manifest man = JarFile.this.getManifest();
 618             if (man != null) {
 619                 return man.getAttributes(super.getName());
 620             } else {
 621                 return null;
 622             }
 623         }
 624         public Certificate[] getCertificates() {
 625             try {
 626                 maybeInstantiateVerifier();
 627             } catch (IOException e) {
 628                 throw new RuntimeException(e);
 629             }
 630             if (certs == null && jv != null) {
 631                 certs = jv.getCerts(JarFile.this, realEntry());
 632             }
 633             return certs == null ? null : certs.clone();
 634         }
 635         public CodeSigner[] getCodeSigners() {
 636             try {
 637                 maybeInstantiateVerifier();
 638             } catch (IOException e) {
 639                 throw new RuntimeException(e);
 640             }
 641             if (signers == null && jv != null) {
 642                 signers = jv.getCodeSigners(JarFile.this, realEntry());
 643             }
 644             return signers == null ? null : signers.clone();
 645         }
 646         JarFileEntry realEntry() {
 647             if (isMultiRelease() && versionMajor != BASE_VERSION_MAJOR) {
 648                 String entryName = super.getName();
 649                 return entryName.equals(this.name) ? this : new JarFileEntry(entryName, this);
 650             }
 651             return this;
 652         }
 653         String realName() {
 654             return super.getName();
 655         }
 656 
 657         @Override
 658         public String getName() {
 659             return name;
 660         }
 661     }
 662 
 663     /*
 664      * Ensures that the JarVerifier has been created if one is
 665      * necessary (i.e., the jar appears to be signed.) This is done as
 666      * a quick check to avoid processing of the manifest for unsigned
 667      * jars.
 668      */
 669     private void maybeInstantiateVerifier() throws IOException {
 670         if (jv != null) {
 671             return;
 672         }
 673 
 674         if (verify) {
 675             String[] names = getMetaInfEntryNames();
 676             if (names != null) {
 677                 for (String nameLower : names) {
 678                     String name = nameLower.toUpperCase(Locale.ENGLISH);
 679                     if (name.endsWith(".DSA") ||
 680                         name.endsWith(".RSA") ||
 681                         name.endsWith(".EC") ||
 682                         name.endsWith(".SF")) {
 683                         // Assume since we found a signature-related file
 684                         // that the jar is signed and that we therefore
 685                         // need a JarVerifier and Manifest
 686                         getManifest();
 687                         return;
 688                     }
 689                 }
 690             }
 691             // No signature-related files; don't instantiate a
 692             // verifier
 693             verify = false;
 694         }
 695     }
 696 
 697 
 698     /*
 699      * Initializes the verifier object by reading all the manifest
 700      * entries and passing them to the verifier.
 701      */
 702     private void initializeVerifier() {
 703         ManifestEntryVerifier mev = null;
 704 
 705         // Verify "META-INF/" entries...
 706         try {
 707             String[] names = getMetaInfEntryNames();
 708             if (names != null) {
 709                 for (String name : names) {
 710                     String uname = name.toUpperCase(Locale.ENGLISH);
 711                     if (MANIFEST_NAME.equals(uname)
 712                             || SignatureFileVerifier.isBlockOrSF(uname)) {
 713                         JarEntry e = getJarEntry(name);
 714                         if (e == null) {
 715                             throw new JarException("corrupted jar file");
 716                         }
 717                         if (mev == null) {
 718                             mev = new ManifestEntryVerifier
 719                                 (getManifestFromReference());
 720                         }
 721                         byte[] b = getBytes(e);
 722                         if (b != null && b.length > 0) {
 723                             jv.beginEntry(e, mev);
 724                             jv.update(b.length, b, 0, b.length, mev);
 725                             jv.update(-1, null, 0, 0, mev);
 726                         }
 727                     }
 728                 }
 729             }
 730         } catch (IOException ex) {
 731             // if we had an error parsing any blocks, just
 732             // treat the jar file as being unsigned
 733             jv = null;
 734             verify = false;
 735             if (JarVerifier.debug != null) {
 736                 JarVerifier.debug.println("jarfile parsing error!");
 737                 ex.printStackTrace();
 738             }
 739         }
 740 
 741         // if after initializing the verifier we have nothing
 742         // signed, we null it out.
 743 
 744         if (jv != null) {
 745 
 746             jv.doneWithMeta();
 747             if (JarVerifier.debug != null) {
 748                 JarVerifier.debug.println("done with meta!");
 749             }
 750 
 751             if (jv.nothingToVerify()) {
 752                 if (JarVerifier.debug != null) {
 753                     JarVerifier.debug.println("nothing to verify!");
 754                 }
 755                 jv = null;
 756                 verify = false;
 757             }
 758         }
 759     }
 760 
 761     /*
 762      * Reads all the bytes for a given entry. Used to process the
 763      * META-INF files.
 764      */
 765     private byte[] getBytes(ZipEntry ze) throws IOException {
 766         try (InputStream is = super.getInputStream(ze)) {
 767             int len = (int)ze.getSize();
 768             int bytesRead;
 769             byte[] b;
 770             // trust specified entry sizes when reasonably small
 771             if (len != -1 && len <= 65535) {
 772                 b = new byte[len];
 773                 bytesRead = is.readNBytes(b, 0, len);
 774             } else {
 775                 b = is.readAllBytes();
 776                 bytesRead = b.length;
 777             }
 778             if (len != -1 && len != bytesRead) {
 779                 throw new EOFException("Expected:" + len + ", read:" + bytesRead);
 780             }
 781             return b;
 782         }
 783     }
 784 
 785     /**
 786      * Returns an input stream for reading the contents of the specified
 787      * zip file entry.
 788      * @param ze the zip file entry
 789      * @return an input stream for reading the contents of the specified
 790      *         zip file entry
 791      * @throws ZipException if a zip file format error has occurred
 792      * @throws IOException if an I/O error has occurred
 793      * @throws SecurityException if any of the jar file entries
 794      *         are incorrectly signed.
 795      * @throws IllegalStateException
 796      *         may be thrown if the jar file has been closed
 797      */
 798     public synchronized InputStream getInputStream(ZipEntry ze)
 799         throws IOException
 800     {
 801         maybeInstantiateVerifier();
 802         if (jv == null) {
 803             return super.getInputStream(ze);
 804         }
 805         if (!jvInitialized) {
 806             initializeVerifier();
 807             jvInitialized = true;
 808             // could be set to null after a call to
 809             // initializeVerifier if we have nothing to
 810             // verify
 811             if (jv == null)
 812                 return super.getInputStream(ze);
 813         }
 814 
 815         // wrap a verifier stream around the real stream
 816         return new JarVerifier.VerifierStream(
 817             getManifestFromReference(),
 818             verifiableEntry(ze),
 819             super.getInputStream(ze),
 820             jv);
 821     }
 822 
 823     private JarEntry verifiableEntry(ZipEntry ze) {
 824         if (ze instanceof JarFileEntry) {
 825             // assure the name and entry match for verification
 826             return ((JarFileEntry)ze).realEntry();
 827         }
 828         ze = getJarEntry(ze.getName());
 829         if (ze instanceof JarFileEntry) {
 830             return ((JarFileEntry)ze).realEntry();
 831         }
 832         return (JarEntry)ze;
 833     }
 834 
 835     // Statics for hand-coded Boyer-Moore search
 836     private static final byte[] CLASSPATH_CHARS =
 837             {'C','L','A','S','S','-','P','A','T','H', ':', ' '};
 838 
 839     // The bad character shift for "class-path:"
 840     private static final byte[] CLASSPATH_LASTOCC;
 841 
 842     private static final byte[] MULTIRELEASE_CHARS =
 843             {'M','U','L','T','I','-','R','E','L','E', 'A', 'S', 'E', ':',
 844                     ' ', 'T', 'R', 'U', 'E'};
 845 
 846     // The bad character shift for "multi-release: "
 847     private static final byte[] MULTIRELEASE_LASTOCC;
 848 
 849     static {
 850         CLASSPATH_LASTOCC = new byte[64];
 851         CLASSPATH_LASTOCC[(int)'C' - 32] = 1;
 852         CLASSPATH_LASTOCC[(int)'L' - 32] = 2;
 853         CLASSPATH_LASTOCC[(int)'S' - 32] = 5;
 854         CLASSPATH_LASTOCC[(int)'-' - 32] = 6;
 855         CLASSPATH_LASTOCC[(int)'P' - 32] = 7;
 856         CLASSPATH_LASTOCC[(int)'A' - 32] = 8;
 857         CLASSPATH_LASTOCC[(int)'T' - 32] = 9;
 858         CLASSPATH_LASTOCC[(int)'H' - 32] = 10;
 859         CLASSPATH_LASTOCC[(int)':' - 32] = 11;
 860         CLASSPATH_LASTOCC[(int)' ' - 32] = 12;
 861 
 862         MULTIRELEASE_LASTOCC = new byte[64];
 863         MULTIRELEASE_LASTOCC[(int)'M' - 32] = 1;
 864         MULTIRELEASE_LASTOCC[(int)'I' - 32] = 5;
 865         MULTIRELEASE_LASTOCC[(int)'-' - 32] = 6;
 866         MULTIRELEASE_LASTOCC[(int)'L' - 32] = 9;
 867         MULTIRELEASE_LASTOCC[(int)'A' - 32] = 11;
 868         MULTIRELEASE_LASTOCC[(int)'S' - 32] = 12;
 869         MULTIRELEASE_LASTOCC[(int)':' - 32] = 14;
 870         MULTIRELEASE_LASTOCC[(int)' ' - 32] = 15;
 871         MULTIRELEASE_LASTOCC[(int)'T' - 32] = 16;
 872         MULTIRELEASE_LASTOCC[(int)'R' - 32] = 17;
 873         MULTIRELEASE_LASTOCC[(int)'U' - 32] = 18;
 874         MULTIRELEASE_LASTOCC[(int)'E' - 32] = 19;
 875     }
 876 
 877     private JarEntry getManEntry() {
 878         if (manEntry == null) {
 879             // First look up manifest entry using standard name
 880             ZipEntry manEntry = super.getEntry(MANIFEST_NAME);
 881             if (manEntry == null) {
 882                 // If not found, then iterate through all the "META-INF/"
 883                 // entries to find a match.
 884                 String[] names = getMetaInfEntryNames();
 885                 if (names != null) {
 886                     for (String name : names) {
 887                         if (MANIFEST_NAME.equals(name.toUpperCase(Locale.ENGLISH))) {
 888                             manEntry = super.getEntry(name);
 889                             break;
 890                         }
 891                     }
 892                 }
 893             }
 894             this.manEntry = (manEntry == null)
 895                     ? null
 896                     : new JarFileEntry(manEntry.getName(), manEntry);
 897         }
 898         return manEntry;
 899     }
 900 
 901    /**
 902     * Returns {@code true} iff this JAR file has a manifest with the
 903     * Class-Path attribute
 904     */
 905     boolean hasClassPathAttribute() throws IOException {
 906         checkForSpecialAttributes();
 907         return hasClassPathAttribute;
 908     }
 909 
 910     /**
 911      * Returns true if the pattern {@code src} is found in {@code b}.
 912      * The {@code lastOcc} array is the precomputed bad character shifts.
 913      * Since there are no repeated substring in our search strings,
 914      * the good suffix shifts can be replaced with a comparison.
 915      */
 916     private int match(byte[] src, byte[] b, byte[] lastOcc) {
 917         int len = src.length;
 918         int last = b.length - len;
 919         int i = 0;
 920         next:
 921         while (i <= last) {
 922             for (int j = (len - 1); j >= 0; j--) {
 923                 byte c = b[i + j];
 924                 if (c >= ' ' && c <= 'z') {
 925                     if (c >= 'a') c -= 32; // Canonicalize
 926 
 927                     if (c != src[j]) {
 928                         // no match
 929                         int goodShift = (j < len - 1) ? len : 1;
 930                         int badShift = lastOcc[c - 32];
 931                         i += Math.max(j + 1 - badShift, goodShift);
 932                         continue next;
 933                     }
 934                 } else {
 935                     // no match, character not valid for name
 936                     i += len;
 937                     continue next;
 938                 }
 939             }
 940             return i;
 941         }
 942         return -1;
 943     }
 944 
 945     /**
 946      * On first invocation, check if the JAR file has the Class-Path
 947      * and the Multi-Release attribute. A no-op on subsequent calls.
 948      */
 949     private void checkForSpecialAttributes() throws IOException {
 950         if (hasCheckedSpecialAttributes) {
 951             return;
 952         }
 953         synchronized (this) {
 954             if (hasCheckedSpecialAttributes) {
 955                 return;
 956             }
 957             JarEntry manEntry = getManEntry();
 958             if (manEntry != null) {
 959                 byte[] b = getBytes(manEntry);
 960                 hasClassPathAttribute = match(CLASSPATH_CHARS, b,
 961                         CLASSPATH_LASTOCC) != -1;
 962                 // is this a multi-release jar file
 963                 if (MULTI_RELEASE_ENABLED) {
 964                     int i = match(MULTIRELEASE_CHARS, b, MULTIRELEASE_LASTOCC);
 965                     if (i != -1) {
 966                         i += MULTIRELEASE_CHARS.length;
 967                         if (i < b.length) {
 968                             byte c = b[i++];
 969                             // Check that the value is followed by a newline
 970                             // and does not have a continuation
 971                             if (c == '\n' &&
 972                                     (i == b.length || b[i] != ' ')) {
 973                                 isMultiRelease = true;
 974                             } else if (c == '\r') {
 975                                 if (i == b.length) {
 976                                     isMultiRelease = true;
 977                                 } else {
 978                                     c = b[i++];
 979                                     if (c == '\n') {
 980                                         if (i == b.length || b[i] != ' ') {
 981                                             isMultiRelease = true;
 982                                         }
 983                                     } else if (c != ' ') {
 984                                         isMultiRelease = true;
 985                                     }
 986                                 }
 987                             }
 988                         }
 989                     }
 990                 }
 991             }
 992             hasCheckedSpecialAttributes = true;
 993         }
 994     }
 995 
 996     private synchronized void ensureInitialization() {
 997         try {
 998             maybeInstantiateVerifier();
 999         } catch (IOException e) {
1000             throw new RuntimeException(e);
1001         }
1002         if (jv != null && !jvInitialized) {
1003             initializeVerifier();
1004             jvInitialized = true;
1005         }
1006     }
1007 
1008     JarEntry newEntry(ZipEntry ze) {
1009         return new JarFileEntry(ze);
1010     }
1011 
1012     Enumeration<String> entryNames(CodeSource[] cs) {
1013         ensureInitialization();
1014         if (jv != null) {
1015             return jv.entryNames(this, cs);
1016         }
1017 
1018         /*
1019          * JAR file has no signed content. Is there a non-signing
1020          * code source?
1021          */
1022         boolean includeUnsigned = false;
1023         for (CodeSource c : cs) {
1024             if (c.getCodeSigners() == null) {
1025                 includeUnsigned = true;
1026                 break;
1027             }
1028         }
1029         if (includeUnsigned) {
1030             return unsignedEntryNames();
1031         } else {
1032             return new Enumeration<>() {
1033 
1034                 public boolean hasMoreElements() {
1035                     return false;
1036                 }
1037 
1038                 public String nextElement() {
1039                     throw new NoSuchElementException();
1040                 }
1041             };
1042         }
1043     }
1044 
1045     /**
1046      * Returns an enumeration of the zip file entries
1047      * excluding internal JAR mechanism entries and including
1048      * signed entries missing from the ZIP directory.
1049      */
1050     Enumeration<JarEntry> entries2() {
1051         ensureInitialization();
1052         if (jv != null) {
1053             return jv.entries2(this, super.entries());
1054         }
1055 
1056         // screen out entries which are never signed
1057         final Enumeration<? extends ZipEntry> enum_ = super.entries();
1058         return new Enumeration<>() {
1059 
1060             ZipEntry entry;
1061 
1062             public boolean hasMoreElements() {
1063                 if (entry != null) {
1064                     return true;
1065                 }
1066                 while (enum_.hasMoreElements()) {
1067                     ZipEntry ze = enum_.nextElement();
1068                     if (JarVerifier.isSigningRelated(ze.getName())) {
1069                         continue;
1070                     }
1071                     entry = ze;
1072                     return true;
1073                 }
1074                 return false;
1075             }
1076 
1077             public JarFileEntry nextElement() {
1078                 if (hasMoreElements()) {
1079                     ZipEntry ze = entry;
1080                     entry = null;
1081                     return new JarFileEntry(ze);
1082                 }
1083                 throw new NoSuchElementException();
1084             }
1085         };
1086     }
1087 
1088     CodeSource[] getCodeSources(URL url) {
1089         ensureInitialization();
1090         if (jv != null) {
1091             return jv.getCodeSources(this, url);
1092         }
1093 
1094         /*
1095          * JAR file has no signed content. Is there a non-signing
1096          * code source?
1097          */
1098         Enumeration<String> unsigned = unsignedEntryNames();
1099         if (unsigned.hasMoreElements()) {
1100             return new CodeSource[]{JarVerifier.getUnsignedCS(url)};
1101         } else {
1102             return null;
1103         }
1104     }
1105 
1106     private Enumeration<String> unsignedEntryNames() {
1107         final Enumeration<JarEntry> entries = entries();
1108         return new Enumeration<>() {
1109 
1110             String name;
1111 
1112             /*
1113              * Grab entries from ZIP directory but screen out
1114              * metadata.
1115              */
1116             public boolean hasMoreElements() {
1117                 if (name != null) {
1118                     return true;
1119                 }
1120                 while (entries.hasMoreElements()) {
1121                     String value;
1122                     ZipEntry e = entries.nextElement();
1123                     value = e.getName();
1124                     if (e.isDirectory() || JarVerifier.isSigningRelated(value)) {
1125                         continue;
1126                     }
1127                     name = value;
1128                     return true;
1129                 }
1130                 return false;
1131             }
1132 
1133             public String nextElement() {
1134                 if (hasMoreElements()) {
1135                     String value = name;
1136                     name = null;
1137                     return value;
1138                 }
1139                 throw new NoSuchElementException();
1140             }
1141         };
1142     }
1143 
1144     CodeSource getCodeSource(URL url, String name) {
1145         ensureInitialization();
1146         if (jv != null) {
1147             if (jv.eagerValidation) {
1148                 CodeSource cs = null;
1149                 JarEntry je = getJarEntry(name);
1150                 if (je != null) {
1151                     cs = jv.getCodeSource(url, this, je);
1152                 } else {
1153                     cs = jv.getCodeSource(url, name);
1154                 }
1155                 return cs;
1156             } else {
1157                 return jv.getCodeSource(url, name);
1158             }
1159         }
1160 
1161         return JarVerifier.getUnsignedCS(url);
1162     }
1163 
1164     void setEagerValidation(boolean eager) {
1165         try {
1166             maybeInstantiateVerifier();
1167         } catch (IOException e) {
1168             throw new RuntimeException(e);
1169         }
1170         if (jv != null) {
1171             jv.setEagerValidation(eager);
1172         }
1173     }
1174 
1175     List<Object> getManifestDigests() {
1176         ensureInitialization();
1177         if (jv != null) {
1178             return jv.getManifestDigests();
1179         }
1180         return new ArrayList<>();
1181     }
1182 }