src/jdk.jartool/share/classes/jdk/security/jarsigner/JarSigner.java
changeset 47216 71c04702a3d5
parent 45118 e4258d800b54
child 48760 25725c11c296
equal deleted inserted replaced
47215:4ebc2e2fb97c 47216:71c04702a3d5
       
     1 /*
       
     2  * Copyright (c) 2015, 2017, 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 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 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}/../specs/security/standard-names.html#messagedigest-algorithms">
       
   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}/../specs/security/standard-names.html#messagedigest-algorithms">
       
   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}/../specs/security/standard-names.html#signature-algorithms">
       
   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}/../specs/security/standard-names.html#signature-algorithms">
       
   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.4.
       
   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 }