View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.openpgp.ant;
18  
19  import java.io.File;
20  import java.io.FileInputStream;
21  import java.io.FileNotFoundException;
22  import java.io.IOException;
23  import org.apache.commons.openpgp.BouncyCastleKeyRing;
24  import org.apache.commons.openpgp.BouncyCastleOpenPgpSignatureVerifier;
25  import org.apache.commons.openpgp.KeyRing;
26  import org.apache.commons.openpgp.OpenPgpException;
27  import org.apache.commons.openpgp.OpenPgpSignatureVerifier;
28  import org.apache.commons.openpgp.SignatureStatus;
29  import org.apache.tools.ant.BuildException;
30  import org.apache.tools.ant.Task;
31  import org.apache.tools.ant.types.Mapper;
32  import org.apache.tools.ant.util.FileNameMapper;
33  import org.apache.tools.ant.util.FileUtils;
34  import org.apache.tools.ant.util.GlobPatternMapper;
35  import org.bouncycastle.openpgp.PGPException;
36  
37  /**
38   * Verify a signature using the Bouncy Castle OpenPGP provider.
39   *
40   * @author <a href="mailto:dennisl@apache.org">Dennis Lundberg</a>
41   */
42  public class OpenPgpVerifierTask extends Task {
43      private File secring;
44      private File pubring;
45      private String password;
46      private File artefact;
47      private boolean asciiarmor = true;
48      private Mapper mapperElement;
49      private String verifyproperty;
50  
51      /**
52       * Set the secret keyring.
53       * @param secring secret keyring file
54       */
55      public void setSecring(File secring) {
56          this.secring = secring;
57      }
58  
59      /**
60       * Set the public keyring.
61       * @param pubring public keyring file
62       */
63      public void setPubring(File pubring) {
64          this.pubring = pubring;
65      }
66  
67      /**
68       * Use ASCII armored signature files?
69       * @param asciiarmor ascii armored signatures?
70       */
71      public void setAsciiarmor(boolean asciiarmor) {
72          this.asciiarmor = asciiarmor;
73      }
74  
75      /**
76       * Set the value of the password.
77       * @param password value of the password
78       */
79      public void setPassword(String password) {
80          this.password = password;
81      }
82  
83      /**
84       * Set the artefact to be handled.
85       * @param artefact artefact to be handled
86       */
87      public void setArtefact(File artefact) {
88          this.artefact = artefact;
89      }
90  
91      /**
92       * Set the name of the property that contains the result of the verification.
93       * @param verifyproperty name of the property
94       */
95      public void setVerifyproperty(String verifyproperty) {
96          this.verifyproperty = verifyproperty;
97      }
98  
99      /**
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 }