1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.plugins.jarsigner;
20
21 import java.io.File;
22 import java.io.IOException;
23 import java.text.MessageFormat;
24 import java.util.ArrayList;
25 import java.util.Arrays;
26 import java.util.Collection;
27 import java.util.HashSet;
28 import java.util.List;
29 import java.util.Optional;
30 import java.util.ResourceBundle;
31
32 import org.apache.maven.artifact.Artifact;
33 import org.apache.maven.execution.MavenSession;
34 import org.apache.maven.plugin.AbstractMojo;
35 import org.apache.maven.plugin.MojoExecutionException;
36 import org.apache.maven.plugins.annotations.Component;
37 import org.apache.maven.plugins.annotations.Parameter;
38 import org.apache.maven.project.MavenProject;
39 import org.apache.maven.settings.Settings;
40 import org.apache.maven.shared.jarsigner.JarSigner;
41 import org.apache.maven.shared.jarsigner.JarSignerRequest;
42 import org.apache.maven.shared.jarsigner.JarSignerUtil;
43 import org.apache.maven.shared.utils.ReaderFactory;
44 import org.apache.maven.shared.utils.StringUtils;
45 import org.apache.maven.shared.utils.cli.Commandline;
46 import org.apache.maven.shared.utils.cli.javatool.JavaToolException;
47 import org.apache.maven.shared.utils.io.FileUtils;
48 import org.apache.maven.toolchain.Toolchain;
49 import org.apache.maven.toolchain.ToolchainManager;
50 import org.sonatype.plexus.components.sec.dispatcher.SecDispatcher;
51 import org.sonatype.plexus.components.sec.dispatcher.SecDispatcherException;
52
53
54
55
56
57
58 public abstract class AbstractJarsignerMojo extends AbstractMojo {
59
60
61
62
63 @Parameter(property = "jarsigner.verbose", defaultValue = "false")
64 private boolean verbose;
65
66
67
68
69 @Parameter(property = "jarsigner.keystore")
70 private String keystore;
71
72
73
74
75 @Parameter(property = "jarsigner.storetype")
76 private String storetype;
77
78
79
80
81 @Parameter(property = "jarsigner.storepass")
82 private String storepass;
83
84
85
86
87 @Parameter(property = "jarsigner.providerName")
88 private String providerName;
89
90
91
92
93 @Parameter(property = "jarsigner.providerClass")
94 private String providerClass;
95
96
97
98
99 @Parameter(property = "jarsigner.providerArg")
100 private String providerArg;
101
102
103
104
105 @Parameter(property = "jarsigner.alias")
106 private String alias;
107
108
109
110
111
112 @Parameter(property = "jarsigner.maxMemory")
113 private String maxMemory;
114
115
116
117
118 @Parameter(property = "jarsigner.archive")
119 private File archive;
120
121
122
123
124
125
126 @Parameter(property = "jarsigner.archiveDirectory")
127 private File archiveDirectory;
128
129
130
131
132
133
134
135
136 @Parameter
137 private String[] includes = {"**/*.?ar"};
138
139
140
141
142
143
144
145 @Parameter
146 private String[] excludes = {};
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165 @Parameter(property = "jarsigner.arguments")
166 private String[] arguments;
167
168
169
170
171 @Parameter(property = "jarsigner.skip", defaultValue = "false")
172 private boolean skip;
173
174
175
176
177
178
179 @Parameter(property = "jarsigner.processMainArtifact", defaultValue = "true")
180 private boolean processMainArtifact;
181
182
183
184
185
186
187
188 @Parameter(property = "jarsigner.processAttachedArtifacts", defaultValue = "true")
189 private boolean processAttachedArtifacts;
190
191
192
193
194
195
196
197 @Parameter(property = "jarsigner.protectedAuthenticationPath", defaultValue = "false")
198 private boolean protectedAuthenticationPath;
199
200
201
202
203
204
205
206 @Parameter
207 private String[] includeClassifiers;
208
209
210
211
212
213
214
215 @Parameter
216 private String[] excludeClassifiers;
217
218
219
220
221 @Parameter(defaultValue = "${project}", readonly = true, required = true)
222 private MavenProject project;
223
224
225
226
227
228
229 @Parameter(defaultValue = "${settings}", readonly = true, required = true)
230 private Settings settings;
231
232
233
234
235
236
237 @Parameter(defaultValue = "${project.basedir}")
238 private File workingDirectory;
239
240
241
242 @Component
243 private JarSigner jarSigner;
244
245
246
247
248
249
250
251 @Parameter(defaultValue = "${session}", readonly = true, required = true)
252 private MavenSession session;
253
254
255
256
257
258
259 @Component
260 private ToolchainManager toolchainManager;
261
262
263
264
265 @Component(hint = "mng-4384")
266 private SecDispatcher securityDispatcher;
267
268 @Override
269 public final void execute() throws MojoExecutionException {
270 if (this.skip) {
271 getLog().info(getMessage("disabled"));
272 return;
273 }
274
275 validateParameters();
276
277 Toolchain toolchain = getToolchain();
278 if (toolchain != null) {
279 getLog().info("Toolchain in maven-jarsigner-plugin: " + toolchain);
280 jarSigner.setToolchain(toolchain);
281 }
282
283 List<File> archives = findJarfiles();
284 processArchives(archives);
285 getLog().info(getMessage("processed", archives.size()));
286 }
287
288
289
290
291
292
293
294 private List<File> findJarfiles() throws MojoExecutionException {
295 if (this.archive != null) {
296
297 return Arrays.asList(this.archive);
298 }
299
300 List<File> archives = new ArrayList<>();
301 if (processMainArtifact) {
302 getFileFromArtifact(this.project.getArtifact()).ifPresent(archives::add);
303 }
304
305 if (processAttachedArtifacts) {
306 Collection<String> includes = new HashSet<>();
307 if (includeClassifiers != null) {
308 includes.addAll(Arrays.asList(includeClassifiers));
309 }
310
311 Collection<String> excludes = new HashSet<>();
312 if (excludeClassifiers != null) {
313 excludes.addAll(Arrays.asList(excludeClassifiers));
314 }
315
316 for (Artifact artifact : this.project.getAttachedArtifacts()) {
317 if (!includes.isEmpty() && !includes.contains(artifact.getClassifier())) {
318 continue;
319 }
320
321 if (excludes.contains(artifact.getClassifier())) {
322 continue;
323 }
324
325 getFileFromArtifact(artifact).ifPresent(archives::add);
326 }
327 } else {
328 if (verbose) {
329 getLog().info(getMessage("ignoringAttachments"));
330 } else {
331 getLog().debug(getMessage("ignoringAttachments"));
332 }
333 }
334
335 if (archiveDirectory != null) {
336 String includeList = (includes != null) ? StringUtils.join(includes, ",") : null;
337 String excludeList = (excludes != null) ? StringUtils.join(excludes, ",") : null;
338
339 try {
340 archives.addAll(FileUtils.getFiles(archiveDirectory, includeList, excludeList));
341 } catch (IOException e) {
342 throw new MojoExecutionException("Failed to scan archive directory for JARs: " + e.getMessage(), e);
343 }
344 }
345
346 return archives;
347 }
348
349
350
351
352
353
354
355
356
357 protected abstract JarSignerRequest createRequest(File archive) throws MojoExecutionException;
358
359
360
361
362
363
364
365
366
367
368
369 protected String getCommandlineInfo(final Commandline commandLine) {
370 if (commandLine == null) {
371 throw new NullPointerException("commandLine");
372 }
373
374 String commandLineInfo = commandLine.toString();
375 commandLineInfo = StringUtils.replace(commandLineInfo, this.storepass, "'*****'");
376 return commandLineInfo;
377 }
378
379 public String getStoretype() {
380 return storetype;
381 }
382
383 public String getStorepass() {
384 return storepass;
385 }
386
387
388
389
390
391
392
393 private static boolean isZipFile(final Artifact artifact) {
394 return artifact != null && artifact.getFile() != null && JarSignerUtil.isZipFile(artifact.getFile());
395 }
396
397
398
399
400
401
402
403
404 private Optional<File> getFileFromArtifact(final Artifact artifact) {
405 if (artifact == null) {
406 throw new NullPointerException("artifact");
407 }
408
409 if (isZipFile(artifact)) {
410 return Optional.of(artifact.getFile());
411 }
412
413 if (this.verbose) {
414 getLog().info(getMessage("unsupported", artifact));
415 } else if (getLog().isDebugEnabled()) {
416 getLog().debug(getMessage("unsupported", artifact));
417 }
418 return Optional.empty();
419 }
420
421
422
423
424
425
426
427 protected void preProcessArchive(final File archive) throws MojoExecutionException {
428
429 }
430
431
432
433
434
435
436 protected void validateParameters() throws MojoExecutionException {
437
438 }
439
440
441
442
443
444
445
446 protected void processArchives(List<File> archives) throws MojoExecutionException {
447 for (File file : archives) {
448 processArchive(file);
449 }
450 }
451
452
453
454
455
456
457
458
459 protected final void processArchive(final File archive) throws MojoExecutionException {
460 if (archive == null) {
461 throw new NullPointerException("archive");
462 }
463
464 preProcessArchive(archive);
465
466 if (this.verbose) {
467 getLog().info(getMessage("processing", archive));
468 } else if (getLog().isDebugEnabled()) {
469 getLog().debug(getMessage("processing", archive));
470 }
471
472 JarSignerRequest request = createRequest(archive);
473 request.setVerbose(verbose);
474 request.setAlias(alias);
475 request.setArchive(archive);
476 request.setKeystore(keystore);
477 request.setStoretype(storetype);
478 request.setProviderArg(providerArg);
479 request.setProviderClass(providerClass);
480 request.setProviderName(providerName);
481 request.setWorkingDirectory(workingDirectory);
482 request.setMaxMemory(maxMemory);
483 request.setProtectedAuthenticationPath(protectedAuthenticationPath);
484
485
486 final List<String> additionalArguments = new ArrayList<>();
487
488 boolean fileEncodingSeen = false;
489
490 if (this.arguments != null) {
491 for (final String argument : this.arguments) {
492 if (argument.trim().startsWith("-J-Dfile.encoding=")) {
493 fileEncodingSeen = true;
494 }
495
496 additionalArguments.add(argument);
497 }
498 }
499
500 if (!fileEncodingSeen) {
501 additionalArguments.add("-J-Dfile.encoding=" + ReaderFactory.FILE_ENCODING);
502 }
503
504
505 if (this.settings != null
506 && this.settings.getActiveProxy() != null
507 && StringUtils.isNotEmpty(this.settings.getActiveProxy().getHost())) {
508 additionalArguments.add(
509 "-J-Dhttp.proxyHost=" + this.settings.getActiveProxy().getHost());
510 additionalArguments.add(
511 "-J-Dhttps.proxyHost=" + this.settings.getActiveProxy().getHost());
512 additionalArguments.add(
513 "-J-Dftp.proxyHost=" + this.settings.getActiveProxy().getHost());
514
515 if (this.settings.getActiveProxy().getPort() > 0) {
516 additionalArguments.add(
517 "-J-Dhttp.proxyPort=" + this.settings.getActiveProxy().getPort());
518 additionalArguments.add(
519 "-J-Dhttps.proxyPort=" + this.settings.getActiveProxy().getPort());
520 additionalArguments.add(
521 "-J-Dftp.proxyPort=" + this.settings.getActiveProxy().getPort());
522 }
523
524 if (StringUtils.isNotEmpty(this.settings.getActiveProxy().getNonProxyHosts())) {
525 additionalArguments.add("-J-Dhttp.nonProxyHosts=\""
526 + this.settings.getActiveProxy().getNonProxyHosts() + "\"");
527
528 additionalArguments.add("-J-Dftp.nonProxyHosts=\""
529 + this.settings.getActiveProxy().getNonProxyHosts() + "\"");
530 }
531 }
532
533 request.setArguments(
534 !additionalArguments.isEmpty()
535 ? additionalArguments.toArray(new String[additionalArguments.size()])
536 : null);
537
538
539 request.setStorepass(decrypt(storepass));
540
541 try {
542 executeJarSigner(jarSigner, request);
543 } catch (JavaToolException e) {
544 throw new MojoExecutionException(getMessage("commandLineException", e.getMessage()), e);
545 }
546 }
547
548
549
550
551
552
553
554
555
556 protected abstract void executeJarSigner(JarSigner jarSigner, JarSignerRequest request)
557 throws JavaToolException, MojoExecutionException;
558
559 protected String decrypt(String encoded) throws MojoExecutionException {
560 try {
561 return securityDispatcher.decrypt(encoded);
562 } catch (SecDispatcherException e) {
563 getLog().error("error using security dispatcher: " + e.getMessage(), e);
564 throw new MojoExecutionException("error using security dispatcher: " + e.getMessage(), e);
565 }
566 }
567
568
569
570
571
572
573
574
575
576
577
578
579 String getMessage(final String key, final Object... args) {
580 if (key == null) {
581 throw new NullPointerException("key");
582 }
583
584 return new MessageFormat(ResourceBundle.getBundle("jarsigner").getString(key)).format(args);
585 }
586
587
588
589
590
591
592
593
594 private Toolchain getToolchain() {
595 Toolchain tc = null;
596 if (toolchainManager != null) {
597 tc = toolchainManager.getToolchainFromBuildContext("jdk", session);
598 }
599
600 return tc;
601 }
602 }