/* * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package jdk.security.jarsigner; import sun.security.x509.AlgorithmId; import sun.security.tools.jarsigner.Builder; import java.io.OutputStream; import java.net.URI; import java.security.KeyStore; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.Provider; import java.security.cert.CertPath; import java.security.cert.Certificate; import java.util.List; import java.util.Objects; import java.util.zip.ZipFile; /** * A utility class to sign a jar file. *

* A caller first creates a {@code JarSigner} object, then calls various * mutators (methods except for {@link #sign}) to specify the options of * signing, and finally calls {@link #sign} to sign a given file. *

* The {@link #sign} method does not change the internal state of a * {@code JarSigner} object. Therefore it's safe to call multiple {@link #sign} * methods on different jar files in parallel. *

* Unless otherwise stated, calling a method with a null argument will throw * a {@link NullPointerException}. *

* Example: *

 * JarSigner signer = new JarSigner(key, certPath)
 *         .digestAlg("SHA-1")
 *         .sigAlg("SHA1withDSA");
 * try (ZipFile in = new ZipFile(inputFile);
 *         FileOutputStream out = new FileOutputStream(outFile)) {
 *     signer.sign(in, out);
 * }
 * 
* * @since 1.9 */ @jdk.Exported public final class JarSigner { private final Builder builder; /** * Creates a {@code JarSigner} object with * a {@link KeyStore.PrivateKeyEntry} object. * * @param entry the {@link KeyStore.PrivateKeyEntry} of the signer. */ public JarSigner(KeyStore.PrivateKeyEntry entry) { builder = new Builder( entry.getPrivateKey(), entry.getCertificateChain()); } /** * Creates a {@code JarSigner} object with a private key and * a certificate path. * * @param privateKey the private key of the signer. * @param certPath the certificate path of the signer. * @throws IllegalArgumentException if {@code certPath} is empty, or * the {@code privateKey} algorithm does not match the algorithm * of the {@code PublicKey} in the end entity certificate * (the first certificate in {@code certPath}). */ public JarSigner(PrivateKey privateKey, CertPath certPath) { List certs = certPath.getCertificates(); if (certs.isEmpty()) { throw new IllegalArgumentException("certPath cannot be empty"); } if (!privateKey.getAlgorithm().equals (certs.get(0).getPublicKey().getAlgorithm())) { throw new IllegalArgumentException ("private key algorithm does not match " + "algorithm of public key in end entity " + "certificate (1st in certPath)"); } builder = new Builder( privateKey, certs.toArray(new Certificate[certs.size()])); } /** * Sets the digest algorithm. If no digest algorithm is specified, * the default algorithm returned by {@link #getDefaultDigestAlg} * will be used. * * @param algorithm the standard name of the algorithm set. See * the MessageDigest section in the * Java Cryptography Architecture Standard Algorithm Name * Documentation for information about standard algorithm names. * @return the {@code JarSigner} itself. * @throws NoSuchAlgorithmException if {@code algorithm} is not available. */ public JarSigner digestAlg(String algorithm) throws NoSuchAlgorithmException { builder.digestAlg(Objects.requireNonNull(algorithm), null); return this; } /** * Sets the digest algorithm from the specified provider. * If no digest algorithm is specified, the default algorithm * returned by {@link #getDefaultDigestAlg} will be used. * * @param algorithm the standard name of the algorithm set. See * the MessageDigest section in the * Java Cryptography Architecture Standard Algorithm Name * Documentation for information about standard algorithm names. * @param provider the provider. * @return the {@code JarSigner} itself. * @throws NoSuchAlgorithmException if {@code algorithm} is not available * in the specified provider. */ public JarSigner digestAlg(String algorithm, Provider provider) throws NoSuchAlgorithmException { builder.digestAlg( Objects.requireNonNull(algorithm), Objects.requireNonNull(provider)); return this; } /** * Sets the signature algorithm. If no signature algorithm * is specified, the default signature algorithm returned by * {@link #getDefaultSigAlg} for the private key algorithm will be used. * * @param algorithm the standard name of the algorithm set. See * the Signature section in the * Java Cryptography Architecture Standard Algorithm Name * Documentation for information about standard algorithm names. * @return the {@code JarSigner} itself. * @throws NoSuchAlgorithmException if {@code algorithm} is not available. * @throws IllegalArgumentException if {@code algorithm} is not compatible * with the algorithm of the signer's private key. */ public JarSigner sigAlg(String algorithm) throws NoSuchAlgorithmException { builder.sigAlg(Objects.requireNonNull(algorithm), null); return this; } /** * Sets the signature algorithm from the specified provider. * If no signature algorithm is specified, the default signature algorithm * returned by {@link #getDefaultSigAlg} for the private key algorithm * will be used. * * @param algorithm the standard name of the algorithm set. See * the Signature section in the * Java Cryptography Architecture Standard Algorithm Name * Documentation for information about standard algorithm names. * @param provider the provider. * @return the {@code JarSigner} itself. * @throws NoSuchAlgorithmException if {@code algorithm} is not available * in the specified provider. * @throws IllegalArgumentException if {@code algorithm} is not compatible * with the algorithm of the signer's private key. */ public JarSigner sigAlg(String algorithm, Provider provider) throws NoSuchAlgorithmException { builder.sigAlg( Objects.requireNonNull(algorithm), Objects.requireNonNull(provider)); return this; } /** * Sets the URI of the Time Stamping Authority (TSA). * * @param uri the URI. * @return the {@code JarSigner} itself. */ public JarSigner tsa(URI uri) { builder.tsa(Objects.requireNonNull(uri)); return this; } /** * Sets the signer name. The name will be used as the base name for * the signature files. All lowercase characters will be converted to * uppercase for signature file names. If a signer name is not * specified, the string "SIGNER" will be used. * * @param name the signer name. * @return the {@code JarSigner} itself. * @throws IllegalArgumentException if {@code name} is empty or has a size * bigger than 8, or it contains characters not from the set * "a-zA-Z0-9_-". */ public JarSigner signerName(String name) { builder.signerName(Objects.requireNonNull(name)); return this; } /** * Gets the default digest algorithm. * * @return the default digest algorithm. */ public static String getDefaultDigestAlg() { return Builder.getDefaultDigestAlg(); } /** * Gets the default signature algorithm for a private key algorithm. * For example, SHA256withRSA for RSA, and SHA256withDSA for DSA. * * @param keyAlg the key algorithm. * @return the default signature algorithm. Returns null if a default * signature algorithm cannot be found. In this case, {@link #sigAlg} * must be called to specify a signature algorithm. Otherwise, * the {@link #sign} method will throw a {@link JarSignerException}. * @throws IllegalArgumentException if {@code keyAlg} is empty. */ public static String getDefaultSigAlg(String keyAlg) { if (keyAlg.isEmpty()) { throw new IllegalArgumentException("key algorithm cannot be empty"); } return AlgorithmId.getDefaultSigAlgForKeyAlg(keyAlg); } /** * Signs a file into an {@link OutputStream}. This method will not close * {@code file} or {@code os}. * * @param file the file to sign. * @param os the output stream. * @throws JarSignerException if the signing fails. */ public void sign(ZipFile file, OutputStream os) { builder.build().sign( Objects.requireNonNull(file), Objects.requireNonNull(os)); } }