/* * 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 java.io.Reader; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.Project; import org.apache.tools.ant.filters.ChainableReader; import org.apache.tools.ant.types.FilterChain; import org.apache.tools.ant.types.Path; import org.apache.tools.ant.types.RedirectorElement; import org.apache.tools.ant.types.Resource; import org.apache.tools.ant.types.resources.FileProvider; /** * JAR verification task. * For every JAR passed in, we fork jarsigner to verify * that it is correctly signed. This is more rigorous than just checking for * the existence of a signature; the entire certification chain is tested * @since Ant 1.7 */ public class VerifyJar extends AbstractJarSignerTask { /** * no file message {@value} */ public static final String ERROR_NO_FILE = "Not found :"; /** Error output if there is a failure to verify the jar. */ public static final String ERROR_NO_VERIFY = "Failed to verify "; /** * The string we look for in the text to indicate direct verification */ private static final String VERIFIED_TEXT = "jar verified."; /** * certification flag */ private boolean certificates = false; private BufferingOutputFilter outputCache = new BufferingOutputFilter(); /** * Ask for certificate information to be printed * @param certificates if true print certificates. */ public void setCertificates(boolean certificates) { this.certificates = certificates; } /** * verify our jar files * @throws BuildException on error. */ @Override public void execute() throws BuildException { //validation logic final boolean hasJar = jar != null; if (!hasJar && !hasResources()) { throw new BuildException(ERROR_NO_SOURCE); } beginExecution(); //patch the redirector to save output to a file RedirectorElement redirector = getRedirector(); redirector.setAlwaysLog(true); FilterChain outputFilterChain = redirector.createOutputFilterChain(); outputFilterChain.add(outputCache); try { Path sources = createUnifiedSourcePath(); for (Resource r : sources) { FileProvider fr = r.as(FileProvider.class); verifyOneJar(fr.getFile()); } } finally { endExecution(); } } /** * verify a JAR. * @param jar the jar to verify. * @throws BuildException if the file could not be verified */ private void verifyOneJar(File jar) { if (!jar.exists()) { throw new BuildException(ERROR_NO_FILE + jar); } final ExecTask cmd = createJarSigner(); setCommonOptions(cmd); bindToKeystore(cmd); //verify special operations addValue(cmd, "-verify"); if (certificates) { addValue(cmd, "-certs"); } //JAR is required addValue(cmd, jar.getPath()); log("Verifying JAR: " + jar.getAbsolutePath()); outputCache.clear(); BuildException ex = null; try { cmd.execute(); } catch (BuildException e) { ex = e; } String results = outputCache.toString(); //deal with jdk1.4.2 bug: if (ex != null) { if (results.indexOf("zip file closed") >= 0) { log("You are running " + JARSIGNER_COMMAND + " against a JVM with a known bug that manifests as an IllegalStateException.", Project.MSG_WARN); } else { throw ex; } } if (results.indexOf(VERIFIED_TEXT) < 0) { throw new BuildException(ERROR_NO_VERIFY + jar); } } /** * we are not thread safe here. Do not use on multiple threads at the same time. */ private static class BufferingOutputFilter implements ChainableReader { private BufferingOutputFilterReader buffer; @Override public Reader chain(Reader rdr) { buffer = new BufferingOutputFilterReader(rdr); return buffer; } @Override public String toString() { return buffer.toString(); } public void clear() { if (buffer != null) { buffer.clear(); } } } /** * catch the output of the buffer */ private static class BufferingOutputFilterReader extends Reader { private Reader next; private StringBuffer buffer = new StringBuffer(); public BufferingOutputFilterReader(Reader next) { this.next = next; } @Override public int read(char[] cbuf, int off, int len) throws IOException { //hand down int result = next.read(cbuf, off, len); //cache buffer.append(cbuf, off, len); //return return result; } @Override public void close() throws IOException { next.close(); } @Override public String toString() { return buffer.toString(); } public void clear() { buffer = new StringBuffer(); } } }