001/** 002 * Copyright 2017 Emmanuel Bourg 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package net.jsign; 018 019import java.io.File; 020 021import org.apache.maven.plugin.AbstractMojo; 022import org.apache.maven.plugin.MojoExecutionException; 023import org.apache.maven.plugin.MojoFailureException; 024import org.apache.maven.plugins.annotations.Component; 025import org.apache.maven.plugins.annotations.LifecyclePhase; 026import org.apache.maven.plugins.annotations.Mojo; 027import org.apache.maven.plugins.annotations.Parameter; 028import org.apache.maven.settings.Proxy; 029import org.apache.maven.settings.Settings; 030import org.sonatype.plexus.components.sec.dispatcher.SecDispatcher; 031import org.sonatype.plexus.components.sec.dispatcher.SecDispatcherException; 032 033/** 034 * Maven plugin for signing files with Authenticode. 035 * 036 * @author Emmanuel Bourg 037 * @since 2.0 038 */ 039@Mojo(name = "sign", defaultPhase = LifecyclePhase.PACKAGE) 040public class JsignMojo extends AbstractMojo { 041 042 /** The file to be signed. */ 043 @Parameter(required = true) 044 private File file; 045 046 /** The program name embedded in the signature. */ 047 @Parameter( property = "jsign.name" ) 048 private String name; 049 050 /** The program URL embedded in the signature. */ 051 @Parameter( property = "jsign.url" ) 052 private String url; 053 054 /** The digest algorithm to use for the signature (SHA-1, SHA-256, SHA-384 or SHA-512). */ 055 @Parameter( property = "jsign.algorithm", defaultValue = "SHA-256" ) 056 private String algorithm; 057 058 /** The keystore file. Required, unless certfile and keyfile are specified. */ 059 @Parameter( property = "jsign.keystore" ) 060 private File keystore; 061 062 /** The password for the keystore. */ 063 @Parameter( property = "jsign.storepass" ) 064 private String storepass; 065 066 /** The type of the keystore (JKS or PKCS12). */ 067 @Parameter( property = "jsign.storetype", defaultValue = "JKS" ) 068 private String storetype; 069 070 /** The alias of the certificate in the keystore. Required if a keystore is specified. */ 071 @Parameter( property = "jsign.alias" ) 072 private String alias; 073 074 /** The file containing the PKCS#7 certificate chain (.p7b or .spc files). */ 075 @Parameter( property = "jsign.certfile" ) 076 private File certfile; 077 078 /** The file containing the private key (PEM or PVK format) */ 079 @Parameter( property = "jsign.keyfile" ) 080 private File keyfile; 081 082 /** The password for the key in the store (if different from the keystore password) or in the keyfile. */ 083 @Parameter( property = "jsign.keypass" ) 084 private String keypass; 085 086 /** The URL of the timestamping authority. */ 087 @Parameter( property = "jsign.tsaurl" ) 088 private String tsaurl; 089 090 /** The protocol used for the timestamping (RFC3161 or Authenticode) */ 091 @Parameter( property = "jsign.tsmode" ) 092 private String tsmode; 093 094 /** The number of retries for timestamping */ 095 @Parameter( property = "jsign.tsretries" ) 096 private int tsretries = -1; 097 098 /** The number of seconds to wait between timestamping retries */ 099 @Parameter( property = "jsign.tsretrywait" ) 100 private int tsretrywait = -1; 101 102 /** Tells if previous signatures should be replaced */ 103 @Parameter( property = "jsign.replace", defaultValue = "false") 104 private boolean replace; 105 106 /** The encoding of the script to be signed (UTF-8 by default). */ 107 @Parameter( property = "jsign.encoding", defaultValue = "UTF-8") 108 private String encoding = "UTF-8"; 109 110 @Parameter(defaultValue = "${settings}", readonly = true) 111 private Settings settings; 112 113 @Parameter( property = "jsign.proxyId" ) 114 private String proxyId; 115 116 @Component(hint = "mng-4384") 117 private SecDispatcher securityDispatcher; 118 119 @Override 120 public void execute() throws MojoExecutionException, MojoFailureException { 121 SignerHelper helper = new SignerHelper(new MavenConsole(getLog()), "element"); 122 123 helper.name(name); 124 helper.url(url); 125 helper.alg(algorithm); 126 helper.keystore(keystore); 127 helper.storepass(decrypt(storepass)); 128 helper.storetype(storetype); 129 helper.alias(alias); 130 helper.certfile(certfile); 131 helper.keyfile(keyfile); 132 helper.keypass(decrypt(keypass)); 133 helper.tsaurl(tsaurl); 134 helper.tsmode(tsmode); 135 helper.tsretries(tsretries); 136 helper.tsretrywait(tsretrywait); 137 helper.replace(replace); 138 helper.encoding(encoding); 139 140 Proxy proxy = getProxyFromSettings(); 141 if (proxy != null) { 142 helper.proxyUrl(proxy.getProtocol() + "://" + proxy.getHost() + ":" + proxy.getPort()); 143 helper.proxyUser(proxy.getUsername()); 144 helper.proxyPass(proxy.getPassword()); 145 } 146 147 try { 148 helper.sign(file); 149 } catch (SignerException e) { 150 throw new MojoFailureException(e.getMessage(), e); 151 } 152 } 153 154 private Proxy getProxyFromSettings() throws MojoExecutionException { 155 if (settings == null) { 156 return null; 157 } 158 159 if (proxyId != null) { 160 for (Proxy proxy : settings.getProxies()) { 161 if (proxyId.equals(proxy.getId())) { 162 return proxy; 163 } 164 } 165 throw new MojoExecutionException("Configured proxy with id=" + proxyId + " not found"); 166 } 167 168 // Get active http/https proxy 169 for (Proxy proxy : settings.getProxies()) { 170 if (proxy.isActive() && ("http".equalsIgnoreCase(proxy.getProtocol()) || "https".equalsIgnoreCase(proxy.getProtocol()))) { 171 return proxy; 172 } 173 } 174 175 return null; 176 } 177 178 private String decrypt(String encoded) throws MojoExecutionException { 179 if (encoded == null) { 180 return null; 181 } 182 183 try { 184 return securityDispatcher.decrypt(encoded); 185 } catch (SecDispatcherException e) { 186 getLog().error("error using security dispatcher: " + e.getMessage(), e); 187 throw new MojoExecutionException("error using security dispatcher: " + e.getMessage(), e); 188 } 189 } 190}