android_tools/java_tools/jarsigner.rs
1use crate::error::*;
2use std::path::{Path, PathBuf};
3use std::process::Command;
4
5/// Signs and verifies Java Archive (JAR) files
6#[derive(Clone, Default)]
7pub struct JarSigner {
8 verify: bool,
9 jar_file: PathBuf,
10 alias: String,
11 keystore: Option<PathBuf>,
12 storepass: Option<String>,
13 storetype: Option<String>,
14 keypass: Option<String>,
15 certchain: Option<PathBuf>,
16 sigfile: Option<PathBuf>,
17 signedjar: Option<String>,
18 digestalg: Option<String>,
19 sigalg: Option<String>,
20 verbose: bool,
21 certs: bool,
22 rev_check: bool,
23 tsa: Option<PathBuf>,
24 tsacert: Option<String>,
25 tsapolicyid: Option<String>,
26 tsadigestalg: Option<String>,
27 altsigner: Option<String>,
28 altsignerpath: Option<Vec<PathBuf>>,
29 internalsf: bool,
30 sectionsonly: bool,
31 protected: bool,
32 provider_name: Option<String>,
33 addprovider: Option<String>,
34 provider_class: Option<String>,
35 provider_arg: Option<PathBuf>,
36 strict: bool,
37 conf: Option<PathBuf>,
38 h: bool,
39 help: bool,
40}
41
42impl JarSigner {
43 /// ## JarFile
44 /// The JAR file to be signed.
45 ///
46 /// If you also specified the -strict option, and the jarsigner command detected
47 /// severe warnings, the message, "jar signed, with signer errors" is displayed.
48 ///
49 /// ## Alias
50 /// The aliases are defined in the keystore specified by `-keystore` or the default
51 /// keystore
52 pub fn new(jar_file: &Path, alias: &str) -> Self {
53 Self {
54 jar_file: jar_file.to_owned(),
55 alias: alias.to_owned(),
56 ..Default::default()
57 }
58 }
59
60 /// Specifies the URL that tells the keystore location. This defaults to the file
61 /// `.keystore` in the user's home directory, as determined by the `user.home` system
62 /// property. A keystore is required when signing. You must explicitly specify a
63 /// keystore when the default keystore does not exist or if you want to use one other
64 /// than the default. A keystore is not required when verifying, but if one is
65 /// specified or the default exists and the `-verbose` option was also specified, then
66 /// additional information is output regarding whether or not any of the certificates
67 /// used to verify the JAR file are contained in that keystore. The `-keystore` argument
68 /// can be a file name and path specification rather than a URL, in which case it is
69 /// treated the same as a file: URL, for example, the following are equivalent:
70 ///
71 /// ```sh
72 /// * `-keystore filePathAndName`
73 /// * `-keystore file:filePathAndName`
74 /// ```
75 ///
76 /// If the Sun PKCS #11 provider was configured in the java.security security
77 /// properties file (located in the JRE's `$JAVA_HOME/lib/security directory`), then the
78 /// keytool and jarsigner tools can operate on the PKCS #11 token by specifying these
79 /// options:
80 ///
81 /// ```sh
82 /// * `-keystore NONE`
83 /// * `-storetype PKCS11`
84 /// ```
85 ///
86 /// For example, the following command lists the contents of the configured PKCS#11
87 /// token:
88 ///
89 /// ```sh
90 /// * `keytool -keystore NONE -storetype PKCS11 -list`
91 /// ```
92 pub fn keystore(&mut self, keystore: &Path) -> &mut Self {
93 self.keystore = Some(keystore.to_owned());
94 self
95 }
96
97 /// Specifies the password that is required to access the keystore. This is only
98 /// needed when signing (not verifying) a JAR file. In that case, if a `-storepass`
99 /// option is not provided at the command line, then the user is prompted for the
100 /// password. If the modifier env or file is not specified, then the password has the
101 /// value argument. Otherwise, the password is retrieved as follows:
102 ///
103 /// * `env:` Retrieve the password from the environment variable named argument
104 /// * `file:` Retrieve the password from the file named argument
105 pub fn storepass(&mut self, storepass: String) -> &mut Self {
106 self.storepass = Some(storepass);
107 self
108 }
109
110 /// Specifies the type of keystore to be instantiated. The default keystore type is
111 /// the one that is specified as the value of the `keystore.type` property in the
112 /// security properties file, which is returned by the static `getDefaultType` method
113 /// in `java.security.KeyStore`. The PIN for a PCKS #11 token can also be
114 /// specified with the `-storepass` option. If none is specified, then the `keytool`
115 /// and `jarsigner` commands prompt for the token PIN. If the token has a protected
116 /// authentication path (such as a dedicated PIN-pad or a biometric reader), then
117 /// the `-protected` option must be specified and no password options can be
118 /// specified
119 pub fn storetype(&mut self, storetype: String) -> &mut Self {
120 self.storetype = Some(storetype);
121 self
122 }
123
124 /// Specifies the password used to protect the private key of the keystore entry
125 /// addressed by the alias specified on the command line. The password is required
126 /// when using jarsigner to sign a JAR file. If no password is provided on the command
127 /// line, and the required password is different from the store password, then the
128 /// user is prompted for it
129 ///
130 /// If the modifier env or file is not specified, then the password has the value
131 /// argument. Otherwise, the password is retrieved as follows:
132 ///
133 /// * `env:` Retrieve the password from the environment variable named argument
134 /// * `file:` Retrieve the password from the file named argument
135 ///
136 /// ## Note
137 /// The password should not be specified on the command line or in a script unless it
138 /// is for testing purposes, or you are on a secure system
139 pub fn keypass(&mut self, keypass: String) -> &mut Self {
140 self.keypass = Some(keypass);
141 self
142 }
143
144 /// Specifies the certificate chain to be used when the certificate chain associated
145 /// with the private key of the keystore entry that is addressed by the alias
146 /// specified on the command line is not complete. This can happen when the keystore
147 /// is located on a hardware token where there is not enough capacity to hold a
148 /// complete certificate chain. The file can be a sequence of concatenated X.509
149 /// certificates, or a single PKCS#7 formatted data block, either in binary encoding
150 /// format or in printable encoding format (also known as Base64 encoding) as defined
151 /// by the Internet RFC 1421 standard
152 pub fn certchain(&mut self, certchain: &Path) -> &mut Self {
153 self.certchain = Some(certchain.to_owned());
154 self
155 }
156
157 /// Specifies the base file name to be used for the generated `.SF` and `.DSA` files. For
158 /// example, if file is DUKESIGN, then the generated .SF and .DSA files are named
159 /// `DUKESIGN.SF` and `DUKESIGN.DSA`, and placed in the META-INF directory of the signed
160 /// JAR file
161 ///
162 /// The characters in the file must come from the set a-zA-Z0-9_-. Only
163 /// letters, numbers, underscore, and hyphen characters are allowed. All lowercase
164 /// characters are converted to uppercase for the .SF and .DSA file names
165 ///
166 /// If no -sigfile option appears on the command line, then the base file name for the
167 /// `.SF` and `.DSA` files is the first 8 characters of the alias name specified on
168 /// the command line, all converted to upper case. If the alias name has fewer
169 /// than 8 characters, then the full alias name is used. If the alias name
170 /// contains any characters that are not valid in a signature file name, then each
171 /// such character is converted to an underscore (_) character to form the file
172 /// name
173 pub fn sigfile(&mut self, sigfile: &Path) -> &mut Self {
174 self.sigfile = Some(sigfile.to_owned());
175 self
176 }
177
178 /// Name of signed JAR file
179 pub fn signedjar(&mut self, signedjar: String) -> &mut Self {
180 self.signedjar = Some(signedjar);
181 self
182 }
183
184 /// Name of digest algorithm
185 pub fn digestalg(&mut self, digestalg: String) -> &mut Self {
186 self.digestalg = Some(digestalg);
187 self
188 }
189
190 /// Specifies the name of the signature algorithm to use to sign the JAR file
191 ///
192 /// For a list of standard signature algorithm names, see "Appendix A: Standard Names"
193 /// in the Java Cryptography Architecture (JCA) Reference Guide at http://docs.oracle.com/javase/8/docs/technotes/guides/security/crypto/CryptoSpec.html#AppA
194 ///
195 /// This algorithm must be compatible with the private key used to sign the JAR file.
196 /// If this option is not specified, then `SHA1withDSA`, `SHA256withRSA`, or
197 /// `SHA256withECDSA` are used depending on the type of private key. There must either
198 /// be a statically installed provider supplying an implementation of the specified
199 /// algorithm or the user must specify one with the `-providerClass` option; otherwise,
200 /// the command will not succeed
201 pub fn sigalg(&mut self, sigalg: String) -> &mut Self {
202 self.sigalg = Some(sigalg);
203 self
204 }
205
206 /// When the `-verbose` option appears on the command line, it indicates verbose mode,
207 /// which causes jarsigner to output extra information about the progress of the JAR
208 /// signing or verification
209 pub fn verbose(&mut self, verbose: bool) -> &mut Self {
210 self.verbose = verbose;
211 self
212 }
213
214 /// If the `-certs` option appears on the command line with the `-verify` and `-verbose`
215 /// options, then the output includes certificate information for each signer of the
216 /// JAR file. This information includes the name of the type of certificate (stored in
217 /// the `.DSA` file) that certifies the signer's public key, and if the certificate is
218 /// an X.509 certificate (an instance of the `java.security.cert.X509Certificate`), then
219 /// the distinguished name of the signer
220 ///
221 /// The keystore is also examined. If no keystore value is specified on the command
222 /// line, then the default keystore file (if any) is checked. If the public key
223 /// certificate for a signer matches an entry in the keystore, then the alias name for
224 /// the keystore entry for that signer is displayed in parentheses
225 pub fn certs(&mut self, certs: bool) -> &mut Self {
226 self.certs = certs;
227 self
228 }
229
230 /// Enable certificate revocation check
231 pub fn rev_check(&mut self, rev_check: bool) -> &mut Self {
232 self.rev_check = rev_check;
233 self
234 }
235
236 /// If [-tsa](http://example.tsa.url) appears on the command line when signing a JAR file
237 /// then a time stamp is generated for the signature. The URL,
238 /// identifies the location of the Time Stamping Authority (TSA) and overrides any URL
239 /// found with the -tsacert option. The `-tsa` option does not require the TSA public
240 /// key certificate to be present in the keystore
241 ///
242 /// To generate the time stamp, jarsigner communicates with the TSA with the
243 /// Time-Stamp Protocol (TSP) defined in RFC 3161. When successful, the time stamp
244 /// token returned by the TSA is stored with the signature in the signature block
245 /// file
246 pub fn tsa(&mut self, tsa: &Path) -> &mut Self {
247 self.tsa = Some(tsa.to_owned());
248 self
249 }
250
251 /// When `-tsacert` alias appears on the command line when signing a JAR file, a time
252 /// stamp is generated for the signature. The alias identifies the TSA public key
253 /// certificate in the keystore that is in effect. The entry's certificate is examined
254 /// for a Subject Information Access extension that contains a URL identifying the
255 /// location of the TSA
256 ///
257 /// The TSA public key certificate must be present in the keystore when using the
258 /// `-tsacert` option
259 pub fn tsacert(&mut self, tsacert: String) -> &mut Self {
260 self.tsacert = Some(tsacert);
261 self
262 }
263
264 /// TSAPolicyID for Timestamping Authority
265 pub fn tsapolicyid(&mut self, tsapolicyid: String) -> &mut Self {
266 self.tsapolicyid = Some(tsapolicyid);
267 self
268 }
269
270 /// Algorithm of digest data in timestamping request
271 pub fn tsadigestalg(&mut self, tsadigestalg: String) -> &mut Self {
272 self.tsadigestalg = Some(tsadigestalg);
273 self
274 }
275
276 /// Class name of an alternative signing mechanism (This option is deprecated and will
277 /// be removed in a future release.)
278 pub fn altsigner(&mut self, altsigner: String) -> &mut Self {
279 self.altsigner = Some(altsigner);
280 self
281 }
282
283 /// Location of an alternative signing mechanism (This option is deprecated and will
284 /// be removed in a future release.)
285 pub fn altsignerpath(&mut self, altsignerpath: &[PathBuf]) -> &mut Self {
286 self.altsignerpath = Some(altsignerpath.to_owned());
287 self
288 }
289
290 /// Include the `.SF` file inside the signature block
291 pub fn internalsf(&mut self, internalsf: bool) -> &mut Self {
292 self.internalsf = internalsf;
293 self
294 }
295
296 /// Don't compute hash of entire manifest
297 pub fn sectionsonly(&mut self, sectionsonly: bool) -> &mut Self {
298 self.sectionsonly = sectionsonly;
299 self
300 }
301
302 /// Keystore has protected authentication path
303 pub fn protected(&mut self, protected: bool) -> &mut Self {
304 self.protected = protected;
305 self
306 }
307
308 /// Provider name
309 pub fn provider_name(&mut self, provider_name: String) -> &mut Self {
310 self.provider_name = Some(provider_name);
311 self
312 }
313
314 /// Add security provider by name (e.g. SunPKCS11)
315 /// add security provider by fully-qualified class name
316 pub fn addprovider(&mut self, addprovider: String) -> &mut Self {
317 self.addprovider = Some(addprovider);
318 self
319 }
320
321 /// Configure argument for -addprovider
322 pub fn provider_class(&mut self, provider_class: String) -> &mut Self {
323 self.provider_class = Some(provider_class);
324 self
325 }
326
327 /// Configure argument for `-providerClass`
328 pub fn provider_arg(&mut self, provider_arg: &Path) -> &mut Self {
329 self.provider_arg = Some(provider_arg.to_owned());
330 self
331 }
332
333 /// Treat warnings as errors
334 pub fn strict(&mut self, strict: bool) -> &mut Self {
335 self.strict = strict;
336 self
337 }
338
339 /// Specify a pre-configured options file
340 pub fn conf(&mut self, conf: &Path) -> &mut Self {
341 self.conf = Some(conf.to_owned());
342 self
343 }
344
345 /// The `-verify` option can take zero or more keystore alias names after the JAR file
346 /// name. When the `-verify` option is specified, the jarsigner command checks that the
347 /// certificate used to verify each signed entry in the JAR file matches one of the
348 /// keystore aliases. The aliases are defined in the keystore specified by `-keystore`
349 /// or the default keystore.
350 ///
351 /// If you also specified the `-strict` option, and the jarsigner command detected
352 /// severe warnings, the message, "jar verified, with signer errors" is displayed
353 pub fn verify(&mut self, verify: bool) -> &mut Self {
354 self.verify = verify;
355 self
356 }
357
358 /// Print this help message
359 pub fn h(&mut self, h: bool) -> &mut Self {
360 self.h = h;
361 self
362 }
363
364 /// Print this help message
365 pub fn help(&mut self, help: bool) -> &mut Self {
366 self.help = help;
367 self
368 }
369
370 /// Runs jarsigner commands and signa JAR file with arguments
371 pub fn run(&self) -> Result<PathBuf> {
372 let mut jarsigner = jarsigner_tool()?;
373 if self.verify {
374 jarsigner.arg("-verify");
375 }
376 jarsigner.arg(&self.jar_file);
377 jarsigner.arg(&self.alias);
378 if let Some(keystore) = &self.keystore {
379 jarsigner.arg("-keystore").arg(keystore);
380 }
381 if let Some(storepass) = &self.storepass {
382 jarsigner.arg("-storepass").arg(storepass);
383 }
384 if let Some(storetype) = &self.storetype {
385 jarsigner.arg("-storetype").arg(storetype);
386 }
387 if let Some(keypass) = &self.keypass {
388 jarsigner.arg("-keypass").arg(keypass);
389 }
390 if let Some(certchain) = &self.certchain {
391 jarsigner.arg("-certchain").arg(certchain);
392 }
393 if let Some(sigfile) = &self.sigfile {
394 jarsigner.arg("-sigfile").arg(sigfile);
395 }
396 if let Some(signedjar) = &self.signedjar {
397 jarsigner.arg("-signedjar").arg(signedjar);
398 }
399 if let Some(digestalg) = &self.digestalg {
400 jarsigner.arg("-digestalg").arg(digestalg);
401 }
402 if let Some(sigalg) = &self.sigalg {
403 jarsigner.arg("-sigalg").arg(sigalg);
404 }
405 if self.verbose {
406 jarsigner.arg("-verbose");
407 }
408 if self.certs {
409 jarsigner.arg("-certs");
410 }
411 if self.rev_check {
412 jarsigner.arg("-revCheck");
413 }
414 if let Some(tsa) = &self.tsa {
415 jarsigner.arg("-tsa").arg(tsa);
416 }
417 if let Some(tsacert) = &self.tsacert {
418 jarsigner.arg("-tsacert").arg(tsacert);
419 }
420 if let Some(tsapolicyid) = &self.tsapolicyid {
421 jarsigner.arg("-tsapolicyid").arg(tsapolicyid);
422 }
423 if let Some(tsadigestalg) = &self.tsadigestalg {
424 jarsigner.arg("-tsadigestalg").arg(tsadigestalg);
425 }
426 if let Some(altsigner) = &self.altsigner {
427 jarsigner.arg("-altsigner").arg(altsigner);
428 }
429 if let Some(altsignerpath) = &self.altsignerpath {
430 jarsigner.arg("-altsigner").arg(
431 altsignerpath
432 .iter()
433 .map(|v| v.to_string_lossy().to_string())
434 .collect::<Vec<String>>()
435 .join(","),
436 );
437 }
438 if self.internalsf {
439 jarsigner.arg("-internalsf");
440 }
441 if self.sectionsonly {
442 jarsigner.arg("-sectionsonly");
443 }
444 if self.protected {
445 jarsigner.arg("-protected");
446 }
447 if let Some(provider_name) = &self.provider_name {
448 jarsigner.arg("-providerName").arg(provider_name);
449 }
450 if let Some(addprovider) = &self.addprovider {
451 jarsigner.arg("-addprovider").arg(addprovider);
452 }
453 if let Some(provider_class) = &self.provider_class {
454 jarsigner.arg("-providerClass").arg(provider_class);
455 }
456 if let Some(provider_arg) = &self.provider_arg {
457 jarsigner.arg("-providerArg").arg(provider_arg);
458 }
459 if self.strict {
460 jarsigner.arg("-strict");
461 }
462 if let Some(conf) = &self.conf {
463 jarsigner.arg("-conf").arg(conf);
464 }
465 if self.h {
466 jarsigner.arg("-h");
467 }
468 if self.help {
469 jarsigner.arg("-help");
470 }
471 jarsigner.output_err(true)?;
472 Ok(self.jar_file.clone())
473 }
474}
475
476/// Signs and verifies `.aab` and Java Archive (JAR) files
477fn jarsigner_tool() -> Result<Command> {
478 if let Ok(jarsigner) = which::which(bin!("jarsigner")) {
479 return Ok(Command::new(jarsigner));
480 }
481 if let Ok(java) = std::env::var("JAVA_HOME") {
482 let keytool = PathBuf::from(java).join("bin").join(bin!("jarsigner.exe"));
483 if keytool.exists() {
484 return Ok(Command::new(keytool));
485 }
486 }
487 Err(Error::CmdNotFound("jarsigner".to_string()))
488}