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 }