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