|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675 |
- /*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
- package org.apache.tools.ant.taskdefs;
-
- import java.io.File;
- import java.io.IOException;
-
- import org.apache.tools.ant.BuildException;
- import org.apache.tools.ant.Project;
- import org.apache.tools.ant.taskdefs.condition.IsSigned;
- import org.apache.tools.ant.types.Path;
- import org.apache.tools.ant.types.Resource;
- import org.apache.tools.ant.types.resources.FileProvider;
- import org.apache.tools.ant.types.resources.FileResource;
- import org.apache.tools.ant.util.FileNameMapper;
- import org.apache.tools.ant.util.FileUtils;
- import org.apache.tools.ant.util.IdentityMapper;
- import org.apache.tools.ant.util.ResourceUtils;
-
- /**
- * Signs JAR or ZIP files with the javasign command line tool. The tool detailed
- * dependency checking: files are only signed if they are not signed. The
- * <code>signjar</code> attribute can point to the file to generate; if this file
- * exists then its modification date is used as a cue as to whether to resign
- * any JAR file.
- *
- * Timestamp driven signing is based on the unstable and inadequately documented
- * information in the Java1.5 docs
- * @see <a href="http://java.sun.com/j2se/1.5.0/docs/guide/security/time-of-signing-beta1.html">
- * beta documentation</a>
- * @ant.task category="java"
- * @since Ant 1.1
- */
- public class SignJar extends AbstractJarSignerTask {
- // CheckStyle:VisibilityModifier OFF - bc
-
- private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
-
- /**
- * error string for unit test verification: {@value}
- */
- public static final String ERROR_TODIR_AND_SIGNEDJAR
- = "'destdir' and 'signedjar' cannot both be set";
- /**
- * error string for unit test verification: {@value}
- */
- public static final String ERROR_TOO_MANY_MAPPERS = "Too many mappers";
- /**
- * error string for unit test verification {@value}
- */
- public static final String ERROR_SIGNEDJAR_AND_PATHS
- = "You cannot specify the signed JAR when using paths or filesets";
- /**
- * error string for unit test verification: {@value}
- */
- public static final String ERROR_BAD_MAP = "Cannot map source file to anything sensible: ";
- /**
- * error string for unit test verification: {@value}
- */
- public static final String ERROR_MAPPER_WITHOUT_DEST
- = "The destDir attribute is required if a mapper is set";
- /**
- * error string for unit test verification: {@value}
- */
- public static final String ERROR_NO_ALIAS = "alias attribute must be set";
- /**
- * error string for unit test verification: {@value}
- */
- public static final String ERROR_NO_STOREPASS = "storepass attribute must be set";
-
- /**
- * name to a signature file
- */
- protected String sigfile;
-
- /**
- * name of a single jar
- */
- protected File signedjar;
-
- /**
- * flag for internal sf signing
- */
- protected boolean internalsf;
-
- /**
- * sign sections only?
- */
- protected boolean sectionsonly;
-
- /**
- * flag to preserve timestamp on modified files
- */
- private boolean preserveLastModified;
-
- /**
- * Whether to assume a jar which has an appropriate .SF file in is already
- * signed.
- */
- protected boolean lazy;
-
- /**
- * the output directory when using paths.
- */
- protected File destDir;
-
- /**
- * mapper for todir work
- */
- private FileNameMapper mapper;
-
- /**
- * URL for a tsa; null implies no tsa support
- */
- protected String tsaurl;
-
- /**
- * Proxy host to be used when connecting to TSA server
- */
- protected String tsaproxyhost;
-
- /**
- * Proxy port to be used when connecting to TSA server
- */
- protected String tsaproxyport;
-
- /**
- * alias for the TSA in the keystore
- */
- protected String tsacert;
-
- /**
- * force signing even if the jar is already signed.
- */
- private boolean force = false;
-
- /**
- * signature algorithm
- */
- private String sigAlg;
-
- /**
- * digest algorithm
- */
- private String digestAlg;
-
- /**
- * tsa digest algorithm
- */
- private String tsaDigestAlg;
-
- // CheckStyle:VisibilityModifier ON
-
- /**
- * name of .SF/.DSA file; optional
- *
- * @param sigfile the name of the .SF/.DSA file
- */
- public void setSigfile(final String sigfile) {
- this.sigfile = sigfile;
- }
-
- /**
- * name of signed JAR file; optional
- *
- * @param signedjar the name of the signed jar file
- */
- public void setSignedjar(final File signedjar) {
- this.signedjar = signedjar;
- }
-
- /**
- * Flag to include the .SF file inside the signature; optional; default
- * false
- *
- * @param internalsf if true include the .SF file inside the signature
- */
- public void setInternalsf(final boolean internalsf) {
- this.internalsf = internalsf;
- }
-
- /**
- * flag to compute hash of entire manifest; optional, default false
- *
- * @param sectionsonly flag to compute hash of entire manifest
- */
- public void setSectionsonly(final boolean sectionsonly) {
- this.sectionsonly = sectionsonly;
- }
-
- /**
- * flag to control whether the presence of a signature file means a JAR is
- * signed; optional, default false
- *
- * @param lazy flag to control whether the presence of a signature
- */
- public void setLazy(final boolean lazy) {
- this.lazy = lazy;
- }
-
- /**
- * Optionally sets the output directory to be used.
- *
- * @param destDir the directory in which to place signed jars
- * @since Ant 1.7
- */
- public void setDestDir(File destDir) {
- this.destDir = destDir;
- }
-
-
- /**
- * add a mapper to determine file naming policy. Only used with toDir
- * processing.
- *
- * @param newMapper the mapper to add.
- * @since Ant 1.7
- */
- public void add(FileNameMapper newMapper) {
- if (mapper != null) {
- throw new BuildException(ERROR_TOO_MANY_MAPPERS);
- }
- mapper = newMapper;
- }
-
- /**
- * get the active mapper; may be null
- * @return mapper or null
- * @since Ant 1.7
- */
- public FileNameMapper getMapper() {
- return mapper;
- }
-
- /**
- * get the -tsaurl url
- * @return url or null
- * @since Ant 1.7
- */
- public String getTsaurl() {
- return tsaurl;
- }
-
- /**
- *
- * @param tsaurl the tsa url.
- * @since Ant 1.7
- */
- public void setTsaurl(String tsaurl) {
- this.tsaurl = tsaurl;
- }
-
- /**
- * Get the proxy host to be used when connecting to the TSA url
- * @return url or null
- * @since Ant 1.9.5
- */
- public String getTsaproxyhost() {
- return tsaproxyhost;
- }
-
- /**
- *
- * @param tsaproxyhost the proxy host to be used when connecting to the TSA.
- * @since Ant 1.9.5
- */
- public void setTsaproxyhost(String tsaproxyhost) {
- this.tsaproxyhost = tsaproxyhost;
- }
-
- /**
- * Get the proxy host to be used when connecting to the TSA url
- * @return url or null
- * @since Ant 1.9.5
- */
- public String getTsaproxyport() {
- return tsaproxyport;
- }
-
- /**
- *
- * @param tsaproxyport the proxy port to be used when connecting to the TSA.
- * @since Ant 1.9.5
- */
- public void setTsaproxyport(String tsaproxyport) {
- this.tsaproxyport = tsaproxyport;
- }
-
- /**
- * get the -tsacert option
- * @since Ant 1.7
- * @return a certificate alias or null
- */
- public String getTsacert() {
- return tsacert;
- }
-
- /**
- * set the alias in the keystore of the TSA to use;
- * @param tsacert the cert alias.
- */
- public void setTsacert(String tsacert) {
- this.tsacert = tsacert;
- }
-
- /**
- * Whether to force signing of a jar even it is already signed.
- * @param b boolean
- * @since Ant 1.8.0
- */
- public void setForce(boolean b) {
- force = b;
- }
-
- /**
- * Should the task force signing of a jar even it is already
- * signed?
- * @return boolean
- * @since Ant 1.8.0
- */
- public boolean isForce() {
- return force;
- }
-
- /**
- * Signature Algorithm; optional
- *
- * @param sigAlg the signature algorithm
- */
- public void setSigAlg(String sigAlg) {
- this.sigAlg = sigAlg;
- }
-
- /**
- * Signature Algorithm; optional
- *
- * @return String
- */
- public String getSigAlg() {
- return sigAlg;
- }
-
- /**
- * Digest Algorithm; optional
- *
- * @param digestAlg the digest algorithm
- */
- public void setDigestAlg(String digestAlg) {
- this.digestAlg = digestAlg;
- }
-
- /**
- * Digest Algorithm; optional
- *
- * @return String
- */
- public String getDigestAlg() {
- return digestAlg;
- }
-
- /**
- * TSA Digest Algorithm; optional
- *
- * @param digestAlg the tsa digest algorithm
- * @since Ant 1.10.2
- */
- public void setTSADigestAlg(String digestAlg) {
- this.tsaDigestAlg = digestAlg;
- }
-
- /**
- * TSA Digest Algorithm; optional
- *
- * @return String
- * @since Ant 1.10.2
- */
- public String getTSADigestAlg() {
- return tsaDigestAlg;
- }
-
- /**
- * sign the jar(s)
- *
- * @throws BuildException on errors
- */
- @Override
- public void execute() throws BuildException {
- //validation logic
- final boolean hasJar = jar != null;
- final boolean hasSignedJar = signedjar != null;
- final boolean hasDestDir = destDir != null;
- final boolean hasMapper = mapper != null;
-
- if (!hasJar && !hasResources()) {
- throw new BuildException(ERROR_NO_SOURCE);
- }
- if (null == alias) {
- throw new BuildException(ERROR_NO_ALIAS);
- }
-
- if (null == storepass) {
- throw new BuildException(ERROR_NO_STOREPASS);
- }
-
- if (hasDestDir && hasSignedJar) {
- throw new BuildException(ERROR_TODIR_AND_SIGNEDJAR);
- }
-
-
- if (hasResources() && hasSignedJar) {
- throw new BuildException(ERROR_SIGNEDJAR_AND_PATHS);
- }
-
- //this isn't strictly needed, but by being fussy now,
- //we can change implementation details later
- if (!hasDestDir && hasMapper) {
- throw new BuildException(ERROR_MAPPER_WITHOUT_DEST);
- }
-
- beginExecution();
-
-
- try {
- //special case single jar handling with signedjar attribute set
- if (hasJar && hasSignedJar) {
- // single jar processing
- signOneJar(jar, signedjar);
- //return here.
- return;
- }
-
- //the rest of the method treats single jar like
- //a nested path with one file
-
- Path sources = createUnifiedSourcePath();
- //set up our mapping policy
- FileNameMapper destMapper = hasMapper ? mapper : new IdentityMapper();
-
- //at this point the paths are set up with lists of files,
- //and the mapper is ready to map from source dirs to dest files
- //now we iterate through every JAR giving source and dest names
- // deal with the paths
- for (Resource r : sources) {
- FileResource fr = ResourceUtils
- .asFileResource(r.as(FileProvider.class));
-
- //calculate our destination directory; it is either the destDir
- //attribute, or the base dir of the fileset (for in situ updates)
- File toDir = hasDestDir ? destDir : fr.getBaseDir();
-
- //determine the destination filename via the mapper
- String[] destFilenames = destMapper.mapFileName(fr.getName());
- if (destFilenames == null || destFilenames.length != 1) {
- //we only like simple mappers.
- throw new BuildException(ERROR_BAD_MAP + fr.getFile());
- }
- File destFile = new File(toDir, destFilenames[0]);
- signOneJar(fr.getFile(), destFile);
- }
- } finally {
- endExecution();
- }
- }
-
- /**
- * Sign one jar.
- * <p/>
- * The signing only takes place if {@link #isUpToDate(File, File)} indicates
- * that it is needed.
- *
- * @param jarSource source to sign
- * @param jarTarget target; may be null
- * @throws BuildException if something goes wrong
- */
- private void signOneJar(File jarSource, File jarTarget)
- throws BuildException {
-
-
- File targetFile = jarTarget;
- if (targetFile == null) {
- targetFile = jarSource;
- }
- if (isUpToDate(jarSource, targetFile)) {
- return;
- }
-
- long lastModified = jarSource.lastModified();
- final ExecTask cmd = createJarSigner();
-
- setCommonOptions(cmd);
-
- bindToKeystore(cmd);
- if (null != sigfile) {
- addValue(cmd, "-sigfile");
- String value = this.sigfile;
- addValue(cmd, value);
- }
-
- try {
- //DO NOT SET THE -signedjar OPTION if source==dest
- //unless you like fielding hotspot crash reports
- if (!FILE_UTILS.areSame(jarSource, targetFile)) {
- addValue(cmd, "-signedjar");
- addValue(cmd, targetFile.getPath());
- }
- } catch (IOException ioex) {
- throw new BuildException(ioex);
- }
-
- if (internalsf) {
- addValue(cmd, "-internalsf");
- }
-
- if (sectionsonly) {
- addValue(cmd, "-sectionsonly");
- }
-
- if (sigAlg != null) {
- addValue(cmd, "-sigalg");
- addValue(cmd, sigAlg);
- }
-
- if (digestAlg != null) {
- addValue(cmd, "-digestalg");
- addValue(cmd, digestAlg);
- }
-
- //add -tsa operations if declared
- addTimestampAuthorityCommands(cmd);
-
- //JAR source is required
- addValue(cmd, jarSource.getPath());
-
- //alias is required for signing
- addValue(cmd, alias);
-
- log("Signing JAR: "
- + jarSource.getAbsolutePath()
- + " to "
- + targetFile.getAbsolutePath()
- + " as " + alias);
-
- cmd.execute();
-
- // restore the lastModified attribute
- if (preserveLastModified) {
- FILE_UTILS.setFileLastModified(targetFile, lastModified);
- }
- }
-
- /**
- * If the tsa parameters are set, this passes them to the command.
- * There is no validation of java version, as third party JDKs
- * may implement this on earlier/later jarsigner implementations.
- * @param cmd the exec task.
- */
- private void addTimestampAuthorityCommands(final ExecTask cmd) {
- if (tsaurl != null) {
- addValue(cmd, "-tsa");
- addValue(cmd, tsaurl);
- }
-
- if (tsacert != null) {
- addValue(cmd, "-tsacert");
- addValue(cmd, tsacert);
- }
-
- if (tsaproxyhost != null) {
- if (tsaurl == null || tsaurl.startsWith("https")) {
- addProxyFor(cmd, "https");
- }
- if (tsaurl == null || !tsaurl.startsWith("https")) {
- addProxyFor(cmd, "http");
- }
- }
-
- if (tsaDigestAlg != null) {
- addValue(cmd, "-tsadigestalg");
- addValue(cmd, tsaDigestAlg);
- }
- }
-
- /**
- * <p>Compare a jar file with its corresponding signed jar. The logic for this
- * is complex, and best explained in the source itself. Essentially if
- * either file doesn't exist, or the destfile has an out of date timestamp,
- * then the return value is false.</p>
- *
- * <p>If we are signing ourself, the check {@link #isSigned(File)} is used to
- * trigger the process.</p>
- *
- * @param jarFile the unsigned jar file
- * @param signedjarFile the result signed jar file
- * @return true if the signedjarFile is considered up to date
- */
- protected boolean isUpToDate(File jarFile, File signedjarFile) {
- if (isForce() || null == jarFile || !jarFile.exists()) {
- //these are pathological cases, but retained in case somebody
- //subclassed us.
- return false;
- }
-
- //we normally compare destination with source
- File destFile = signedjarFile;
- if (destFile == null) {
- //but if no dest is specified, compare source to source
- destFile = jarFile;
- }
-
- //if, by any means, the destfile and source match,
- if (jarFile.equals(destFile)) {
- if (lazy) {
- //we check the presence of signatures on lazy signing
- return isSigned(jarFile);
- }
- //unsigned or non-lazy self signings are always false
- return false;
- }
-
- //if they are different, the timestamps are used
- return FILE_UTILS.isUpToDate(jarFile, destFile);
- }
-
- /**
- * test for a file being signed, by looking for a signature in the META-INF
- * directory with our alias/sigfile.
- *
- * @param file the file to be checked
- * @return true if the file is signed
- * @see IsSigned#isSigned(File, String)
- */
- protected boolean isSigned(File file) {
- try {
- return IsSigned.isSigned(file, sigfile == null ? alias : sigfile);
- } catch (IOException e) {
- //just log this
- log(e.toString(), Project.MSG_VERBOSE);
- return false;
- }
- }
-
- /**
- * true to indicate that the signed jar modification date remains the same
- * as the original. Defaults to false
- *
- * @param preserveLastModified if true preserve the last modified time
- */
- public void setPreserveLastModified(boolean preserveLastModified) {
- this.preserveLastModified = preserveLastModified;
- }
-
- private void addProxyFor(final ExecTask cmd, final String scheme) {
- addValue(cmd, "-J-D" + scheme + ".proxyHost=" + tsaproxyhost);
-
- if (tsaproxyport != null) {
- addValue(cmd, "-J-D" + scheme + ".proxyPort=" + tsaproxyport);
- }
- }
- }
|