You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

SignJar.java 19 kB

11 years ago
8 years ago
11 years ago
8 years ago
11 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675
  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. */
  18. package org.apache.tools.ant.taskdefs;
  19. import java.io.File;
  20. import java.io.IOException;
  21. import org.apache.tools.ant.BuildException;
  22. import org.apache.tools.ant.Project;
  23. import org.apache.tools.ant.taskdefs.condition.IsSigned;
  24. import org.apache.tools.ant.types.Path;
  25. import org.apache.tools.ant.types.Resource;
  26. import org.apache.tools.ant.types.resources.FileProvider;
  27. import org.apache.tools.ant.types.resources.FileResource;
  28. import org.apache.tools.ant.util.FileNameMapper;
  29. import org.apache.tools.ant.util.FileUtils;
  30. import org.apache.tools.ant.util.IdentityMapper;
  31. import org.apache.tools.ant.util.ResourceUtils;
  32. /**
  33. * Signs JAR or ZIP files with the javasign command line tool. The tool detailed
  34. * dependency checking: files are only signed if they are not signed. The
  35. * <code>signjar</code> attribute can point to the file to generate; if this file
  36. * exists then its modification date is used as a cue as to whether to resign
  37. * any JAR file.
  38. *
  39. * Timestamp driven signing is based on the unstable and inadequately documented
  40. * information in the Java1.5 docs
  41. * @see <a href="http://java.sun.com/j2se/1.5.0/docs/guide/security/time-of-signing-beta1.html">
  42. * beta documentation</a>
  43. * @ant.task category="java"
  44. * @since Ant 1.1
  45. */
  46. public class SignJar extends AbstractJarSignerTask {
  47. // CheckStyle:VisibilityModifier OFF - bc
  48. private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
  49. /**
  50. * error string for unit test verification: {@value}
  51. */
  52. public static final String ERROR_TODIR_AND_SIGNEDJAR
  53. = "'destdir' and 'signedjar' cannot both be set";
  54. /**
  55. * error string for unit test verification: {@value}
  56. */
  57. public static final String ERROR_TOO_MANY_MAPPERS = "Too many mappers";
  58. /**
  59. * error string for unit test verification {@value}
  60. */
  61. public static final String ERROR_SIGNEDJAR_AND_PATHS
  62. = "You cannot specify the signed JAR when using paths or filesets";
  63. /**
  64. * error string for unit test verification: {@value}
  65. */
  66. public static final String ERROR_BAD_MAP = "Cannot map source file to anything sensible: ";
  67. /**
  68. * error string for unit test verification: {@value}
  69. */
  70. public static final String ERROR_MAPPER_WITHOUT_DEST
  71. = "The destDir attribute is required if a mapper is set";
  72. /**
  73. * error string for unit test verification: {@value}
  74. */
  75. public static final String ERROR_NO_ALIAS = "alias attribute must be set";
  76. /**
  77. * error string for unit test verification: {@value}
  78. */
  79. public static final String ERROR_NO_STOREPASS = "storepass attribute must be set";
  80. /**
  81. * name to a signature file
  82. */
  83. protected String sigfile;
  84. /**
  85. * name of a single jar
  86. */
  87. protected File signedjar;
  88. /**
  89. * flag for internal sf signing
  90. */
  91. protected boolean internalsf;
  92. /**
  93. * sign sections only?
  94. */
  95. protected boolean sectionsonly;
  96. /**
  97. * flag to preserve timestamp on modified files
  98. */
  99. private boolean preserveLastModified;
  100. /**
  101. * Whether to assume a jar which has an appropriate .SF file in is already
  102. * signed.
  103. */
  104. protected boolean lazy;
  105. /**
  106. * the output directory when using paths.
  107. */
  108. protected File destDir;
  109. /**
  110. * mapper for todir work
  111. */
  112. private FileNameMapper mapper;
  113. /**
  114. * URL for a tsa; null implies no tsa support
  115. */
  116. protected String tsaurl;
  117. /**
  118. * Proxy host to be used when connecting to TSA server
  119. */
  120. protected String tsaproxyhost;
  121. /**
  122. * Proxy port to be used when connecting to TSA server
  123. */
  124. protected String tsaproxyport;
  125. /**
  126. * alias for the TSA in the keystore
  127. */
  128. protected String tsacert;
  129. /**
  130. * force signing even if the jar is already signed.
  131. */
  132. private boolean force = false;
  133. /**
  134. * signature algorithm
  135. */
  136. private String sigAlg;
  137. /**
  138. * digest algorithm
  139. */
  140. private String digestAlg;
  141. /**
  142. * tsa digest algorithm
  143. */
  144. private String tsaDigestAlg;
  145. // CheckStyle:VisibilityModifier ON
  146. /**
  147. * name of .SF/.DSA file; optional
  148. *
  149. * @param sigfile the name of the .SF/.DSA file
  150. */
  151. public void setSigfile(final String sigfile) {
  152. this.sigfile = sigfile;
  153. }
  154. /**
  155. * name of signed JAR file; optional
  156. *
  157. * @param signedjar the name of the signed jar file
  158. */
  159. public void setSignedjar(final File signedjar) {
  160. this.signedjar = signedjar;
  161. }
  162. /**
  163. * Flag to include the .SF file inside the signature; optional; default
  164. * false
  165. *
  166. * @param internalsf if true include the .SF file inside the signature
  167. */
  168. public void setInternalsf(final boolean internalsf) {
  169. this.internalsf = internalsf;
  170. }
  171. /**
  172. * flag to compute hash of entire manifest; optional, default false
  173. *
  174. * @param sectionsonly flag to compute hash of entire manifest
  175. */
  176. public void setSectionsonly(final boolean sectionsonly) {
  177. this.sectionsonly = sectionsonly;
  178. }
  179. /**
  180. * flag to control whether the presence of a signature file means a JAR is
  181. * signed; optional, default false
  182. *
  183. * @param lazy flag to control whether the presence of a signature
  184. */
  185. public void setLazy(final boolean lazy) {
  186. this.lazy = lazy;
  187. }
  188. /**
  189. * Optionally sets the output directory to be used.
  190. *
  191. * @param destDir the directory in which to place signed jars
  192. * @since Ant 1.7
  193. */
  194. public void setDestDir(File destDir) {
  195. this.destDir = destDir;
  196. }
  197. /**
  198. * add a mapper to determine file naming policy. Only used with toDir
  199. * processing.
  200. *
  201. * @param newMapper the mapper to add.
  202. * @since Ant 1.7
  203. */
  204. public void add(FileNameMapper newMapper) {
  205. if (mapper != null) {
  206. throw new BuildException(ERROR_TOO_MANY_MAPPERS);
  207. }
  208. mapper = newMapper;
  209. }
  210. /**
  211. * get the active mapper; may be null
  212. * @return mapper or null
  213. * @since Ant 1.7
  214. */
  215. public FileNameMapper getMapper() {
  216. return mapper;
  217. }
  218. /**
  219. * get the -tsaurl url
  220. * @return url or null
  221. * @since Ant 1.7
  222. */
  223. public String getTsaurl() {
  224. return tsaurl;
  225. }
  226. /**
  227. *
  228. * @param tsaurl the tsa url.
  229. * @since Ant 1.7
  230. */
  231. public void setTsaurl(String tsaurl) {
  232. this.tsaurl = tsaurl;
  233. }
  234. /**
  235. * Get the proxy host to be used when connecting to the TSA url
  236. * @return url or null
  237. * @since Ant 1.9.5
  238. */
  239. public String getTsaproxyhost() {
  240. return tsaproxyhost;
  241. }
  242. /**
  243. *
  244. * @param tsaproxyhost the proxy host to be used when connecting to the TSA.
  245. * @since Ant 1.9.5
  246. */
  247. public void setTsaproxyhost(String tsaproxyhost) {
  248. this.tsaproxyhost = tsaproxyhost;
  249. }
  250. /**
  251. * Get the proxy host to be used when connecting to the TSA url
  252. * @return url or null
  253. * @since Ant 1.9.5
  254. */
  255. public String getTsaproxyport() {
  256. return tsaproxyport;
  257. }
  258. /**
  259. *
  260. * @param tsaproxyport the proxy port to be used when connecting to the TSA.
  261. * @since Ant 1.9.5
  262. */
  263. public void setTsaproxyport(String tsaproxyport) {
  264. this.tsaproxyport = tsaproxyport;
  265. }
  266. /**
  267. * get the -tsacert option
  268. * @since Ant 1.7
  269. * @return a certificate alias or null
  270. */
  271. public String getTsacert() {
  272. return tsacert;
  273. }
  274. /**
  275. * set the alias in the keystore of the TSA to use;
  276. * @param tsacert the cert alias.
  277. */
  278. public void setTsacert(String tsacert) {
  279. this.tsacert = tsacert;
  280. }
  281. /**
  282. * Whether to force signing of a jar even it is already signed.
  283. * @param b boolean
  284. * @since Ant 1.8.0
  285. */
  286. public void setForce(boolean b) {
  287. force = b;
  288. }
  289. /**
  290. * Should the task force signing of a jar even it is already
  291. * signed?
  292. * @return boolean
  293. * @since Ant 1.8.0
  294. */
  295. public boolean isForce() {
  296. return force;
  297. }
  298. /**
  299. * Signature Algorithm; optional
  300. *
  301. * @param sigAlg the signature algorithm
  302. */
  303. public void setSigAlg(String sigAlg) {
  304. this.sigAlg = sigAlg;
  305. }
  306. /**
  307. * Signature Algorithm; optional
  308. *
  309. * @return String
  310. */
  311. public String getSigAlg() {
  312. return sigAlg;
  313. }
  314. /**
  315. * Digest Algorithm; optional
  316. *
  317. * @param digestAlg the digest algorithm
  318. */
  319. public void setDigestAlg(String digestAlg) {
  320. this.digestAlg = digestAlg;
  321. }
  322. /**
  323. * Digest Algorithm; optional
  324. *
  325. * @return String
  326. */
  327. public String getDigestAlg() {
  328. return digestAlg;
  329. }
  330. /**
  331. * TSA Digest Algorithm; optional
  332. *
  333. * @param digestAlg the tsa digest algorithm
  334. * @since Ant 1.10.2
  335. */
  336. public void setTSADigestAlg(String digestAlg) {
  337. this.tsaDigestAlg = digestAlg;
  338. }
  339. /**
  340. * TSA Digest Algorithm; optional
  341. *
  342. * @return String
  343. * @since Ant 1.10.2
  344. */
  345. public String getTSADigestAlg() {
  346. return tsaDigestAlg;
  347. }
  348. /**
  349. * sign the jar(s)
  350. *
  351. * @throws BuildException on errors
  352. */
  353. @Override
  354. public void execute() throws BuildException {
  355. //validation logic
  356. final boolean hasJar = jar != null;
  357. final boolean hasSignedJar = signedjar != null;
  358. final boolean hasDestDir = destDir != null;
  359. final boolean hasMapper = mapper != null;
  360. if (!hasJar && !hasResources()) {
  361. throw new BuildException(ERROR_NO_SOURCE);
  362. }
  363. if (null == alias) {
  364. throw new BuildException(ERROR_NO_ALIAS);
  365. }
  366. if (null == storepass) {
  367. throw new BuildException(ERROR_NO_STOREPASS);
  368. }
  369. if (hasDestDir && hasSignedJar) {
  370. throw new BuildException(ERROR_TODIR_AND_SIGNEDJAR);
  371. }
  372. if (hasResources() && hasSignedJar) {
  373. throw new BuildException(ERROR_SIGNEDJAR_AND_PATHS);
  374. }
  375. //this isn't strictly needed, but by being fussy now,
  376. //we can change implementation details later
  377. if (!hasDestDir && hasMapper) {
  378. throw new BuildException(ERROR_MAPPER_WITHOUT_DEST);
  379. }
  380. beginExecution();
  381. try {
  382. //special case single jar handling with signedjar attribute set
  383. if (hasJar && hasSignedJar) {
  384. // single jar processing
  385. signOneJar(jar, signedjar);
  386. //return here.
  387. return;
  388. }
  389. //the rest of the method treats single jar like
  390. //a nested path with one file
  391. Path sources = createUnifiedSourcePath();
  392. //set up our mapping policy
  393. FileNameMapper destMapper = hasMapper ? mapper : new IdentityMapper();
  394. //at this point the paths are set up with lists of files,
  395. //and the mapper is ready to map from source dirs to dest files
  396. //now we iterate through every JAR giving source and dest names
  397. // deal with the paths
  398. for (Resource r : sources) {
  399. FileResource fr = ResourceUtils
  400. .asFileResource(r.as(FileProvider.class));
  401. //calculate our destination directory; it is either the destDir
  402. //attribute, or the base dir of the fileset (for in situ updates)
  403. File toDir = hasDestDir ? destDir : fr.getBaseDir();
  404. //determine the destination filename via the mapper
  405. String[] destFilenames = destMapper.mapFileName(fr.getName());
  406. if (destFilenames == null || destFilenames.length != 1) {
  407. //we only like simple mappers.
  408. throw new BuildException(ERROR_BAD_MAP + fr.getFile());
  409. }
  410. File destFile = new File(toDir, destFilenames[0]);
  411. signOneJar(fr.getFile(), destFile);
  412. }
  413. } finally {
  414. endExecution();
  415. }
  416. }
  417. /**
  418. * Sign one jar.
  419. * <p/>
  420. * The signing only takes place if {@link #isUpToDate(File, File)} indicates
  421. * that it is needed.
  422. *
  423. * @param jarSource source to sign
  424. * @param jarTarget target; may be null
  425. * @throws BuildException if something goes wrong
  426. */
  427. private void signOneJar(File jarSource, File jarTarget)
  428. throws BuildException {
  429. File targetFile = jarTarget;
  430. if (targetFile == null) {
  431. targetFile = jarSource;
  432. }
  433. if (isUpToDate(jarSource, targetFile)) {
  434. return;
  435. }
  436. long lastModified = jarSource.lastModified();
  437. final ExecTask cmd = createJarSigner();
  438. setCommonOptions(cmd);
  439. bindToKeystore(cmd);
  440. if (null != sigfile) {
  441. addValue(cmd, "-sigfile");
  442. String value = this.sigfile;
  443. addValue(cmd, value);
  444. }
  445. try {
  446. //DO NOT SET THE -signedjar OPTION if source==dest
  447. //unless you like fielding hotspot crash reports
  448. if (!FILE_UTILS.areSame(jarSource, targetFile)) {
  449. addValue(cmd, "-signedjar");
  450. addValue(cmd, targetFile.getPath());
  451. }
  452. } catch (IOException ioex) {
  453. throw new BuildException(ioex);
  454. }
  455. if (internalsf) {
  456. addValue(cmd, "-internalsf");
  457. }
  458. if (sectionsonly) {
  459. addValue(cmd, "-sectionsonly");
  460. }
  461. if (sigAlg != null) {
  462. addValue(cmd, "-sigalg");
  463. addValue(cmd, sigAlg);
  464. }
  465. if (digestAlg != null) {
  466. addValue(cmd, "-digestalg");
  467. addValue(cmd, digestAlg);
  468. }
  469. //add -tsa operations if declared
  470. addTimestampAuthorityCommands(cmd);
  471. //JAR source is required
  472. addValue(cmd, jarSource.getPath());
  473. //alias is required for signing
  474. addValue(cmd, alias);
  475. log("Signing JAR: "
  476. + jarSource.getAbsolutePath()
  477. + " to "
  478. + targetFile.getAbsolutePath()
  479. + " as " + alias);
  480. cmd.execute();
  481. // restore the lastModified attribute
  482. if (preserveLastModified) {
  483. FILE_UTILS.setFileLastModified(targetFile, lastModified);
  484. }
  485. }
  486. /**
  487. * If the tsa parameters are set, this passes them to the command.
  488. * There is no validation of java version, as third party JDKs
  489. * may implement this on earlier/later jarsigner implementations.
  490. * @param cmd the exec task.
  491. */
  492. private void addTimestampAuthorityCommands(final ExecTask cmd) {
  493. if (tsaurl != null) {
  494. addValue(cmd, "-tsa");
  495. addValue(cmd, tsaurl);
  496. }
  497. if (tsacert != null) {
  498. addValue(cmd, "-tsacert");
  499. addValue(cmd, tsacert);
  500. }
  501. if (tsaproxyhost != null) {
  502. if (tsaurl == null || tsaurl.startsWith("https")) {
  503. addProxyFor(cmd, "https");
  504. }
  505. if (tsaurl == null || !tsaurl.startsWith("https")) {
  506. addProxyFor(cmd, "http");
  507. }
  508. }
  509. if (tsaDigestAlg != null) {
  510. addValue(cmd, "-tsadigestalg");
  511. addValue(cmd, tsaDigestAlg);
  512. }
  513. }
  514. /**
  515. * <p>Compare a jar file with its corresponding signed jar. The logic for this
  516. * is complex, and best explained in the source itself. Essentially if
  517. * either file doesn't exist, or the destfile has an out of date timestamp,
  518. * then the return value is false.</p>
  519. *
  520. * <p>If we are signing ourself, the check {@link #isSigned(File)} is used to
  521. * trigger the process.</p>
  522. *
  523. * @param jarFile the unsigned jar file
  524. * @param signedjarFile the result signed jar file
  525. * @return true if the signedjarFile is considered up to date
  526. */
  527. protected boolean isUpToDate(File jarFile, File signedjarFile) {
  528. if (isForce() || null == jarFile || !jarFile.exists()) {
  529. //these are pathological cases, but retained in case somebody
  530. //subclassed us.
  531. return false;
  532. }
  533. //we normally compare destination with source
  534. File destFile = signedjarFile;
  535. if (destFile == null) {
  536. //but if no dest is specified, compare source to source
  537. destFile = jarFile;
  538. }
  539. //if, by any means, the destfile and source match,
  540. if (jarFile.equals(destFile)) {
  541. if (lazy) {
  542. //we check the presence of signatures on lazy signing
  543. return isSigned(jarFile);
  544. }
  545. //unsigned or non-lazy self signings are always false
  546. return false;
  547. }
  548. //if they are different, the timestamps are used
  549. return FILE_UTILS.isUpToDate(jarFile, destFile);
  550. }
  551. /**
  552. * test for a file being signed, by looking for a signature in the META-INF
  553. * directory with our alias/sigfile.
  554. *
  555. * @param file the file to be checked
  556. * @return true if the file is signed
  557. * @see IsSigned#isSigned(File, String)
  558. */
  559. protected boolean isSigned(File file) {
  560. try {
  561. return IsSigned.isSigned(file, sigfile == null ? alias : sigfile);
  562. } catch (IOException e) {
  563. //just log this
  564. log(e.toString(), Project.MSG_VERBOSE);
  565. return false;
  566. }
  567. }
  568. /**
  569. * true to indicate that the signed jar modification date remains the same
  570. * as the original. Defaults to false
  571. *
  572. * @param preserveLastModified if true preserve the last modified time
  573. */
  574. public void setPreserveLastModified(boolean preserveLastModified) {
  575. this.preserveLastModified = preserveLastModified;
  576. }
  577. private void addProxyFor(final ExecTask cmd, final String scheme) {
  578. addValue(cmd, "-J-D" + scheme + ".proxyHost=" + tsaproxyhost);
  579. if (tsaproxyport != null) {
  580. addValue(cmd, "-J-D" + scheme + ".proxyPort=" + tsaproxyport);
  581. }
  582. }
  583. }