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}