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     // The good suffix shift for "class-path: "
 843     private static final byte[] CLASSPATH_OPTOSFT;
 844 
 845     private static final byte[] MULTIRELEASE_CHARS =
 846             {'M','U','L','T','I','-','R','E','L','E', 'A', 'S', 'E', ':',
 847                     ' ', 'T', 'R', 'U', 'E'};
 848 
 849     // The bad character shift for "multi-release: true"
 850     private static final byte[] MULTIRELEASE_LASTOCC;
 851 
 852     // The good suffix shift for "multi-release: true"
 853     private static final byte[] MULTIRELEASE_OPTOSFT;
 854 
 855     static {
 856         CLASSPATH_LASTOCC = new byte[64];
 857         CLASSPATH_OPTOSFT = new byte[12];
 858         CLASSPATH_LASTOCC[(int)'C' - 32] = 1;
 859         CLASSPATH_LASTOCC[(int)'L' - 32] = 2;
 860         CLASSPATH_LASTOCC[(int)'S' - 32] = 5;
 861         CLASSPATH_LASTOCC[(int)'-' - 32] = 6;
 862         CLASSPATH_LASTOCC[(int)'P' - 32] = 7;
 863         CLASSPATH_LASTOCC[(int)'A' - 32] = 8;
 864         CLASSPATH_LASTOCC[(int)'T' - 32] = 9;
 865         CLASSPATH_LASTOCC[(int)'H' - 32] = 10;
 866         CLASSPATH_LASTOCC[(int)':' - 32] = 11;
 867         CLASSPATH_LASTOCC[(int)' ' - 32] = 12;
 868         for (int i = 0; i < 11; i++) {
 869             CLASSPATH_OPTOSFT[i] = 12;
 870         }
 871         CLASSPATH_OPTOSFT[11] = 1;
 872 
 873         MULTIRELEASE_LASTOCC = new byte[64];
 874         MULTIRELEASE_OPTOSFT = new byte[19];
 875         MULTIRELEASE_LASTOCC[(int)'M' - 32] = 1;
 876         MULTIRELEASE_LASTOCC[(int)'I' - 32] = 5;
 877         MULTIRELEASE_LASTOCC[(int)'-' - 32] = 6;
 878         MULTIRELEASE_LASTOCC[(int)'L' - 32] = 9;
 879         MULTIRELEASE_LASTOCC[(int)'A' - 32] = 11;
 880         MULTIRELEASE_LASTOCC[(int)'S' - 32] = 12;
 881         MULTIRELEASE_LASTOCC[(int)':' - 32] = 14;
 882         MULTIRELEASE_LASTOCC[(int)' ' - 32] = 15;
 883         MULTIRELEASE_LASTOCC[(int)'T' - 32] = 16;
 884         MULTIRELEASE_LASTOCC[(int)'R' - 32] = 17;
 885         MULTIRELEASE_LASTOCC[(int)'U' - 32] = 18;
 886         MULTIRELEASE_LASTOCC[(int)'E' - 32] = 19;
 887         for (int i = 0; i < 17; i++) {
 888             MULTIRELEASE_OPTOSFT[i] = 19;
 889         }
 890         MULTIRELEASE_OPTOSFT[17] = 6;
 891         MULTIRELEASE_OPTOSFT[18] = 1;
 892     }
 893 
 894     private JarEntry getManEntry() {
 895         if (manEntry == null) {
 896             // First look up manifest entry using standard name
 897             ZipEntry manEntry = super.getEntry(MANIFEST_NAME);
 898             if (manEntry == null) {
 899                 // If not found, then iterate through all the "META-INF/"
 900                 // entries to find a match.
 901                 String[] names = getMetaInfEntryNames();
 902                 if (names != null) {
 903                     for (String name : names) {
 904                         if (MANIFEST_NAME.equals(name.toUpperCase(Locale.ENGLISH))) {
 905                             manEntry = super.getEntry(name);
 906                             break;
 907                         }
 908                     }
 909                 }
 910             }
 911             this.manEntry = (manEntry == null)
 912                     ? null
 913                     : new JarFileEntry(manEntry.getName(), manEntry);
 914         }
 915         return manEntry;
 916     }
 917 
 918    /**
 919     * Returns {@code true} iff this JAR file has a manifest with the
 920     * Class-Path attribute
 921     */
 922     boolean hasClassPathAttribute() throws IOException {
 923         checkForSpecialAttributes();
 924         return hasClassPathAttribute;
 925     }
 926 
 927     /**
 928      * Returns true if the pattern {@code src} is found in {@code b}.
 929      * The {@code lastOcc} array is the precomputed bad character shifts.
 930      * Since there are no repeated substring in our search strings,
 931      * the good suffix shifts can be replaced with a comparison.
 932      */
 933     private int match(byte[] src, byte[] b, byte[] lastOcc, byte[] optoSft) {
 934         int len = src.length;
 935         int last = b.length - len;
 936         int i = 0;
 937         next:
 938         while (i <= last) {
 939             for (int j = (len - 1); j >= 0; j--) {
 940                 byte c = b[i + j];
 941                 if (c >= ' ' && c <= 'z') {
 942                     if (c >= 'a') c -= 32; // Canonicalize
 943 
 944                     if (c != src[j]) {
 945                         // no match
 946                         int badShift = lastOcc[c - 32];
 947                         i += Math.max(j + 1 - badShift, optoSft[j]);
 948                         continue next;
 949                     }
 950                 } else {
 951                     // no match, character not valid for name
 952                     i += len;
 953                     continue next;
 954                 }
 955             }
 956             return i;
 957         }
 958         return -1;
 959     }
 960 
 961     /**
 962      * On first invocation, check if the JAR file has the Class-Path
 963      * and the Multi-Release attribute. A no-op on subsequent calls.
 964      */
 965     private void checkForSpecialAttributes() throws IOException {
 966         if (hasCheckedSpecialAttributes) {
 967             return;
 968         }
 969         synchronized (this) {
 970             if (hasCheckedSpecialAttributes) {
 971                 return;
 972             }
 973             JarEntry manEntry = getManEntry();
 974             if (manEntry != null) {
 975                 byte[] b = getBytes(manEntry);
 976                 hasClassPathAttribute = match(CLASSPATH_CHARS, b,
 977                         CLASSPATH_LASTOCC, CLASSPATH_OPTOSFT) != -1;
 978                 // is this a multi-release jar file
 979                 if (MULTI_RELEASE_ENABLED) {
 980                     int i = match(MULTIRELEASE_CHARS, b, MULTIRELEASE_LASTOCC,
 981                             MULTIRELEASE_OPTOSFT);
 982                     if (i != -1) {
 983                         i += MULTIRELEASE_CHARS.length;
 984                         if (i < b.length) {
 985                             byte c = b[i++];
 986                             // Check that the value is followed by a newline
 987                             // and does not have a continuation
 988                             if (c == '\n' &&
 989                                     (i == b.length || b[i] != ' ')) {
 990                                 isMultiRelease = true;
 991                             } else if (c == '\r') {
 992                                 if (i == b.length) {
 993                                     isMultiRelease = true;
 994                                 } else {
 995                                     c = b[i++];
 996                                     if (c == '\n') {
 997                                         if (i == b.length || b[i] != ' ') {
 998                                             isMultiRelease = true;
 999                                         }
1000                                     } else if (c != ' ') {
1001                                         isMultiRelease = true;
1002                                     }
1003                                 }
1004                             }
1005                         }
1006                     }
1007                 }
1008             }
1009             hasCheckedSpecialAttributes = true;
1010         }
1011     }
1012 
1013     private synchronized void ensureInitialization() {
1014         try {
1015             maybeInstantiateVerifier();
1016         } catch (IOException e) {
1017             throw new RuntimeException(e);
1018         }
1019         if (jv != null && !jvInitialized) {
1020             initializeVerifier();
1021             jvInitialized = true;
1022         }
1023     }
1024 
1025     JarEntry newEntry(ZipEntry ze) {
1026         return new JarFileEntry(ze);
1027     }
1028 
1029     Enumeration<String> entryNames(CodeSource[] cs) {
1030         ensureInitialization();
1031         if (jv != null) {
1032             return jv.entryNames(this, cs);
1033         }
1034 
1035         /*
1036          * JAR file has no signed content. Is there a non-signing
1037          * code source?
1038          */
1039         boolean includeUnsigned = false;
1040         for (CodeSource c : cs) {
1041             if (c.getCodeSigners() == null) {
1042                 includeUnsigned = true;
1043                 break;
1044             }
1045         }
1046         if (includeUnsigned) {
1047             return unsignedEntryNames();
1048         } else {
1049             return new Enumeration<>() {
1050 
1051                 public boolean hasMoreElements() {
1052                     return false;
1053                 }
1054 
1055                 public String nextElement() {
1056                     throw new NoSuchElementException();
1057                 }
1058             };
1059         }
1060     }
1061 
1062     /**
1063      * Returns an enumeration of the zip file entries
1064      * excluding internal JAR mechanism entries and including
1065      * signed entries missing from the ZIP directory.
1066      */
1067     Enumeration<JarEntry> entries2() {
1068         ensureInitialization();
1069         if (jv != null) {
1070             return jv.entries2(this, super.entries());
1071         }
1072 
1073         // screen out entries which are never signed
1074         final Enumeration<? extends ZipEntry> enum_ = super.entries();
1075         return new Enumeration<>() {
1076 
1077             ZipEntry entry;
1078 
1079             public boolean hasMoreElements() {
1080                 if (entry != null) {
1081                     return true;
1082                 }
1083                 while (enum_.hasMoreElements()) {
1084                     ZipEntry ze = enum_.nextElement();
1085                     if (JarVerifier.isSigningRelated(ze.getName())) {
1086                         continue;
1087                     }
1088                     entry = ze;
1089                     return true;
1090                 }
1091                 return false;
1092             }
1093 
1094             public JarFileEntry nextElement() {
1095                 if (hasMoreElements()) {
1096                     ZipEntry ze = entry;
1097                     entry = null;
1098                     return new JarFileEntry(ze);
1099                 }
1100                 throw new NoSuchElementException();
1101             }
1102         };
1103     }
1104 
1105     CodeSource[] getCodeSources(URL url) {
1106         ensureInitialization();
1107         if (jv != null) {
1108             return jv.getCodeSources(this, url);
1109         }
1110 
1111         /*
1112          * JAR file has no signed content. Is there a non-signing
1113          * code source?
1114          */
1115         Enumeration<String> unsigned = unsignedEntryNames();
1116         if (unsigned.hasMoreElements()) {
1117             return new CodeSource[]{JarVerifier.getUnsignedCS(url)};
1118         } else {
1119             return null;
1120         }
1121     }
1122 
1123     private Enumeration<String> unsignedEntryNames() {
1124         final Enumeration<JarEntry> entries = entries();
1125         return new Enumeration<>() {
1126 
1127             String name;
1128 
1129             /*
1130              * Grab entries from ZIP directory but screen out
1131              * metadata.
1132              */
1133             public boolean hasMoreElements() {
1134                 if (name != null) {
1135                     return true;
1136                 }
1137                 while (entries.hasMoreElements()) {
1138                     String value;
1139                     ZipEntry e = entries.nextElement();
1140                     value = e.getName();
1141                     if (e.isDirectory() || JarVerifier.isSigningRelated(value)) {
1142                         continue;
1143                     }
1144                     name = value;
1145                     return true;
1146                 }
1147                 return false;
1148             }
1149 
1150             public String nextElement() {
1151                 if (hasMoreElements()) {
1152                     String value = name;
1153                     name = null;
1154                     return value;
1155                 }
1156                 throw new NoSuchElementException();
1157             }
1158         };
1159     }
1160 
1161     CodeSource getCodeSource(URL url, String name) {
1162         ensureInitialization();
1163         if (jv != null) {
1164             if (jv.eagerValidation) {
1165                 CodeSource cs = null;
1166                 JarEntry je = getJarEntry(name);
1167                 if (je != null) {
1168                     cs = jv.getCodeSource(url, this, je);
1169                 } else {
1170                     cs = jv.getCodeSource(url, name);
1171                 }
1172                 return cs;
1173             } else {
1174                 return jv.getCodeSource(url, name);
1175             }
1176         }
1177 
1178         return JarVerifier.getUnsignedCS(url);
1179     }
1180 
1181     void setEagerValidation(boolean eager) {
1182         try {
1183             maybeInstantiateVerifier();
1184         } catch (IOException e) {
1185             throw new RuntimeException(e);
1186         }
1187         if (jv != null) {
1188             jv.setEagerValidation(eager);
1189         }
1190     }
1191 
1192     List<Object> getManifestDigests() {
1193         ensureInitialization();
1194         if (jv != null) {
1195             return jv.getManifestDigests();
1196         }
1197         return new ArrayList<>();
1198     }
1199 }