001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.commons.openpgp.ant;
018
019 import java.io.File;
020 import java.io.FileInputStream;
021 import java.io.FileNotFoundException;
022 import java.io.IOException;
023 import org.apache.commons.openpgp.BouncyCastleKeyRing;
024 import org.apache.commons.openpgp.BouncyCastleOpenPgpSignatureVerifier;
025 import org.apache.commons.openpgp.KeyRing;
026 import org.apache.commons.openpgp.OpenPgpException;
027 import org.apache.commons.openpgp.OpenPgpSignatureVerifier;
028 import org.apache.commons.openpgp.SignatureStatus;
029 import org.apache.tools.ant.BuildException;
030 import org.apache.tools.ant.Task;
031 import org.apache.tools.ant.types.Mapper;
032 import org.apache.tools.ant.util.FileNameMapper;
033 import org.apache.tools.ant.util.FileUtils;
034 import org.apache.tools.ant.util.GlobPatternMapper;
035 import org.bouncycastle.openpgp.PGPException;
036
037 /**
038 * Verify a signature using the Bouncy Castle OpenPGP provider.
039 *
040 * @author <a href="mailto:dennisl@apache.org">Dennis Lundberg</a>
041 */
042 public class OpenPgpVerifierTask extends Task {
043 private File secring;
044 private File pubring;
045 private String password;
046 private File artefact;
047 private boolean asciiarmor = true;
048 private Mapper mapperElement;
049 private String verifyproperty;
050
051 /**
052 * Set the secret keyring.
053 * @param secring secret keyring file
054 */
055 public void setSecring(File secring) {
056 this.secring = secring;
057 }
058
059 /**
060 * Set the public keyring.
061 * @param pubring public keyring file
062 */
063 public void setPubring(File pubring) {
064 this.pubring = pubring;
065 }
066
067 /**
068 * Use ASCII armored signature files?
069 * @param asciiarmor ascii armored signatures?
070 */
071 public void setAsciiarmor(boolean asciiarmor) {
072 this.asciiarmor = asciiarmor;
073 }
074
075 /**
076 * Set the value of the password.
077 * @param password value of the password
078 */
079 public void setPassword(String password) {
080 this.password = password;
081 }
082
083 /**
084 * Set the artefact to be handled.
085 * @param artefact artefact to be handled
086 */
087 public void setArtefact(File artefact) {
088 this.artefact = artefact;
089 }
090
091 /**
092 * Set the name of the property that contains the result of the verification.
093 * @param verifyproperty name of the property
094 */
095 public void setVerifyproperty(String verifyproperty) {
096 this.verifyproperty = verifyproperty;
097 }
098
099 /**
100 * Define the mapper to map source to destination files.
101 * @return a mapper to be configured.
102 * @exception org.apache.tools.ant.BuildException if more than one mapper is defined.
103 */
104 public Mapper createMapper() throws BuildException {
105 if (mapperElement != null) {
106 throw new BuildException("Cannot define more than one mapper",
107 getLocation());
108 }
109 mapperElement = new Mapper(getProject());
110 return mapperElement;
111 }
112
113 public void execute() {
114 if (secring == null) {
115 throw new BuildException("secring attribute compulsory");
116 }
117 if (pubring == null) {
118 throw new BuildException("pubring attribute compulsory");
119 }
120 if (password == null) {
121 throw new BuildException("password attribute compulsory");
122 }
123 if (artefact == null) {
124 throw new BuildException("The 'artefact' attribute is compulsory.");
125 }
126 if (verifyproperty == null) {
127 throw new BuildException("The 'verifyproperty' attribute is compulsory.");
128 }
129 if (!secring.exists() || !secring.canRead()) {
130 throw new BuildException("secret keyring file '" + secring.getAbsolutePath() + "' does not exist or is not readable");
131 }
132 if (!pubring.exists() || !pubring.canRead()) {
133 throw new BuildException("public keyring file '" + pubring.getAbsolutePath() + "' does not exist or is not readable");
134 }
135 FileInputStream secStream;
136 FileInputStream pubStream;
137 KeyRing keyRing = null;
138 try {
139 secStream = new FileInputStream(secring);
140 pubStream = new FileInputStream(pubring);
141 keyRing = new BouncyCastleKeyRing(secStream,
142 pubStream, password.toCharArray() );
143 } catch (IOException ioe) {
144 throw new BuildException(ioe);
145 } catch (PGPException pgpe) {
146 throw new BuildException(pgpe);
147 }
148 if (artefact != null) {
149 doHandle(keyRing, artefact);
150 }
151 FileUtils.close(secStream);
152 FileUtils.close(pubStream);
153 }
154
155 private void doHandle(KeyRing keyRing, File oneartefact) {
156 doHandle(keyRing, oneartefact, oneartefact.getParentFile(), oneartefact.getName());
157 }
158
159 private void doHandle(KeyRing keyRing, File oneartefact, File basedir, String relpath) {
160 FileInputStream artifactFis = null;
161 FileInputStream signatureFis = null;
162 File signature;
163 boolean isValid = false;
164
165 try {
166 artifactFis = new FileInputStream(oneartefact);
167 FileNameMapper mapper = getMapper();
168 String [] mappedFiles = mapper.mapFileName(relpath);
169 if (mappedFiles == null || mappedFiles.length != 1) {
170 throw new BuildException("mapper returned more or less than one output");
171 }
172 signature = new File(basedir, mappedFiles[0]);
173 signatureFis = new FileInputStream(signature);
174 OpenPgpSignatureVerifier verifier = new BouncyCastleOpenPgpSignatureVerifier();
175 SignatureStatus status = verifier.verifyDetachedSignature(artifactFis, signatureFis, keyRing);
176 isValid = status.isValid();
177 } catch (FileNotFoundException fnfe) {
178 throw new BuildException(fnfe);
179 } catch (IOException ioe) {
180 throw new BuildException(ioe);
181 } catch (OpenPgpException opgpe) {
182 throw new BuildException(opgpe);
183 }
184 finally {
185 getProject().setProperty(verifyproperty, Boolean.toString(isValid));
186 }
187 FileUtils.close(signatureFis);
188 FileUtils.close(artifactFis);
189 }
190
191 /**
192 * Return the mapper to use based on nested elements or use a default mapping.
193 */
194 private FileNameMapper getMapper() {
195 FileNameMapper mapper = null;
196 if (mapperElement != null) {
197 mapper = mapperElement.getImplementation();
198 } else {
199 mapper = new GlobPatternMapper();
200 mapper.setFrom("*");
201 if (asciiarmor) {
202 mapper.setTo("*.asc");
203 } else {
204 mapper.setTo("*.sig");
205 }
206 }
207 return mapper;
208 }
209 }