1 /*
   2  * Copyright (c) 2015, 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 jdk.security.jarsigner;
  27 
  28 import com.sun.jarsigner.ContentSigner;
  29 import com.sun.jarsigner.ContentSignerParameters;
  30 import sun.security.tools.PathList;
  31 import sun.security.tools.jarsigner.TimestampedSigner;
  32 import sun.security.util.ManifestDigester;
  33 import sun.security.util.SignatureFileVerifier;
  34 import sun.security.x509.AlgorithmId;
  35 
  36 import java.io.*;
  37 import java.net.SocketTimeoutException;
  38 import java.net.URI;
  39 import java.net.URL;
  40 import java.net.URLClassLoader;
  41 import java.security.*;
  42 import java.security.cert.CertPath;
  43 import java.security.cert.Certificate;
  44 import java.security.cert.CertificateException;
  45 import java.security.cert.X509Certificate;
  46 import java.util.*;
  47 import java.util.function.BiConsumer;
  48 import java.util.jar.Attributes;
  49 import java.util.jar.JarEntry;
  50 import java.util.jar.JarFile;
  51 import java.util.jar.Manifest;
  52 import java.util.zip.ZipEntry;
  53 import java.util.zip.ZipFile;
  54 import java.util.zip.ZipOutputStream;
  55 
  56 /**
  57  * An immutable utility class to sign a jar file.
  58  * <p>
  59  * A caller creates a {@code JarSigner.Builder} object, (optionally) sets
  60  * some parameters, and calls {@link JarSigner.Builder#build build} to create
  61  * a {@code JarSigner} object. This {@code JarSigner} object can then
  62  * be used to sign a jar file.
  63  * <p>
  64  * Unless otherwise stated, calling a method of {@code JarSigner} or
  65  * {@code JarSigner.Builder} with a null argument will throw
  66  * a {@link NullPointerException}.
  67  * <p>
  68  * Example:
  69  * <pre>
  70  * JarSigner signer = new JarSigner.Builder(key, certPath)
  71  *         .digestAlgorithm("SHA-1")
  72  *         .signatureAlgorithm("SHA1withDSA")
  73  *         .build();
  74  * try (ZipFile in = new ZipFile(inputFile);
  75  *         FileOutputStream out = new FileOutputStream(outputFile)) {
  76  *     signer.sign(in, out);
  77  * }
  78  * </pre>
  79  *
  80  * @since 1.9
  81  */
  82 public final class JarSigner {
  83 
  84     /**
  85      * A mutable builder class that can create an immutable {@code JarSigner}
  86      * from various signing-related parameters.
  87      *
  88      * @since 1.9
  89      */
  90     public static class Builder {
  91 
  92         // Signer materials:
  93         final PrivateKey privateKey;
  94         final X509Certificate[] certChain;
  95 
  96         // JarSigner options:
  97         // Support multiple digestalg internally. Can be null, but not empty
  98         String[] digestalg;
  99         String sigalg;
 100         // Precisely should be one provider for each digestalg, maybe later
 101         Provider digestProvider;
 102         Provider sigProvider;
 103         URI tsaUrl;
 104         String signerName;
 105         BiConsumer<String,String> handler;
 106 
 107         // Implementation-specific properties:
 108         String tSAPolicyID;
 109         String tSADigestAlg;
 110         boolean signManifest = true;
 111         boolean externalSF = true;
 112         String altSignerPath;
 113         String altSigner;
 114 
 115         /**
 116          * Creates a {@code JarSigner.Builder} object with
 117          * a {@link KeyStore.PrivateKeyEntry} object.
 118          *
 119          * @param entry the {@link KeyStore.PrivateKeyEntry} of the signer.
 120          */
 121         public Builder(KeyStore.PrivateKeyEntry entry) {
 122             this.privateKey = entry.getPrivateKey();
 123             try {
 124                 // called internally, no need to clone
 125                 Certificate[] certs = entry.getCertificateChain();
 126                 this.certChain = Arrays.copyOf(certs, certs.length,
 127                         X509Certificate[].class);
 128             } catch (ArrayStoreException ase) {
 129                 // Wrong type, not X509Certificate. Won't document.
 130                 throw new IllegalArgumentException(
 131                         "Entry does not contain X509Certificate");
 132             }
 133         }
 134 
 135         /**
 136          * Creates a {@code JarSigner.Builder} object with a private key and
 137          * a certification path.
 138          *
 139          * @param privateKey the private key of the signer.
 140          * @param certPath the certification path of the signer.
 141          * @throws IllegalArgumentException if {@code certPath} is empty, or
 142          *      the {@code privateKey} algorithm does not match the algorithm
 143          *      of the {@code PublicKey} in the end entity certificate
 144          *      (the first certificate in {@code certPath}).
 145          */
 146         public Builder(PrivateKey privateKey, CertPath certPath) {
 147             List<? extends Certificate> certs = certPath.getCertificates();
 148             if (certs.isEmpty()) {
 149                 throw new IllegalArgumentException("certPath cannot be empty");
 150             }
 151             if (!privateKey.getAlgorithm().equals
 152                     (certs.get(0).getPublicKey().getAlgorithm())) {
 153                 throw new IllegalArgumentException
 154                         ("private key algorithm does not match " +
 155                                 "algorithm of public key in end entity " +
 156                                 "certificate (the 1st in certPath)");
 157             }
 158             this.privateKey = privateKey;
 159             try {
 160                 this.certChain = certs.toArray(new X509Certificate[certs.size()]);
 161             } catch (ArrayStoreException ase) {
 162                 // Wrong type, not X509Certificate.
 163                 throw new IllegalArgumentException(
 164                         "Entry does not contain X509Certificate");
 165             }
 166         }
 167 
 168         /**
 169          * Sets the digest algorithm. If no digest algorithm is specified,
 170          * the default algorithm returned by {@link #getDefaultDigestAlgorithm}
 171          * will be used.
 172          *
 173          * @param algorithm the standard name of the algorithm. See
 174          *      the {@code MessageDigest} section in the <a href=
 175          *      "{@docRoot}/../technotes/guides/security/StandardNames.html#MessageDigest">
 176          *      Java Cryptography Architecture Standard Algorithm Name
 177          *      Documentation</a> for information about standard algorithm names.
 178          * @return the {@code JarSigner.Builder} itself.
 179          * @throws NoSuchAlgorithmException if {@code algorithm} is not available.
 180          */
 181         public Builder digestAlgorithm(String algorithm) throws NoSuchAlgorithmException {
 182             MessageDigest.getInstance(Objects.requireNonNull(algorithm));
 183             this.digestalg = new String[]{algorithm};
 184             this.digestProvider = null;
 185             return this;
 186         }
 187 
 188         /**
 189          * Sets the digest algorithm from the specified provider.
 190          * If no digest algorithm is specified, the default algorithm
 191          * returned by {@link #getDefaultDigestAlgorithm} will be used.
 192          *
 193          * @param algorithm the standard name of the algorithm. See
 194          *      the {@code MessageDigest} section in the <a href=
 195          *      "{@docRoot}/../technotes/guides/security/StandardNames.html#MessageDigest">
 196          *      Java Cryptography Architecture Standard Algorithm Name
 197          *      Documentation</a> for information about standard algorithm names.
 198          * @param provider the provider.
 199          * @return the {@code JarSigner.Builder} itself.
 200          * @throws NoSuchAlgorithmException if {@code algorithm} is not
 201          *      available in the specified provider.
 202          */
 203         public Builder digestAlgorithm(String algorithm, Provider provider)
 204                 throws NoSuchAlgorithmException {
 205             MessageDigest.getInstance(
 206                     Objects.requireNonNull(algorithm),
 207                     Objects.requireNonNull(provider));
 208             this.digestalg = new String[]{algorithm};
 209             this.digestProvider = provider;
 210             return this;
 211         }
 212 
 213         /**
 214          * Sets the signature algorithm. If no signature algorithm
 215          * is specified, the default signature algorithm returned by
 216          * {@link #getDefaultSignatureAlgorithm} for the private key
 217          * will be used.
 218          *
 219          * @param algorithm the standard name of the algorithm. See
 220          *      the {@code Signature} section in the <a href=
 221          *      "{@docRoot}/../technotes/guides/security/StandardNames.html#Signature">
 222          *      Java Cryptography Architecture Standard Algorithm Name
 223          *      Documentation</a> for information about standard algorithm names.
 224          * @return the {@code JarSigner.Builder} itself.
 225          * @throws NoSuchAlgorithmException if {@code algorithm} is not available.
 226          * @throws IllegalArgumentException if {@code algorithm} is not
 227          *      compatible with the algorithm of the signer's private key.
 228          */
 229         public Builder signatureAlgorithm(String algorithm)
 230                 throws NoSuchAlgorithmException {
 231             // Check availability
 232             Signature.getInstance(Objects.requireNonNull(algorithm));
 233             AlgorithmId.checkKeyAndSigAlgMatch(
 234                     privateKey.getAlgorithm(), algorithm);
 235             this.sigalg = algorithm;
 236             this.sigProvider = null;
 237             return this;
 238         }
 239 
 240         /**
 241          * Sets the signature algorithm from the specified provider. If no
 242          * signature algorithm is specified, the default signature algorithm
 243          * returned by {@link #getDefaultSignatureAlgorithm} for the private
 244          * key will be used.
 245          *
 246          * @param algorithm the standard name of the algorithm. See
 247          *      the {@code Signature} section in the <a href=
 248          *      "{@docRoot}/../technotes/guides/security/StandardNames.html#Signature">
 249          *      Java Cryptography Architecture Standard Algorithm Name
 250          *      Documentation</a> for information about standard algorithm names.
 251          * @param provider  the provider.
 252          * @return the {@code JarSigner.Builder} itself.
 253          * @throws NoSuchAlgorithmException if {@code algorithm} is not
 254          *      available in the specified provider.
 255          * @throws IllegalArgumentException if {@code algorithm} is not
 256          *      compatible with the algorithm of the signer's private key.
 257          */
 258         public Builder signatureAlgorithm(String algorithm, Provider provider)
 259                 throws NoSuchAlgorithmException {
 260             // Check availability
 261             Signature.getInstance(
 262                     Objects.requireNonNull(algorithm),
 263                     Objects.requireNonNull(provider));
 264             AlgorithmId.checkKeyAndSigAlgMatch(
 265                     privateKey.getAlgorithm(), algorithm);
 266             this.sigalg = algorithm;
 267             this.sigProvider = provider;
 268             return this;
 269         }
 270 
 271         /**
 272          * Sets the URI of the Time Stamping Authority (TSA).
 273          *
 274          * @param uri the URI.
 275          * @return the {@code JarSigner.Builder} itself.
 276          */
 277         public Builder tsa(URI uri) {
 278             this.tsaUrl = Objects.requireNonNull(uri);
 279             return this;
 280         }
 281 
 282         /**
 283          * Sets the signer name. The name will be used as the base name for
 284          * the signature files. All lowercase characters will be converted to
 285          * uppercase for signature file names. If a signer name is not
 286          * specified, the string "SIGNER" will be used.
 287          *
 288          * @param name the signer name.
 289          * @return the {@code JarSigner.Builder} itself.
 290          * @throws IllegalArgumentException if {@code name} is empty or has
 291          *      a size bigger than 8, or it contains characters not from the
 292          *      set "a-zA-Z0-9_-".
 293          */
 294         public Builder signerName(String name) {
 295             if (name.isEmpty() || name.length() > 8) {
 296                 throw new IllegalArgumentException("Name too long");
 297             }
 298 
 299             name = name.toUpperCase(Locale.ENGLISH);
 300 
 301             for (int j = 0; j < name.length(); j++) {
 302                 char c = name.charAt(j);
 303                 if (!
 304                         ((c >= 'A' && c <= 'Z') ||
 305                                 (c >= '0' && c <= '9') ||
 306                                 (c == '-') ||
 307                                 (c == '_'))) {
 308                     throw new IllegalArgumentException(
 309                             "Invalid characters in name");
 310                 }
 311             }
 312             this.signerName = name;
 313             return this;
 314         }
 315 
 316         /**
 317          * Sets en event handler that will be triggered when a {@link JarEntry}
 318          * is to be added, signed, or updated during the signing process.
 319          * <p>
 320          * The handler can be used to display signing progress. The first
 321          * argument of the handler can be "adding", "signing", or "updating",
 322          * and the second argument is the name of the {@link JarEntry}
 323          * being processed.
 324          *
 325          * @param handler the event handler.
 326          * @return the {@code JarSigner.Builder} itself.
 327          */
 328         public Builder eventHandler(BiConsumer<String,String> handler) {
 329             this.handler = Objects.requireNonNull(handler);
 330             return this;
 331         }
 332 
 333         /**
 334          * Sets an additional implementation-specific property indicated by
 335          * the specified key.
 336          *
 337          * @implNote This implementation supports the following properties:
 338          * <ul>
 339          * <li>"tsaDigestAlg": algorithm of digest data in the timestamping
 340          * request. The default value is the same as the result of
 341          * {@link #getDefaultDigestAlgorithm}.
 342          * <li>"tsaPolicyId": TSAPolicyID for Timestamping Authority.
 343          * No default value.
 344          * <li>"internalsf": "true" if the .SF file is included inside the
 345          * signature block, "false" otherwise. Default "false".
 346          * <li>"sectionsonly": "true" if the .SF file only contains the hash
 347          * value for each section of the manifest and not for the whole
 348          * manifest, "false" otherwise. Default "false".
 349          * </ul>
 350          * All property names are case-insensitive.
 351          *
 352          * @param key the name of the property.
 353          * @param value the value of the property.
 354          * @return the {@code JarSigner.Builder} itself.
 355          * @throws UnsupportedOperationException if the key is not supported
 356          *      by this implementation.
 357          * @throws IllegalArgumentException if the value is not accepted as
 358          *      a legal value for this key.
 359          */
 360         public Builder setProperty(String key, String value) {
 361             Objects.requireNonNull(key);
 362             Objects.requireNonNull(value);
 363             switch (key.toLowerCase(Locale.US)) {
 364                 case "tsadigestalg":
 365                     try {
 366                         MessageDigest.getInstance(value);
 367                     } catch (NoSuchAlgorithmException nsae) {
 368                         throw new IllegalArgumentException(
 369                                 "Invalid tsadigestalg", nsae);
 370                     }
 371                     this.tSADigestAlg = value;
 372                     break;
 373                 case "tsapolicyid":
 374                     this.tSAPolicyID = value;
 375                     break;
 376                 case "internalsf":
 377                     switch (value) {
 378                         case "true":
 379                             externalSF = false;
 380                             break;
 381                         case "false":
 382                             externalSF = true;
 383                             break;
 384                         default:
 385                             throw new IllegalArgumentException(
 386                                 "Invalid internalsf value");
 387                     }
 388                     break;
 389                 case "sectionsonly":
 390                     switch (value) {
 391                         case "true":
 392                             signManifest = false;
 393                             break;
 394                         case "false":
 395                             signManifest = true;
 396                             break;
 397                         default:
 398                             throw new IllegalArgumentException(
 399                                 "Invalid signManifest value");
 400                     }
 401                     break;
 402                 case "altsignerpath":
 403                     altSignerPath = value;
 404                     break;
 405                 case "altsigner":
 406                     altSigner = value;
 407                     break;
 408                 default:
 409                     throw new UnsupportedOperationException(
 410                             "Unsupported key " + key);
 411             }
 412             return this;
 413         }
 414 
 415         /**
 416          * Gets the default digest algorithm.
 417          *
 418          * @implNote This implementation returns "SHA-256". The value may
 419          * change in the future.
 420          *
 421          * @return the default digest algorithm.
 422          */
 423         public static String getDefaultDigestAlgorithm() {
 424             return "SHA-256";
 425         }
 426 
 427         /**
 428          * Gets the default signature algorithm for a private key.
 429          * For example, SHA256withRSA for a 2048-bit RSA key, and
 430          * SHA384withECDSA for a 384-bit EC key.
 431          *
 432          * @implNote This implementation makes use of comparable strengths
 433          * as defined in Tables 2 and 3 of NIST SP 800-57 Part 1-Rev.3.
 434          * Specifically, if a DSA or RSA key with a key size greater than 7680
 435          * bits, or an EC key with a key size greater than or equal to 512 bits,
 436          * SHA-512 will be used as the hash function for the signature.
 437          * If a DSA or RSA key has a key size greater than 3072 bits, or an
 438          * EC key has a key size greater than or equal to 384 bits, SHA-384 will
 439          * be used. Otherwise, SHA-256 will be used. The value may
 440          * change in the future.
 441          *
 442          * @param key the private key.
 443          * @return the default signature algorithm. Returns null if a default
 444          *      signature algorithm cannot be found. In this case,
 445          *      {@link #signatureAlgorithm} must be called to specify a
 446          *      signature algorithm. Otherwise, the {@link #build} method
 447          *      will throw an {@link IllegalArgumentException}.
 448          */
 449         public static String getDefaultSignatureAlgorithm(PrivateKey key) {
 450             return AlgorithmId.getDefaultSigAlgForKey(Objects.requireNonNull(key));
 451         }
 452 
 453         /**
 454          * Builds a {@code JarSigner} object from the parameters set by the
 455          * setter methods.
 456          * <p>
 457          * This method does not modify internal state of this {@code Builder}
 458          * object and can be called multiple times to generate multiple
 459          * {@code JarSigner} objects. After this method is called, calling
 460          * any method on this {@code Builder} will have no effect on
 461          * the newly built {@code JarSigner} object.
 462          *
 463          * @return the {@code JarSigner} object.
 464          * @throws IllegalArgumentException if a signature algorithm is not
 465          *      set and cannot be derived from the private key using the
 466          *      {@link #getDefaultSignatureAlgorithm} method.
 467          */
 468         public JarSigner build() {
 469             return new JarSigner(this);
 470         }
 471     }
 472 
 473     private static final String META_INF = "META-INF/";
 474 
 475     // All fields in Builder are duplicated here as final. Those not
 476     // provided but has a default value will be filled with default value.
 477 
 478     // Precisely, a final array field can still be modified if only
 479     // reference is copied, no clone is done because we are concerned about
 480     // casual change instead of malicious attack.
 481 
 482     // Signer materials:
 483     private final PrivateKey privateKey;
 484     private final X509Certificate[] certChain;
 485 
 486     // JarSigner options:
 487     private final String[] digestalg;
 488     private final String sigalg;
 489     private final Provider digestProvider;
 490     private final Provider sigProvider;
 491     private final URI tsaUrl;
 492     private final String signerName;
 493     private final BiConsumer<String,String> handler;
 494 
 495     // Implementation-specific properties:
 496     private final String tSAPolicyID;
 497     private final String tSADigestAlg;
 498     private final boolean signManifest; // "sign" the whole manifest
 499     private final boolean externalSF; // leave the .SF out of the PKCS7 block
 500     private final String altSignerPath;
 501     private final String altSigner;
 502 
 503     private JarSigner(JarSigner.Builder builder) {
 504 
 505         this.privateKey = builder.privateKey;
 506         this.certChain = builder.certChain;
 507         if (builder.digestalg != null) {
 508             // No need to clone because builder only accepts one alg now
 509             this.digestalg = builder.digestalg;
 510         } else {
 511             this.digestalg = new String[] {
 512                     Builder.getDefaultDigestAlgorithm() };
 513         }
 514         this.digestProvider = builder.digestProvider;
 515         if (builder.sigalg != null) {
 516             this.sigalg = builder.sigalg;
 517         } else {
 518             this.sigalg = JarSigner.Builder
 519                     .getDefaultSignatureAlgorithm(privateKey);
 520             if (this.sigalg == null) {
 521                 throw new IllegalArgumentException(
 522                         "No signature alg for " + privateKey.getAlgorithm());
 523             }
 524         }
 525         this.sigProvider = builder.sigProvider;
 526         this.tsaUrl = builder.tsaUrl;
 527 
 528         if (builder.signerName == null) {
 529             this.signerName = "SIGNER";
 530         } else {
 531             this.signerName = builder.signerName;
 532         }
 533         this.handler = builder.handler;
 534 
 535         if (builder.tSADigestAlg != null) {
 536             this.tSADigestAlg = builder.tSADigestAlg;
 537         } else {
 538             this.tSADigestAlg = Builder.getDefaultDigestAlgorithm();
 539         }
 540         this.tSAPolicyID = builder.tSAPolicyID;
 541         this.signManifest = builder.signManifest;
 542         this.externalSF = builder.externalSF;
 543         this.altSigner = builder.altSigner;
 544         this.altSignerPath = builder.altSignerPath;
 545     }
 546 
 547     /**
 548      * Signs a file into an {@link OutputStream}. This method will not close
 549      * {@code file} or {@code os}.
 550      *
 551      * @param file the file to sign.
 552      * @param os the output stream.
 553      * @throws JarSignerException if the signing fails.
 554      */
 555     public void sign(ZipFile file, OutputStream os) {
 556         try {
 557             sign0(Objects.requireNonNull(file),
 558                     Objects.requireNonNull(os));
 559         } catch (SocketTimeoutException | CertificateException e) {
 560             // CertificateException is thrown when the received cert from TSA
 561             // has no id-kp-timeStamping in its Extended Key Usages extension.
 562             throw new JarSignerException("Error applying timestamp", e);
 563         } catch (IOException ioe) {
 564             throw new JarSignerException("I/O error", ioe);
 565         } catch (NoSuchAlgorithmException | InvalidKeyException e) {
 566             throw new JarSignerException("Error in signer materials", e);
 567         } catch (SignatureException se) {
 568             throw new JarSignerException("Error creating signature", se);
 569         }
 570     }
 571 
 572     /**
 573      * Returns the digest algorithm for this {@code JarSigner}.
 574      * <p>
 575      * The return value is never null.
 576      *
 577      * @return the digest algorithm.
 578      */
 579     public String getDigestAlgorithm() {
 580         return digestalg[0];
 581     }
 582 
 583     /**
 584      * Returns the signature algorithm for this {@code JarSigner}.
 585      * <p>
 586      * The return value is never null.
 587      *
 588      * @return the signature algorithm.
 589      */
 590     public String getSignatureAlgorithm() {
 591         return sigalg;
 592     }
 593 
 594     /**
 595      * Returns the URI of the Time Stamping Authority (TSA).
 596      *
 597      * @return the URI of the TSA.
 598      */
 599     public URI getTsa() {
 600         return tsaUrl;
 601     }
 602 
 603     /**
 604      * Returns the signer name of this {@code JarSigner}.
 605      * <p>
 606      * The return value is never null.
 607      *
 608      * @return the signer name.
 609      */
 610     public String getSignerName() {
 611         return signerName;
 612     }
 613 
 614     /**
 615      * Returns the value of an additional implementation-specific property
 616      * indicated by the specified key. If a property is not set but has a
 617      * default value, the default value will be returned.
 618      *
 619      * @implNote See {@link JarSigner.Builder#setProperty} for a list of
 620      * properties this implementation supports. All property names are
 621      * case-insensitive.
 622      *
 623      * @param key the name of the property.
 624      * @return the value for the property.
 625      * @throws UnsupportedOperationException if the key is not supported
 626      *      by this implementation.
 627      */
 628     public String getProperty(String key) {
 629         Objects.requireNonNull(key);
 630         switch (key.toLowerCase(Locale.US)) {
 631             case "tsadigestalg":
 632                 return tSADigestAlg;
 633             case "tsapolicyid":
 634                 return tSAPolicyID;
 635             case "internalsf":
 636                 return Boolean.toString(!externalSF);
 637             case "sectionsonly":
 638                 return Boolean.toString(!signManifest);
 639             case "altsignerpath":
 640                 return altSignerPath;
 641             case "altsigner":
 642                 return altSigner;
 643             default:
 644                 throw new UnsupportedOperationException(
 645                         "Unsupported key " + key);
 646         }
 647     }
 648 
 649     private void sign0(ZipFile zipFile, OutputStream os)
 650             throws IOException, CertificateException, NoSuchAlgorithmException,
 651             SignatureException, InvalidKeyException {
 652         MessageDigest[] digests;
 653         try {
 654             digests = new MessageDigest[digestalg.length];
 655             for (int i = 0; i < digestalg.length; i++) {
 656                 if (digestProvider == null) {
 657                     digests[i] = MessageDigest.getInstance(digestalg[i]);
 658                 } else {
 659                     digests[i] = MessageDigest.getInstance(
 660                             digestalg[i], digestProvider);
 661                 }
 662             }
 663         } catch (NoSuchAlgorithmException asae) {
 664             // Should not happen. User provided alg were checked, and default
 665             // alg should always be available.
 666             throw new AssertionError(asae);
 667         }
 668 
 669         PrintStream ps = new PrintStream(os);
 670         ZipOutputStream zos = new ZipOutputStream(ps);
 671 
 672         Manifest manifest = new Manifest();
 673         Map<String, Attributes> mfEntries = manifest.getEntries();
 674 
 675         // The Attributes of manifest before updating
 676         Attributes oldAttr = null;
 677 
 678         boolean mfModified = false;
 679         boolean mfCreated = false;
 680         byte[] mfRawBytes = null;
 681 
 682         // Check if manifest exists
 683         ZipEntry mfFile;
 684         if ((mfFile = getManifestFile(zipFile)) != null) {
 685             // Manifest exists. Read its raw bytes.
 686             mfRawBytes = zipFile.getInputStream(mfFile).readAllBytes();
 687             manifest.read(new ByteArrayInputStream(mfRawBytes));
 688             oldAttr = (Attributes) (manifest.getMainAttributes().clone());
 689         } else {
 690             // Create new manifest
 691             Attributes mattr = manifest.getMainAttributes();
 692             mattr.putValue(Attributes.Name.MANIFEST_VERSION.toString(),
 693                     "1.0");
 694             String javaVendor = System.getProperty("java.vendor");
 695             String jdkVersion = System.getProperty("java.version");
 696             mattr.putValue("Created-By", jdkVersion + " (" + javaVendor
 697                     + ")");
 698             mfFile = new ZipEntry(JarFile.MANIFEST_NAME);
 699             mfCreated = true;
 700         }
 701 
 702         /*
 703          * For each entry in jar
 704          * (except for signature-related META-INF entries),
 705          * do the following:
 706          *
 707          * - if entry is not contained in manifest, add it to manifest;
 708          * - if entry is contained in manifest, calculate its hash and
 709          *   compare it with the one in the manifest; if they are
 710          *   different, replace the hash in the manifest with the newly
 711          *   generated one. (This may invalidate existing signatures!)
 712          */
 713         Vector<ZipEntry> mfFiles = new Vector<>();
 714 
 715         boolean wasSigned = false;
 716 
 717         for (Enumeration<? extends ZipEntry> enum_ = zipFile.entries();
 718              enum_.hasMoreElements(); ) {
 719             ZipEntry ze = enum_.nextElement();
 720 
 721             if (ze.getName().startsWith(META_INF)) {
 722                 // Store META-INF files in vector, so they can be written
 723                 // out first
 724                 mfFiles.addElement(ze);
 725 
 726                 if (SignatureFileVerifier.isBlockOrSF(
 727                         ze.getName().toUpperCase(Locale.ENGLISH))) {
 728                     wasSigned = true;
 729                 }
 730 
 731                 if (SignatureFileVerifier.isSigningRelated(ze.getName())) {
 732                     // ignore signature-related and manifest files
 733                     continue;
 734                 }
 735             }
 736 
 737             if (manifest.getAttributes(ze.getName()) != null) {
 738                 // jar entry is contained in manifest, check and
 739                 // possibly update its digest attributes
 740                 if (updateDigests(ze, zipFile, digests,
 741                         manifest)) {
 742                     mfModified = true;
 743                 }
 744             } else if (!ze.isDirectory()) {
 745                 // Add entry to manifest
 746                 Attributes attrs = getDigestAttributes(ze, zipFile, digests);
 747                 mfEntries.put(ze.getName(), attrs);
 748                 mfModified = true;
 749             }
 750         }
 751 
 752         // Recalculate the manifest raw bytes if necessary
 753         if (mfModified) {
 754             ByteArrayOutputStream baos = new ByteArrayOutputStream();
 755             manifest.write(baos);
 756             if (wasSigned) {
 757                 byte[] newBytes = baos.toByteArray();
 758                 if (mfRawBytes != null
 759                         && oldAttr.equals(manifest.getMainAttributes())) {
 760 
 761                     /*
 762                      * Note:
 763                      *
 764                      * The Attributes object is based on HashMap and can handle
 765                      * continuation columns. Therefore, even if the contents are
 766                      * not changed (in a Map view), the bytes that it write()
 767                      * may be different from the original bytes that it read()
 768                      * from. Since the signature on the main attributes is based
 769                      * on raw bytes, we must retain the exact bytes.
 770                      */
 771 
 772                     int newPos = findHeaderEnd(newBytes);
 773                     int oldPos = findHeaderEnd(mfRawBytes);
 774 
 775                     if (newPos == oldPos) {
 776                         System.arraycopy(mfRawBytes, 0, newBytes, 0, oldPos);
 777                     } else {
 778                         // cat oldHead newTail > newBytes
 779                         byte[] lastBytes = new byte[oldPos +
 780                                 newBytes.length - newPos];
 781                         System.arraycopy(mfRawBytes, 0, lastBytes, 0, oldPos);
 782                         System.arraycopy(newBytes, newPos, lastBytes, oldPos,
 783                                 newBytes.length - newPos);
 784                         newBytes = lastBytes;
 785                     }
 786                 }
 787                 mfRawBytes = newBytes;
 788             } else {
 789                 mfRawBytes = baos.toByteArray();
 790             }
 791         }
 792 
 793         // Write out the manifest
 794         if (mfModified) {
 795             // manifest file has new length
 796             mfFile = new ZipEntry(JarFile.MANIFEST_NAME);
 797         }
 798         if (handler != null) {
 799             if (mfCreated) {
 800                 handler.accept("adding", mfFile.getName());
 801             } else if (mfModified) {
 802                 handler.accept("updating", mfFile.getName());
 803             }
 804         }
 805 
 806         zos.putNextEntry(mfFile);
 807         zos.write(mfRawBytes);
 808 
 809         // Calculate SignatureFile (".SF") and SignatureBlockFile
 810         ManifestDigester manDig = new ManifestDigester(mfRawBytes);
 811         SignatureFile sf = new SignatureFile(digests, manifest, manDig,
 812                 signerName, signManifest);
 813 
 814         byte[] block;
 815 
 816         Signature signer;
 817         if (sigProvider == null ) {
 818             signer = Signature.getInstance(sigalg);
 819         } else {
 820             signer = Signature.getInstance(sigalg, sigProvider);
 821         }
 822         signer.initSign(privateKey);
 823 
 824         ByteArrayOutputStream baos = new ByteArrayOutputStream();
 825         sf.write(baos);
 826 
 827         byte[] content = baos.toByteArray();
 828 
 829         signer.update(content);
 830         byte[] signature = signer.sign();
 831 
 832         @SuppressWarnings("deprecation")
 833         ContentSigner signingMechanism = null;
 834         if (altSigner != null) {
 835             signingMechanism = loadSigningMechanism(altSigner,
 836                     altSignerPath);
 837         }
 838 
 839         @SuppressWarnings("deprecation")
 840         ContentSignerParameters params =
 841                 new JarSignerParameters(null, tsaUrl, tSAPolicyID,
 842                         tSADigestAlg, signature,
 843                         signer.getAlgorithm(), certChain, content, zipFile);
 844         block = sf.generateBlock(params, externalSF, signingMechanism);
 845 
 846         String sfFilename = sf.getMetaName();
 847         String bkFilename = sf.getBlockName(privateKey);
 848 
 849         ZipEntry sfFile = new ZipEntry(sfFilename);
 850         ZipEntry bkFile = new ZipEntry(bkFilename);
 851 
 852         long time = System.currentTimeMillis();
 853         sfFile.setTime(time);
 854         bkFile.setTime(time);
 855 
 856         // signature file
 857         zos.putNextEntry(sfFile);
 858         sf.write(zos);
 859 
 860         if (handler != null) {
 861             if (zipFile.getEntry(sfFilename) != null) {
 862                 handler.accept("updating", sfFilename);
 863             } else {
 864                 handler.accept("adding", sfFilename);
 865             }
 866         }
 867 
 868         // signature block file
 869         zos.putNextEntry(bkFile);
 870         zos.write(block);
 871 
 872         if (handler != null) {
 873             if (zipFile.getEntry(bkFilename) != null) {
 874                 handler.accept("updating", bkFilename);
 875             } else {
 876                 handler.accept("adding", bkFilename);
 877             }
 878         }
 879 
 880         // Write out all other META-INF files that we stored in the
 881         // vector
 882         for (int i = 0; i < mfFiles.size(); i++) {
 883             ZipEntry ze = mfFiles.elementAt(i);
 884             if (!ze.getName().equalsIgnoreCase(JarFile.MANIFEST_NAME)
 885                     && !ze.getName().equalsIgnoreCase(sfFilename)
 886                     && !ze.getName().equalsIgnoreCase(bkFilename)) {
 887                 if (handler != null) {
 888                     if (manifest.getAttributes(ze.getName()) != null) {
 889                         handler.accept("signing", ze.getName());
 890                     } else if (!ze.isDirectory()) {
 891                         handler.accept("adding", ze.getName());
 892                     }
 893                 }
 894                 writeEntry(zipFile, zos, ze);
 895             }
 896         }
 897 
 898         // Write out all other files
 899         for (Enumeration<? extends ZipEntry> enum_ = zipFile.entries();
 900              enum_.hasMoreElements(); ) {
 901             ZipEntry ze = enum_.nextElement();
 902 
 903             if (!ze.getName().startsWith(META_INF)) {
 904                 if (handler != null) {
 905                     if (manifest.getAttributes(ze.getName()) != null) {
 906                         handler.accept("signing", ze.getName());
 907                     } else {
 908                         handler.accept("adding", ze.getName());
 909                     }
 910                 }
 911                 writeEntry(zipFile, zos, ze);
 912             }
 913         }
 914         zipFile.close();
 915         zos.close();
 916     }
 917 
 918     private void writeEntry(ZipFile zf, ZipOutputStream os, ZipEntry ze)
 919             throws IOException {
 920         ZipEntry ze2 = new ZipEntry(ze.getName());
 921         ze2.setMethod(ze.getMethod());
 922         ze2.setTime(ze.getTime());
 923         ze2.setComment(ze.getComment());
 924         ze2.setExtra(ze.getExtra());
 925         if (ze.getMethod() == ZipEntry.STORED) {
 926             ze2.setSize(ze.getSize());
 927             ze2.setCrc(ze.getCrc());
 928         }
 929         os.putNextEntry(ze2);
 930         writeBytes(zf, ze, os);
 931     }
 932 
 933     private void writeBytes
 934             (ZipFile zf, ZipEntry ze, ZipOutputStream os) throws IOException {
 935         try (InputStream is = zf.getInputStream(ze)) {
 936             is.transferTo(os);
 937         }
 938     }
 939 
 940     private boolean updateDigests(ZipEntry ze, ZipFile zf,
 941                                   MessageDigest[] digests,
 942                                   Manifest mf) throws IOException {
 943         boolean update = false;
 944 
 945         Attributes attrs = mf.getAttributes(ze.getName());
 946         String[] base64Digests = getDigests(ze, zf, digests);
 947 
 948         for (int i = 0; i < digests.length; i++) {
 949             // The entry name to be written into attrs
 950             String name = null;
 951             try {
 952                 // Find if the digest already exists. An algorithm could have
 953                 // different names. For example, last time it was SHA, and this
 954                 // time it's SHA-1.
 955                 AlgorithmId aid = AlgorithmId.get(digests[i].getAlgorithm());
 956                 for (Object key : attrs.keySet()) {
 957                     if (key instanceof Attributes.Name) {
 958                         String n = key.toString();
 959                         if (n.toUpperCase(Locale.ENGLISH).endsWith("-DIGEST")) {
 960                             String tmp = n.substring(0, n.length() - 7);
 961                             if (AlgorithmId.get(tmp).equals(aid)) {
 962                                 name = n;
 963                                 break;
 964                             }
 965                         }
 966                     }
 967                 }
 968             } catch (NoSuchAlgorithmException nsae) {
 969                 // Ignored. Writing new digest entry.
 970             }
 971 
 972             if (name == null) {
 973                 name = digests[i].getAlgorithm() + "-Digest";
 974                 attrs.putValue(name, base64Digests[i]);
 975                 update = true;
 976             } else {
 977                 // compare digests, and replace the one in the manifest
 978                 // if they are different
 979                 String mfDigest = attrs.getValue(name);
 980                 if (!mfDigest.equalsIgnoreCase(base64Digests[i])) {
 981                     attrs.putValue(name, base64Digests[i]);
 982                     update = true;
 983                 }
 984             }
 985         }
 986         return update;
 987     }
 988 
 989     private Attributes getDigestAttributes(
 990             ZipEntry ze, ZipFile zf, MessageDigest[] digests)
 991             throws IOException {
 992 
 993         String[] base64Digests = getDigests(ze, zf, digests);
 994         Attributes attrs = new Attributes();
 995 
 996         for (int i = 0; i < digests.length; i++) {
 997             attrs.putValue(digests[i].getAlgorithm() + "-Digest",
 998                     base64Digests[i]);
 999         }
1000         return attrs;
1001     }
1002 
1003     /*
1004      * Returns manifest entry from given jar file, or null if given jar file
1005      * does not have a manifest entry.
1006      */
1007     private ZipEntry getManifestFile(ZipFile zf) {
1008         ZipEntry ze = zf.getEntry(JarFile.MANIFEST_NAME);
1009         if (ze == null) {
1010             // Check all entries for matching name
1011             Enumeration<? extends ZipEntry> enum_ = zf.entries();
1012             while (enum_.hasMoreElements() && ze == null) {
1013                 ze = enum_.nextElement();
1014                 if (!JarFile.MANIFEST_NAME.equalsIgnoreCase
1015                         (ze.getName())) {
1016                     ze = null;
1017                 }
1018             }
1019         }
1020         return ze;
1021     }
1022 
1023     private String[] getDigests(
1024             ZipEntry ze, ZipFile zf, MessageDigest[] digests)
1025             throws IOException {
1026 
1027         int n, i;
1028         try (InputStream is = zf.getInputStream(ze)) {
1029             long left = ze.getSize();
1030             byte[] buffer = new byte[8192];
1031             while ((left > 0)
1032                     && (n = is.read(buffer, 0, buffer.length)) != -1) {
1033                 for (i = 0; i < digests.length; i++) {
1034                     digests[i].update(buffer, 0, n);
1035                 }
1036                 left -= n;
1037             }
1038         }
1039 
1040         // complete the digests
1041         String[] base64Digests = new String[digests.length];
1042         for (i = 0; i < digests.length; i++) {
1043             base64Digests[i] = Base64.getEncoder()
1044                     .encodeToString(digests[i].digest());
1045         }
1046         return base64Digests;
1047     }
1048 
1049     @SuppressWarnings("fallthrough")
1050     private int findHeaderEnd(byte[] bs) {
1051         // Initial state true to deal with empty header
1052         boolean newline = true;     // just met a newline
1053         int len = bs.length;
1054         for (int i = 0; i < len; i++) {
1055             switch (bs[i]) {
1056                 case '\r':
1057                     if (i < len - 1 && bs[i + 1] == '\n') i++;
1058                     // fallthrough
1059                 case '\n':
1060                     if (newline) return i + 1;    //+1 to get length
1061                     newline = true;
1062                     break;
1063                 default:
1064                     newline = false;
1065             }
1066         }
1067         // If header end is not found, it means the MANIFEST.MF has only
1068         // the main attributes section and it does not end with 2 newlines.
1069         // Returns the whole length so that it can be completely replaced.
1070         return len;
1071     }
1072 
1073     /*
1074      * Try to load the specified signing mechanism.
1075      * The URL class loader is used.
1076      */
1077     @SuppressWarnings("deprecation")
1078     private ContentSigner loadSigningMechanism(String signerClassName,
1079                                                String signerClassPath) {
1080 
1081         // construct class loader
1082         String cpString;   // make sure env.class.path defaults to dot
1083 
1084         // do prepends to get correct ordering
1085         cpString = PathList.appendPath(
1086                 System.getProperty("env.class.path"), null);
1087         cpString = PathList.appendPath(
1088                 System.getProperty("java.class.path"), cpString);
1089         cpString = PathList.appendPath(signerClassPath, cpString);
1090         URL[] urls = PathList.pathToURLs(cpString);
1091         ClassLoader appClassLoader = new URLClassLoader(urls);
1092 
1093         try {
1094             // attempt to find signer
1095             Class<?> signerClass = appClassLoader.loadClass(signerClassName);
1096             Object signer = signerClass.newInstance();
1097             return (ContentSigner) signer;
1098         } catch (ClassNotFoundException|InstantiationException|
1099                 IllegalAccessException|ClassCastException e) {
1100             throw new IllegalArgumentException(
1101                     "Invalid altSigner or altSignerPath", e);
1102         }
1103     }
1104 
1105     static class SignatureFile {
1106 
1107         /**
1108          * SignatureFile
1109          */
1110         Manifest sf;
1111 
1112         /**
1113          * .SF base name
1114          */
1115         String baseName;
1116 
1117         public SignatureFile(MessageDigest digests[],
1118                              Manifest mf,
1119                              ManifestDigester md,
1120                              String baseName,
1121                              boolean signManifest) {
1122 
1123             this.baseName = baseName;
1124 
1125             String version = System.getProperty("java.version");
1126             String javaVendor = System.getProperty("java.vendor");
1127 
1128             sf = new Manifest();
1129             Attributes mattr = sf.getMainAttributes();
1130 
1131             mattr.putValue(Attributes.Name.SIGNATURE_VERSION.toString(), "1.0");
1132             mattr.putValue("Created-By", version + " (" + javaVendor + ")");
1133 
1134             if (signManifest) {
1135                 for (MessageDigest digest: digests) {
1136                     mattr.putValue(digest.getAlgorithm() + "-Digest-Manifest",
1137                             Base64.getEncoder().encodeToString(
1138                                     md.manifestDigest(digest)));
1139                 }
1140             }
1141 
1142             // create digest of the manifest main attributes
1143             ManifestDigester.Entry mde =
1144                     md.get(ManifestDigester.MF_MAIN_ATTRS, false);
1145             if (mde != null) {
1146                 for (MessageDigest digest: digests) {
1147                     mattr.putValue(digest.getAlgorithm() +
1148                                     "-Digest-" + ManifestDigester.MF_MAIN_ATTRS,
1149                             Base64.getEncoder().encodeToString(
1150                                     mde.digest(digest)));
1151                 }
1152             } else {
1153                 throw new IllegalStateException
1154                         ("ManifestDigester failed to create " +
1155                                 "Manifest-Main-Attribute entry");
1156             }
1157 
1158             // go through the manifest entries and create the digests
1159             Map<String, Attributes> entries = sf.getEntries();
1160             for (String name: mf.getEntries().keySet()) {
1161                 mde = md.get(name, false);
1162                 if (mde != null) {
1163                     Attributes attr = new Attributes();
1164                     for (MessageDigest digest: digests) {
1165                         attr.putValue(digest.getAlgorithm() + "-Digest",
1166                                 Base64.getEncoder().encodeToString(
1167                                         mde.digest(digest)));
1168                     }
1169                     entries.put(name, attr);
1170                 }
1171             }
1172         }
1173 
1174         // Write .SF file
1175         public void write(OutputStream out) throws IOException {
1176             sf.write(out);
1177         }
1178 
1179         // get .SF file name
1180         public String getMetaName() {
1181             return "META-INF/" + baseName + ".SF";
1182         }
1183 
1184         // get .DSA (or .DSA, .EC) file name
1185         public String getBlockName(PrivateKey privateKey) {
1186             String keyAlgorithm = privateKey.getAlgorithm();
1187             return "META-INF/" + baseName + "." + keyAlgorithm;
1188         }
1189 
1190         // Generates the PKCS#7 content of block file
1191         @SuppressWarnings("deprecation")
1192         public byte[] generateBlock(ContentSignerParameters params,
1193                                     boolean externalSF,
1194                                     ContentSigner signingMechanism)
1195                 throws NoSuchAlgorithmException,
1196                        IOException, CertificateException {
1197 
1198             if (signingMechanism == null) {
1199                 signingMechanism = new TimestampedSigner();
1200             }
1201             return signingMechanism.generateSignedData(
1202                     params,
1203                     externalSF,
1204                     params.getTimestampingAuthority() != null
1205                         || params.getTimestampingAuthorityCertificate() != null);
1206         }
1207     }
1208 
1209     @SuppressWarnings("deprecation")
1210     class JarSignerParameters implements ContentSignerParameters {
1211 
1212         private String[] args;
1213         private URI tsa;
1214         private byte[] signature;
1215         private String signatureAlgorithm;
1216         private X509Certificate[] signerCertificateChain;
1217         private byte[] content;
1218         private ZipFile source;
1219         private String tSAPolicyID;
1220         private String tSADigestAlg;
1221 
1222         JarSignerParameters(String[] args, URI tsa,
1223                             String tSAPolicyID, String tSADigestAlg,
1224                             byte[] signature, String signatureAlgorithm,
1225                             X509Certificate[] signerCertificateChain,
1226                             byte[] content, ZipFile source) {
1227 
1228             Objects.requireNonNull(signature);
1229             Objects.requireNonNull(signatureAlgorithm);
1230             Objects.requireNonNull(signerCertificateChain);
1231 
1232             this.args = args;
1233             this.tsa = tsa;
1234             this.tSAPolicyID = tSAPolicyID;
1235             this.tSADigestAlg = tSADigestAlg;
1236             this.signature = signature;
1237             this.signatureAlgorithm = signatureAlgorithm;
1238             this.signerCertificateChain = signerCertificateChain;
1239             this.content = content;
1240             this.source = source;
1241         }
1242 
1243         public String[] getCommandLine() {
1244             return args;
1245         }
1246 
1247         public URI getTimestampingAuthority() {
1248             return tsa;
1249         }
1250 
1251         public X509Certificate getTimestampingAuthorityCertificate() {
1252             // We don't use this param. Always provide tsaURI.
1253             return null;
1254         }
1255 
1256         public String getTSAPolicyID() {
1257             return tSAPolicyID;
1258         }
1259 
1260         public String getTSADigestAlg() {
1261             return tSADigestAlg;
1262         }
1263 
1264         public byte[] getSignature() {
1265             return signature;
1266         }
1267 
1268         public String getSignatureAlgorithm() {
1269             return signatureAlgorithm;
1270         }
1271 
1272         public X509Certificate[] getSignerCertificateChain() {
1273             return signerCertificateChain;
1274         }
1275 
1276         public byte[] getContent() {
1277             return content;
1278         }
1279 
1280         public ZipFile getSource() {
1281             return source;
1282         }
1283     }
1284 }