1 /* 2 * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.security.tools.jarsigner; 27 28 import com.sun.jarsigner.ContentSigner; 29 import com.sun.jarsigner.ContentSignerParameters; 30 import sun.misc.IOUtils; 31 import sun.security.util.ManifestDigester; 32 import sun.security.util.SignatureFileVerifier; 33 import sun.security.x509.AlgorithmId; 34 35 import jdk.security.jarsigner.JarSigner; 36 import jdk.security.jarsigner.JarSignerException; 37 38 import java.io.ByteArrayInputStream; 39 import java.io.ByteArrayOutputStream; 40 import java.io.IOException; 41 import java.io.InputStream; 42 import java.io.OutputStream; 43 import java.io.PrintStream; 44 import java.net.SocketTimeoutException; 45 import java.net.URI; 46 import java.security.InvalidKeyException; 47 import java.security.MessageDigest; 48 import java.security.NoSuchAlgorithmException; 49 import java.security.PrivateKey; 50 import java.security.Provider; 51 import java.security.Signature; 52 import java.security.SignatureException; 53 import java.security.cert.CertificateException; 54 import java.security.cert.X509Certificate; 55 import java.util.Base64; 56 import java.util.Enumeration; 57 import java.util.Iterator; 58 import java.util.Locale; 59 import java.util.Map; 60 import java.util.Objects; 61 import java.util.Vector; 62 import java.util.jar.Attributes; 63 import java.util.jar.JarFile; 64 import java.util.jar.Manifest; 65 import java.util.zip.ZipEntry; 66 import java.util.zip.ZipFile; 67 import java.util.zip.ZipOutputStream; 68 69 /** 70 * The class that actually signs a jar. 71 * 72 * This class is separated from Builder to make sure the sign() method here 73 * does not modify any internal status of a JarSigner. All fields in Builder 74 * are duplicated here as final. 75 */ 76 public class Action { 77 78 private static final String META_INF = "META-INF/"; 79 80 // Signer materials 81 private final PrivateKey privateKey; 82 private final X509Certificate[] certChain; 83 84 // Below are all option settings 85 // Common: 86 private final String[] digestalg; 87 private final String sigalg; 88 private final Provider digestProvider; 89 private final Provider sigProvider; 90 private final URI tsaUrl; 91 private final String signerName; 92 // Seldom: 93 private final String tSAPolicyID; 94 private final String tSADigestAlg; 95 private final X509Certificate tsaCert; 96 // Useless: 97 private final boolean signManifest; // "sign" the whole manifest 98 private final boolean externalSF; // leave the .SF out of the PKCS7 block 99 private final ContentSigner signingMechanism; 100 101 private final Builder.EventHandler handler; 102 103 public Action(Builder builder) { 104 105 this.privateKey = builder.privateKey; 106 this.certChain = builder.certChain; 107 this.digestalg = builder.digestalg; 108 this.digestProvider = builder.digestProvider; 109 this.sigalg = builder.sigalg; 110 this.sigProvider = builder.sigProvider; 111 this.tsaUrl = builder.tsaUrl; 112 this.signerName = builder.signerName; 113 this.tSAPolicyID = builder.tSAPolicyID; 114 this.tSADigestAlg = builder.tSADigestAlg; 115 this.tsaCert = builder.tsaCert; 116 this.signManifest = builder.signManifest; 117 this.externalSF = builder.externalSF; 118 this.signingMechanism = builder.signingMechanism; 119 this.handler = builder.handler; 120 } 121 122 public void sign(ZipFile zipFile, OutputStream os) { 123 try { 124 sign0(zipFile, os); 125 } catch (SocketTimeoutException | CertificateException e) { 126 throw new JarSignerException("Error applying timestamp", e); 127 } catch (IOException ioe) { 128 throw new JarSignerException("I/O error", ioe); 129 } catch (NoSuchAlgorithmException | InvalidKeyException e) { 130 throw new JarSignerException("Signer error", e); 131 } catch (SignatureException se) { 132 throw new JarSignerException("Signing error", se); 133 } 134 } 135 136 private void sign0(ZipFile zipFile, OutputStream os) 137 throws IOException, CertificateException, NoSuchAlgorithmException, 138 SignatureException, InvalidKeyException { 139 String name; 140 if (signerName == null) { 141 name = "SIGNER"; 142 } else { 143 name = signerName; 144 } 145 146 MessageDigest[] digests; 147 try { 148 if (digestalg == null) { 149 digests = new MessageDigest[]{ 150 MessageDigest.getInstance(Builder.getDefaultDigestAlg())}; 151 } else { 152 digests = new MessageDigest[digestalg.length]; 153 for (int i = 0; i < digestalg.length; i++) { 154 if (digestProvider == null) { 155 digests[i] = MessageDigest.getInstance(digestalg[i]); 156 } else { 157 digests[i] = MessageDigest.getInstance( 158 digestalg[i], digestProvider); 159 } 160 } 161 } 162 } catch (NoSuchAlgorithmException asae) { 163 // Should not happen. User provided alg were checked, and default 164 // alg should always be available. 165 throw new AssertionError(asae); 166 } 167 168 PrintStream ps = new PrintStream(os); 169 ZipOutputStream zos = new ZipOutputStream(ps); 170 171 Manifest manifest = new Manifest(); 172 Map<String, Attributes> mfEntries = manifest.getEntries(); 173 174 // The Attributes of manifest before updating 175 Attributes oldAttr = null; 176 177 boolean mfModified = false; 178 boolean mfCreated = false; 179 byte[] mfRawBytes = null; 180 181 // Check if manifest exists 182 ZipEntry mfFile; 183 if ((mfFile = getManifestFile(zipFile)) != null) { 184 // Manifest exists. Read its raw bytes. 185 mfRawBytes = IOUtils.readFully( 186 zipFile.getInputStream(mfFile), -1, true); 187 manifest.read(new ByteArrayInputStream(mfRawBytes)); 188 oldAttr = (Attributes) (manifest.getMainAttributes().clone()); 189 } else { 190 // Create new manifest 191 Attributes mattr = manifest.getMainAttributes(); 192 mattr.putValue(Attributes.Name.MANIFEST_VERSION.toString(), 193 "1.0"); 194 String javaVendor = System.getProperty("java.vendor"); 195 String jdkVersion = System.getProperty("java.version"); 196 mattr.putValue("Created-By", jdkVersion + " (" + javaVendor 197 + ")"); 198 mfFile = new ZipEntry(JarFile.MANIFEST_NAME); 199 mfCreated = true; 200 } 201 202 /* 203 * For each entry in jar 204 * (except for signature-related META-INF entries), 205 * do the following: 206 * 207 * - if entry is not contained in manifest, add it to manifest; 208 * - if entry is contained in manifest, calculate its hash and 209 * compare it with the one in the manifest; if they are 210 * different, replace the hash in the manifest with the newly 211 * generated one. (This may invalidate existing signatures!) 212 */ 213 Vector<ZipEntry> mfFiles = new Vector<>(); 214 215 boolean wasSigned = false; 216 217 for (Enumeration<? extends ZipEntry> enum_ = zipFile.entries(); 218 enum_.hasMoreElements(); ) { 219 ZipEntry ze = enum_.nextElement(); 220 221 if (ze.getName().startsWith(META_INF)) { 222 // Store META-INF files in vector, so they can be written 223 // out first 224 mfFiles.addElement(ze); 225 226 if (SignatureFileVerifier.isBlockOrSF( 227 ze.getName().toUpperCase(Locale.ENGLISH))) { 228 wasSigned = true; 229 } 230 231 if (SignatureFileVerifier.isSigningRelated(ze.getName())) { 232 // ignore signature-related and manifest files 233 continue; 234 } 235 } 236 237 if (manifest.getAttributes(ze.getName()) != null) { 238 // jar entry is contained in manifest, check and 239 // possibly update its digest attributes 240 if (updateDigests(ze, zipFile, digests, 241 manifest) == true) { 242 mfModified = true; 243 } 244 } else if (!ze.isDirectory()) { 245 // Add entry to manifest 246 Attributes attrs = getDigestAttributes(ze, zipFile, digests); 247 mfEntries.put(ze.getName(), attrs); 248 mfModified = true; 249 } 250 } 251 252 // Recalculate the manifest raw bytes if necessary 253 if (mfModified) { 254 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 255 manifest.write(baos); 256 if (wasSigned) { 257 byte[] newBytes = baos.toByteArray(); 258 if (mfRawBytes != null 259 && oldAttr.equals(manifest.getMainAttributes())) { 260 261 /* 262 * Note: 263 * 264 * The Attributes object is based on HashMap and can handle 265 * continuation columns. Therefore, even if the contents are 266 * not changed (in a Map view), the bytes that it write() 267 * may be different from the original bytes that it read() 268 * from. Since the signature on the main attributes is based 269 * on raw bytes, we must retain the exact bytes. 270 */ 271 272 int newPos = findHeaderEnd(newBytes); 273 int oldPos = findHeaderEnd(mfRawBytes); 274 275 if (newPos == oldPos) { 276 System.arraycopy(mfRawBytes, 0, newBytes, 0, oldPos); 277 } else { 278 // cat oldHead newTail > newBytes 279 byte[] lastBytes = new byte[oldPos + 280 newBytes.length - newPos]; 281 System.arraycopy(mfRawBytes, 0, lastBytes, 0, oldPos); 282 System.arraycopy(newBytes, newPos, lastBytes, oldPos, 283 newBytes.length - newPos); 284 newBytes = lastBytes; 285 } 286 } 287 mfRawBytes = newBytes; 288 } else { 289 mfRawBytes = baos.toByteArray(); 290 } 291 } 292 293 // Write out the manifest 294 if (mfModified) { 295 // manifest file has new length 296 mfFile = new ZipEntry(JarFile.MANIFEST_NAME); 297 } 298 if (mfCreated) { 299 handler.adding(mfFile.getName()); 300 } else if (mfModified) { 301 handler.updating(mfFile.getName()); 302 } 303 304 zos.putNextEntry(mfFile); 305 zos.write(mfRawBytes); 306 307 // Calculate SignatureFile (".SF") and SignatureBlockFile 308 ManifestDigester manDig = new ManifestDigester(mfRawBytes); 309 SignatureFile sf = new SignatureFile(digests, manifest, manDig, 310 name, signManifest); 311 312 byte[] block = null; 313 314 Signature signer; 315 if (sigalg == null) { 316 String alg = JarSigner.getDefaultSigAlg(privateKey.getAlgorithm()); 317 if (alg == null) { 318 throw new NoSuchAlgorithmException( 319 "No signature alg for " + privateKey.getAlgorithm()); 320 } 321 signer = Signature.getInstance(alg); 322 } else if (sigProvider == null ) { 323 signer = Signature.getInstance(sigalg); 324 } else { 325 signer = Signature.getInstance(sigalg, sigProvider); 326 } 327 signer.initSign(privateKey); 328 329 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 330 sf.write(baos); 331 332 byte[] content = baos.toByteArray(); 333 334 signer.update(content); 335 byte[] signature = signer.sign(); 336 ContentSignerParameters params = 337 new JarSignerParameters(null, tsaUrl, tsaCert, tSAPolicyID, 338 tSADigestAlg, signature, 339 signer.getAlgorithm(), certChain, content, zipFile); 340 block = sf.generateBlock(params, externalSF, signingMechanism); 341 342 String sfFilename = sf.getMetaName(); 343 String bkFilename = sf.getBlockName(privateKey); 344 345 ZipEntry sfFile = new ZipEntry(sfFilename); 346 ZipEntry bkFile = new ZipEntry(bkFilename); 347 348 long time = System.currentTimeMillis(); 349 sfFile.setTime(time); 350 bkFile.setTime(time); 351 352 // signature file 353 zos.putNextEntry(sfFile); 354 sf.write(zos); 355 356 if (zipFile.getEntry(sfFilename) != null) { 357 handler.updating(sfFilename); 358 } else { 359 handler.adding(sfFilename); 360 } 361 362 // signature block file 363 zos.putNextEntry(bkFile); 364 zos.write(block); 365 366 if (zipFile.getEntry(bkFilename) != null) { 367 handler.updating(bkFilename); 368 } else { 369 handler.adding(bkFilename); 370 } 371 372 // Write out all other META-INF files that we stored in the 373 // vector 374 for (int i = 0; i < mfFiles.size(); i++) { 375 ZipEntry ze = mfFiles.elementAt(i); 376 if (!ze.getName().equalsIgnoreCase(JarFile.MANIFEST_NAME) 377 && !ze.getName().equalsIgnoreCase(sfFilename) 378 && !ze.getName().equalsIgnoreCase(bkFilename)) { 379 if (manifest.getAttributes(ze.getName()) != null) { 380 handler.signing(ze.getName()); 381 } else if (!ze.isDirectory()) { 382 handler.adding(ze.getName()); 383 } 384 writeEntry(zipFile, zos, ze); 385 } 386 } 387 388 // Write out all other files 389 for (Enumeration<? extends ZipEntry> enum_ = zipFile.entries(); 390 enum_.hasMoreElements(); ) { 391 ZipEntry ze = enum_.nextElement(); 392 393 if (!ze.getName().startsWith(META_INF)) { 394 if (manifest.getAttributes(ze.getName()) != null) { 395 handler.signing(ze.getName()); 396 } else { 397 handler.adding(ze.getName()); 398 } 399 writeEntry(zipFile, zos, ze); 400 } 401 } 402 zipFile.close(); 403 zos.close(); 404 } 405 406 private void writeEntry(ZipFile zf, ZipOutputStream os, ZipEntry ze) 407 throws IOException { 408 ZipEntry ze2 = new ZipEntry(ze.getName()); 409 ze2.setMethod(ze.getMethod()); 410 ze2.setTime(ze.getTime()); 411 ze2.setComment(ze.getComment()); 412 ze2.setExtra(ze.getExtra()); 413 if (ze.getMethod() == ZipEntry.STORED) { 414 ze2.setSize(ze.getSize()); 415 ze2.setCrc(ze.getCrc()); 416 } 417 os.putNextEntry(ze2); 418 writeBytes(zf, ze, os); 419 } 420 421 private void writeBytes 422 (ZipFile zf, ZipEntry ze, ZipOutputStream os) throws IOException { 423 try (InputStream is = zf.getInputStream(ze)) { 424 is.transferTo(os); 425 } 426 } 427 428 private boolean updateDigests(ZipEntry ze, ZipFile zf, 429 MessageDigest[] digests, 430 Manifest mf) throws IOException { 431 boolean update = false; 432 433 Attributes attrs = mf.getAttributes(ze.getName()); 434 String[] base64Digests = getDigests(ze, zf, digests); 435 436 for (int i = 0; i < digests.length; i++) { 437 // The entry name to be written into attrs 438 String name = null; 439 try { 440 // Find if the digest already exists. An algorithm could have 441 // different names. For example, last time it was SHA, and this 442 // time it's SHA-1. 443 AlgorithmId aid = AlgorithmId.get(digests[i].getAlgorithm()); 444 for (Object key : attrs.keySet()) { 445 if (key instanceof Attributes.Name) { 446 String n = ((Attributes.Name) key).toString(); 447 if (n.toUpperCase(Locale.ENGLISH).endsWith("-DIGEST")) { 448 String tmp = n.substring(0, n.length() - 7); 449 if (AlgorithmId.get(tmp).equals(aid)) { 450 name = n; 451 break; 452 } 453 } 454 } 455 } 456 } catch (NoSuchAlgorithmException nsae) { 457 // Ignored. Writing new digest entry. 458 } 459 460 if (name == null) { 461 name = digests[i].getAlgorithm() + "-Digest"; 462 attrs.putValue(name, base64Digests[i]); 463 update = true; 464 } else { 465 // compare digests, and replace the one in the manifest 466 // if they are different 467 String mfDigest = attrs.getValue(name); 468 if (!mfDigest.equalsIgnoreCase(base64Digests[i])) { 469 attrs.putValue(name, base64Digests[i]); 470 update = true; 471 } 472 } 473 } 474 return update; 475 } 476 477 private Attributes getDigestAttributes( 478 ZipEntry ze, ZipFile zf, MessageDigest[] digests) 479 throws IOException { 480 481 String[] base64Digests = getDigests(ze, zf, digests); 482 Attributes attrs = new Attributes(); 483 484 for (int i = 0; i < digests.length; i++) { 485 attrs.putValue(digests[i].getAlgorithm() + "-Digest", 486 base64Digests[i]); 487 } 488 return attrs; 489 } 490 491 /* 492 * Returns manifest entry from given jar file, or null if given jar file 493 * does not have a manifest entry. 494 */ 495 private ZipEntry getManifestFile(ZipFile zf) { 496 ZipEntry ze = zf.getEntry(JarFile.MANIFEST_NAME); 497 if (ze == null) { 498 // Check all entries for matching name 499 Enumeration<? extends ZipEntry> enum_ = zf.entries(); 500 while (enum_.hasMoreElements() && ze == null) { 501 ze = enum_.nextElement(); 502 if (!JarFile.MANIFEST_NAME.equalsIgnoreCase 503 (ze.getName())) { 504 ze = null; 505 } 506 } 507 } 508 return ze; 509 } 510 511 private String[] getDigests( 512 ZipEntry ze, ZipFile zf, MessageDigest[] digests) 513 throws IOException { 514 515 int n, i; 516 try (InputStream is = zf.getInputStream(ze)) { 517 long left = ze.getSize(); 518 byte[] buffer = new byte[8192]; 519 while ((left > 0) 520 && (n = is.read(buffer, 0, buffer.length)) != -1) { 521 for (i = 0; i < digests.length; i++) { 522 digests[i].update(buffer, 0, n); 523 } 524 left -= n; 525 } 526 } 527 528 // complete the digests 529 String[] base64Digests = new String[digests.length]; 530 for (i = 0; i < digests.length; i++) { 531 base64Digests[i] = Base64.getEncoder() 532 .encodeToString(digests[i].digest()); 533 } 534 return base64Digests; 535 } 536 537 @SuppressWarnings("fallthrough") 538 private int findHeaderEnd(byte[] bs) { 539 // Initial state true to deal with empty header 540 boolean newline = true; // just met a newline 541 int len = bs.length; 542 for (int i = 0; i < len; i++) { 543 switch (bs[i]) { 544 case '\r': 545 if (i < len - 1 && bs[i + 1] == '\n') i++; 546 // fallthrough 547 case '\n': 548 if (newline) return i + 1; //+1 to get length 549 newline = true; 550 break; 551 default: 552 newline = false; 553 } 554 } 555 // If header end is not found, it means the MANIFEST.MF has only 556 // the main attributes section and it does not end with 2 newlines. 557 // Returns the whole length so that it can be completely replaced. 558 return len; 559 } 560 561 static class SignatureFile { 562 563 /** 564 * SignatureFile 565 */ 566 Manifest sf; 567 568 /** 569 * .SF base name 570 */ 571 String baseName; 572 573 public SignatureFile(MessageDigest digests[], 574 Manifest mf, 575 ManifestDigester md, 576 String baseName, 577 boolean signManifest) { 578 579 this.baseName = baseName; 580 581 String version = System.getProperty("java.version"); 582 String javaVendor = System.getProperty("java.vendor"); 583 584 sf = new Manifest(); 585 Attributes mattr = sf.getMainAttributes(); 586 587 mattr.putValue(Attributes.Name.SIGNATURE_VERSION.toString(), "1.0"); 588 mattr.putValue("Created-By", version + " (" + javaVendor + ")"); 589 590 if (signManifest) { 591 for (int i = 0; i < digests.length; i++) { 592 mattr.putValue(digests[i].getAlgorithm() + "-Digest-Manifest", 593 Base64.getEncoder().encodeToString(md.manifestDigest(digests[i]))); 594 } 595 } 596 597 // create digest of the manifest main attributes 598 ManifestDigester.Entry mde = 599 md.get(ManifestDigester.MF_MAIN_ATTRS, false); 600 if (mde != null) { 601 for (int i = 0; i < digests.length; i++) { 602 mattr.putValue(digests[i].getAlgorithm() + 603 "-Digest-" + ManifestDigester.MF_MAIN_ATTRS, 604 Base64.getEncoder().encodeToString(mde.digest(digests[i]))); 605 } 606 } else { 607 throw new IllegalStateException 608 ("ManifestDigester failed to create " + 609 "Manifest-Main-Attribute entry"); 610 } 611 612 // go through the manifest entries and create the digests 613 Map<String, Attributes> entries = sf.getEntries(); 614 Iterator<Map.Entry<String, Attributes>> mit = 615 mf.getEntries().entrySet().iterator(); 616 while (mit.hasNext()) { 617 Map.Entry<String, Attributes> e = mit.next(); 618 String name = e.getKey(); 619 mde = md.get(name, false); 620 if (mde != null) { 621 Attributes attr = new Attributes(); 622 for (int i = 0; i < digests.length; i++) { 623 attr.putValue(digests[i].getAlgorithm() + "-Digest", 624 Base64.getEncoder().encodeToString(mde.digest(digests[i]))); 625 } 626 entries.put(name, attr); 627 } 628 } 629 } 630 631 // Write .SF file 632 public void write(OutputStream out) throws IOException { 633 sf.write(out); 634 } 635 636 // get .SF file name 637 public String getMetaName() { 638 return "META-INF/" + baseName + ".SF"; 639 } 640 641 // get .DSA (or .DSA, .EC) file name 642 public String getBlockName(PrivateKey privateKey) { 643 String keyAlgorithm = privateKey.getAlgorithm(); 644 return "META-INF/" + baseName + "." + keyAlgorithm; 645 } 646 647 // Generates the PKCS#7 content of block file 648 public byte[] generateBlock(ContentSignerParameters params, 649 boolean externalSF, 650 ContentSigner signingMechanism) 651 throws NoSuchAlgorithmException, IOException, CertificateException { 652 653 if (signingMechanism == null) { 654 signingMechanism = new TimestampedSigner(); 655 } 656 return signingMechanism.generateSignedData( 657 params, externalSF, 658 params.getTimestampingAuthority() != null 659 || params.getTimestampingAuthorityCertificate() != null); 660 } 661 } 662 663 class JarSignerParameters implements ContentSignerParameters { 664 665 private String[] args; 666 private URI tsa; 667 private X509Certificate tsaCertificate; 668 private byte[] signature; 669 private String signatureAlgorithm; 670 private X509Certificate[] signerCertificateChain; 671 private byte[] content; 672 private ZipFile source; 673 private String tSAPolicyID; 674 private String tSADigestAlg; 675 676 JarSignerParameters(String[] args, URI tsa, X509Certificate tsaCertificate, 677 String tSAPolicyID, String tSADigestAlg, 678 byte[] signature, String signatureAlgorithm, 679 X509Certificate[] signerCertificateChain, byte[] content, 680 ZipFile source) { 681 682 Objects.requireNonNull(signature); 683 Objects.requireNonNull(signatureAlgorithm); 684 Objects.requireNonNull(signerCertificateChain); 685 686 if (tSADigestAlg == null) { 687 tSADigestAlg = Builder.getDefaultDigestAlg(); 688 } 689 690 this.args = args; 691 this.tsa = tsa; 692 this.tsaCertificate = tsaCertificate; 693 this.tSAPolicyID = tSAPolicyID; 694 this.tSADigestAlg = tSADigestAlg; 695 this.signature = signature; 696 this.signatureAlgorithm = signatureAlgorithm; 697 this.signerCertificateChain = signerCertificateChain; 698 this.content = content; 699 this.source = source; 700 } 701 702 public String[] getCommandLine() { 703 return args; 704 } 705 706 public URI getTimestampingAuthority() { 707 return tsa; 708 } 709 710 public X509Certificate getTimestampingAuthorityCertificate() { 711 return tsaCertificate; 712 } 713 714 public String getTSAPolicyID() { 715 return tSAPolicyID; 716 } 717 718 public String getTSADigestAlg() { 719 return tSADigestAlg; 720 } 721 722 public byte[] getSignature() { 723 return signature; 724 } 725 726 public String getSignatureAlgorithm() { 727 return signatureAlgorithm; 728 } 729 730 public X509Certificate[] getSignerCertificateChain() { 731 return signerCertificateChain; 732 } 733 734 public byte[] getContent() { 735 return content; 736 } 737 738 public ZipFile getSource() { 739 return source; 740 } 741 } 742 }