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 }