History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/sun/security/tools/JarSigner.java (1 of 18352 files). Show all

Unified · Split

java/pkg/sun/security/tools/JarSigner.java+2445−0
@@ -0,0 +1,2445 @@
1+/*
2+ * Copyright (c) 1997, 2011, 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;
27+
28+import java.io.*;
29+import java.util.*;
30+import java.util.zip.*;
31+import java.util.jar.*;
32+import java.math.BigInteger;
33+import java.net.URI;
34+import java.net.URISyntaxException;
35+import java.text.Collator;
36+import java.text.MessageFormat;
37+import java.security.cert.Certificate;
38+import java.security.cert.X509Certificate;
39+import java.security.cert.CertificateException;
40+import java.security.*;
41+import java.lang.reflect.Constructor;
42+
43+import com.sun.jarsigner.ContentSigner;
44+import com.sun.jarsigner.ContentSignerParameters;
45+import java.net.SocketTimeoutException;
46+import java.net.URL;
47+import java.net.URLClassLoader;
48+import java.security.cert.CertPath;
49+import java.security.cert.CertPathValidator;
50+import java.security.cert.CertificateExpiredException;
51+import java.security.cert.CertificateFactory;
52+import java.security.cert.CertificateNotYetValidException;
53+import java.security.cert.PKIXParameters;
54+import java.security.cert.TrustAnchor;
55+import java.util.Map.Entry;
56+import sun.security.x509.*;
57+import sun.security.util.*;
58+import sun.misc.BASE64Encoder;
59+
60+
61+/**
62+ * <p>The jarsigner utility.
63+ *
64+ * The exit codes for the main method are:
65+ *
66+ * 0: success
67+ * 1: any error that the jar cannot be signed or verified, including:
68+ * keystore loading error
69+ * TSP communciation error
70+ * jarsigner command line error...
71+ * otherwise: error codes from -strict
72+ *
73+ * @author Roland Schemers
74+ * @author Jan Luehe
75+ */
76+
77+public class JarSigner {
78+
79+ // for i18n
80+ private static final java.util.ResourceBundle rb =
81+ java.util.ResourceBundle.getBundle
82+ ("sun.security.tools.JarSignerResources");
83+ private static final Collator collator = Collator.getInstance();
84+ static {
85+ // this is for case insensitive string comparisions
86+ collator.setStrength(Collator.PRIMARY);
87+ }
88+
89+ private static final String META_INF = "META-INF/";
90+
91+ // prefix for new signature-related files in META-INF directory
92+ private static final String SIG_PREFIX = META_INF + "SIG-";
93+
94+ private static final Class[] PARAM_STRING = { String.class };
95+
96+ private static final String NONE = "NONE";
97+ private static final String P11KEYSTORE = "PKCS11";
98+
99+ private static final long SIX_MONTHS = 180*24*60*60*1000L; //milliseconds
100+
101+ // Attention:
102+ // This is the entry that get launched by the security tool jarsigner.
103+ public static void main(String args[]) throws Exception {
104+ JarSigner js = new JarSigner();
105+ js.run(args);
106+ }
107+
108+ static final String VERSION = "1.0";
109+
110+ static final int IN_KEYSTORE = 0x01; // signer is in keystore
111+ static final int IN_SCOPE = 0x02;
112+ static final int NOT_ALIAS = 0x04; // alias list is NOT empty and
113+ // signer is not in alias list
114+ static final int SIGNED_BY_ALIAS = 0x08; // signer is in alias list
115+
116+ X509Certificate[] certChain; // signer's cert chain (when composing)
117+ PrivateKey privateKey; // private key
118+ KeyStore store; // the keystore specified by -keystore
119+ // or the default keystore, never null
120+
121+ String keystore; // key store file
122+ boolean nullStream = false; // null keystore input stream (NONE)
123+ boolean token = false; // token-based keystore
124+ String jarfile; // jar files to sign or verify
125+ String alias; // alias to sign jar with
126+ List<String> ckaliases = new ArrayList<>(); // aliases in -verify
127+ char[] storepass; // keystore password
128+ boolean protectedPath; // protected authentication path
129+ String storetype; // keystore type
130+ String providerName; // provider name
131+ Vector<String> providers = null; // list of providers
132+ // arguments for provider constructors
133+ HashMap<String,String> providerArgs = new HashMap<>();
134+ char[] keypass; // private key password
135+ String sigfile; // name of .SF file
136+ String sigalg; // name of signature algorithm
137+ String digestalg = "SHA-256"; // name of digest algorithm
138+ String signedjar; // output filename
139+ String tsaUrl; // location of the Timestamping Authority
140+ String tsaAlias; // alias for the Timestamping Authority's certificate
141+ String altCertChain; // file to read alternative cert chain from
142+ boolean verify = false; // verify the jar
143+ String verbose = null; // verbose output when signing/verifying
144+ boolean showcerts = false; // show certs when verifying
145+ boolean debug = false; // debug
146+ boolean signManifest = true; // "sign" the whole manifest
147+ boolean externalSF = true; // leave the .SF out of the PKCS7 block
148+ boolean strict = false; // treat warnings as error
149+
150+ // read zip entry raw bytes
151+ private ByteArrayOutputStream baos = new ByteArrayOutputStream(2048);
152+ private byte[] buffer = new byte[8192];
153+ private ContentSigner signingMechanism = null;
154+ private String altSignerClass = null;
155+ private String altSignerClasspath = null;
156+ private ZipFile zipFile = null;
157+
158+ private boolean hasExpiredCert = false;
159+ private boolean hasExpiringCert = false;
160+ private boolean notYetValidCert = false;
161+ private boolean chainNotValidated = false;
162+ private boolean notSignedByAlias = false;
163+ private boolean aliasNotInStore = false;
164+ private boolean hasUnsignedEntry = false;
165+ private boolean badKeyUsage = false;
166+ private boolean badExtendedKeyUsage = false;
167+ private boolean badNetscapeCertType = false;
168+
169+ CertificateFactory certificateFactory;
170+ CertPathValidator validator;
171+ PKIXParameters pkixParameters;
172+
173+ public void run(String args[]) {
174+ try {
175+ parseArgs(args);
176+
177+ // Try to load and install the specified providers
178+ if (providers != null) {
179+ ClassLoader cl = ClassLoader.getSystemClassLoader();
180+ Enumeration<String> e = providers.elements();
181+ while (e.hasMoreElements()) {
182+ String provName = e.nextElement();
183+ Class<?> provClass;
184+ if (cl != null) {
185+ provClass = cl.loadClass(provName);
186+ } else {
187+ provClass = Class.forName(provName);
188+ }
189+
190+ String provArg = providerArgs.get(provName);
191+ Object obj;
192+ if (provArg == null) {
193+ obj = provClass.newInstance();
194+ } else {
195+ Constructor<?> c =
196+ provClass.getConstructor(PARAM_STRING);
197+ obj = c.newInstance(provArg);
198+ }
199+
200+ if (!(obj instanceof Provider)) {
201+ MessageFormat form = new MessageFormat(rb.getString
202+ ("provName.not.a.provider"));
203+ Object[] source = {provName};
204+ throw new Exception(form.format(source));
205+ }
206+ Security.addProvider((Provider)obj);
207+ }
208+ }
209+
210+ if (verify) {
211+ try {
212+ loadKeyStore(keystore, false);
213+ } catch (Exception e) {
214+ if ((keystore != null) || (storepass != null)) {
215+ System.out.println(rb.getString("jarsigner.error.") +
216+ e.getMessage());
217+ System.exit(1);
218+ }
219+ }
220+ /* if (debug) {
221+ SignatureFileVerifier.setDebug(true);
222+ ManifestEntryVerifier.setDebug(true);
223+ }
224+ */
225+ verifyJar(jarfile);
226+ } else {
227+ loadKeyStore(keystore, true);
228+ getAliasInfo(alias);
229+
230+ // load the alternative signing mechanism
231+ if (altSignerClass != null) {
232+ signingMechanism = loadSigningMechanism(altSignerClass,
233+ altSignerClasspath);
234+ }
235+ signJar(jarfile, alias, args);
236+ }
237+ } catch (Exception e) {
238+ System.out.println(rb.getString("jarsigner.error.") + e);
239+ if (debug) {
240+ e.printStackTrace();
241+ }
242+ System.exit(1);
243+ } finally {
244+ // zero-out private key password
245+ if (keypass != null) {
246+ Arrays.fill(keypass, ' ');
247+ keypass = null;
248+ }
249+ // zero-out keystore password
250+ if (storepass != null) {
251+ Arrays.fill(storepass, ' ');
252+ storepass = null;
253+ }
254+ }
255+
256+ if (strict) {
257+ int exitCode = 0;
258+ if (hasExpiringCert) {
259+ exitCode |= 2;
260+ }
261+ if (chainNotValidated) {
262+ // hasExpiredCert and notYetValidCert included in this case
263+ exitCode |= 4;
264+ }
265+ if (badKeyUsage || badExtendedKeyUsage || badNetscapeCertType) {
266+ exitCode |= 8;
267+ }
268+ if (hasUnsignedEntry) {
269+ exitCode |= 16;
270+ }
271+ if (notSignedByAlias || aliasNotInStore) {
272+ exitCode |= 32;
273+ }
274+ if (exitCode != 0) {
275+ System.exit(exitCode);
276+ }
277+ }
278+ }
279+
280+ /*
281+ * Parse command line arguments.
282+ */
283+ void parseArgs(String args[]) {
284+ /* parse flags */
285+ int n = 0;
286+
287+ if (args.length == 0) fullusage();
288+ for (n=0; n < args.length; n++) {
289+
290+ String flags = args[n];
291+ String modifier = null;
292+ if (flags.charAt(0) == '-') {
293+ int pos = flags.indexOf(':');
294+ if (pos > 0) {
295+ modifier = flags.substring(pos+1);
296+ flags = flags.substring(0, pos);
297+ }
298+ }
299+
300+ if (collator.compare(flags, "-keystore") == 0) {
301+ if (++n == args.length) usageNoArg();
302+ keystore = args[n];
303+ } else if (collator.compare(flags, "-storepass") ==0) {
304+ if (++n == args.length) usageNoArg();
305+ storepass = getPass(modifier, args[n]);
306+ } else if (collator.compare(flags, "-storetype") ==0) {
307+ if (++n == args.length) usageNoArg();
308+ storetype = args[n];
309+ } else if (collator.compare(flags, "-providerName") ==0) {
310+ if (++n == args.length) usageNoArg();
311+ providerName = args[n];
312+ } else if ((collator.compare(flags, "-provider") == 0) ||
313+ (collator.compare(flags, "-providerClass") == 0)) {
314+ if (++n == args.length) usageNoArg();
315+ if (providers == null) {
316+ providers = new Vector<String>(3);
317+ }
318+ providers.add(args[n]);
319+
320+ if (args.length > (n+1)) {
321+ flags = args[n+1];
322+ if (collator.compare(flags, "-providerArg") == 0) {
323+ if (args.length == (n+2)) usageNoArg();
324+ providerArgs.put(args[n], args[n+2]);
325+ n += 2;
326+ }
327+ }
328+ } else if (collator.compare(flags, "-protected") ==0) {
329+ protectedPath = true;
330+ } else if (collator.compare(flags, "-certchain") ==0) {
331+ if (++n == args.length) usageNoArg();
332+ altCertChain = args[n];
333+ } else if (collator.compare(flags, "-debug") ==0) {
334+ debug = true;
335+ } else if (collator.compare(flags, "-keypass") ==0) {
336+ if (++n == args.length) usageNoArg();
337+ keypass = getPass(modifier, args[n]);
338+ } else if (collator.compare(flags, "-sigfile") ==0) {
339+ if (++n == args.length) usageNoArg();
340+ sigfile = args[n];
341+ } else if (collator.compare(flags, "-signedjar") ==0) {
342+ if (++n == args.length) usageNoArg();
343+ signedjar = args[n];
344+ } else if (collator.compare(flags, "-tsa") ==0) {
345+ if (++n == args.length) usageNoArg();
346+ tsaUrl = args[n];
347+ } else if (collator.compare(flags, "-tsacert") ==0) {
348+ if (++n == args.length) usageNoArg();
349+ tsaAlias = args[n];
350+ } else if (collator.compare(flags, "-altsigner") ==0) {
351+ if (++n == args.length) usageNoArg();
352+ altSignerClass = args[n];
353+ } else if (collator.compare(flags, "-altsignerpath") ==0) {
354+ if (++n == args.length) usageNoArg();
355+ altSignerClasspath = args[n];
356+ } else if (collator.compare(flags, "-sectionsonly") ==0) {
357+ signManifest = false;
358+ } else if (collator.compare(flags, "-internalsf") ==0) {
359+ externalSF = false;
360+ } else if (collator.compare(flags, "-verify") ==0) {
361+ verify = true;
362+ } else if (collator.compare(flags, "-verbose") ==0) {
363+ verbose = (modifier != null) ? modifier : "all";
364+ } else if (collator.compare(flags, "-sigalg") ==0) {
365+ if (++n == args.length) usageNoArg();
366+ sigalg = args[n];
367+ } else if (collator.compare(flags, "-digestalg") ==0) {
368+ if (++n == args.length) usageNoArg();
369+ digestalg = args[n];
370+ } else if (collator.compare(flags, "-certs") ==0) {
371+ showcerts = true;
372+ } else if (collator.compare(flags, "-strict") ==0) {
373+ strict = true;
374+ } else if (collator.compare(flags, "-h") == 0 ||
375+ collator.compare(flags, "-help") == 0) {
376+ fullusage();
377+ } else {
378+ if (!flags.startsWith("-")) {
379+ if (jarfile == null) {
380+ jarfile = flags;
381+ } else {
382+ alias = flags;
383+ ckaliases.add(alias);
384+ }
385+ } else {
386+ System.err.println(
387+ rb.getString("Illegal.option.") + flags);
388+ usage();
389+ }
390+ }
391+ }
392+
393+ // -certs must always be specified with -verbose
394+ if (verbose == null) showcerts = false;
395+
396+ if (jarfile == null) {
397+ System.err.println(rb.getString("Please.specify.jarfile.name"));
398+ usage();
399+ }
400+ if (!verify && alias == null) {
401+ System.err.println(rb.getString("Please.specify.alias.name"));
402+ usage();
403+ }
404+ if (!verify && ckaliases.size() > 1) {
405+ System.err.println(rb.getString("Only.one.alias.can.be.specified"));
406+ usage();
407+ }
408+
409+ if (storetype == null) {
410+ storetype = KeyStore.getDefaultType();
411+ }
412+ storetype = KeyStoreUtil.niceStoreTypeName(storetype);
413+
414+ try {
415+ if (signedjar != null && new File(signedjar).getCanonicalPath().equals(
416+ new File(jarfile).getCanonicalPath())) {
417+ signedjar = null;
418+ }
419+ } catch (IOException ioe) {
420+ // File system error?
421+ // Just ignore it.
422+ }
423+
424+ if (P11KEYSTORE.equalsIgnoreCase(storetype) ||
425+ KeyStoreUtil.isWindowsKeyStore(storetype)) {
426+ token = true;
427+ if (keystore == null) {
428+ keystore = NONE;
429+ }
430+ }
431+
432+ if (NONE.equals(keystore)) {
433+ nullStream = true;
434+ }
435+
436+ if (token && !nullStream) {
437+ System.err.println(MessageFormat.format(rb.getString
438+ (".keystore.must.be.NONE.if.storetype.is.{0}"), storetype));
439+ usage();
440+ }
441+
442+ if (token && keypass != null) {
443+ System.err.println(MessageFormat.format(rb.getString
444+ (".keypass.can.not.be.specified.if.storetype.is.{0}"), storetype));
445+ usage();
446+ }
447+
448+ if (protectedPath) {
449+ if (storepass != null || keypass != null) {
450+ System.err.println(rb.getString
451+ ("If.protected.is.specified.then.storepass.and.keypass.must.not.be.specified"));
452+ usage();
453+ }
454+ }
455+ if (KeyStoreUtil.isWindowsKeyStore(storetype)) {
456+ if (storepass != null || keypass != null) {
457+ System.err.println(rb.getString
458+ ("If.keystore.is.not.password.protected.then.storepass.and.keypass.must.not.be.specified"));
459+ usage();
460+ }
461+ }
462+ }
463+
464+ static char[] getPass(String modifier, String arg) {
465+ char[] output = KeyTool.getPassWithModifier(modifier, arg);
466+ if (output != null) return output;
467+ usage();
468+ return null; // Useless, usage() already exit
469+ }
470+
471+ static void usageNoArg() {
472+ System.out.println(rb.getString("Option.lacks.argument"));
473+ usage();
474+ }
475+
476+ static void usage() {
477+ System.out.println();
478+ System.out.println(rb.getString("Please.type.jarsigner.help.for.usage"));
479+ System.exit(1);
480+ }
481+
482+ static void fullusage() {
483+ System.out.println(rb.getString
484+ ("Usage.jarsigner.options.jar.file.alias"));
485+ System.out.println(rb.getString
486+ (".jarsigner.verify.options.jar.file.alias."));
487+ System.out.println();
488+ System.out.println(rb.getString
489+ (".keystore.url.keystore.location"));
490+ System.out.println();
491+ System.out.println(rb.getString
492+ (".storepass.password.password.for.keystore.integrity"));
493+ System.out.println();
494+ System.out.println(rb.getString
495+ (".storetype.type.keystore.type"));
496+ System.out.println();
497+ System.out.println(rb.getString
498+ (".keypass.password.password.for.private.key.if.different."));
499+ System.out.println();
500+ System.out.println(rb.getString
501+ (".certchain.file.name.of.alternative.certchain.file"));
502+ System.out.println();
503+ System.out.println(rb.getString
504+ (".sigfile.file.name.of.SF.DSA.file"));
505+ System.out.println();
506+ System.out.println(rb.getString
507+ (".signedjar.file.name.of.signed.JAR.file"));
508+ System.out.println();
509+ System.out.println(rb.getString
510+ (".digestalg.algorithm.name.of.digest.algorithm"));
511+ System.out.println();
512+ System.out.println(rb.getString
513+ (".sigalg.algorithm.name.of.signature.algorithm"));
514+ System.out.println();
515+ System.out.println(rb.getString
516+ (".verify.verify.a.signed.JAR.file"));
517+ System.out.println();
518+ System.out.println(rb.getString
519+ (".verbose.suboptions.verbose.output.when.signing.verifying."));
520+ System.out.println(rb.getString
521+ (".suboptions.can.be.all.grouped.or.summary"));
522+ System.out.println();
523+ System.out.println(rb.getString
524+ (".certs.display.certificates.when.verbose.and.verifying"));
525+ System.out.println();
526+ System.out.println(rb.getString
527+ (".tsa.url.location.of.the.Timestamping.Authority"));
528+ System.out.println();
529+ System.out.println(rb.getString
530+ (".tsacert.alias.public.key.certificate.for.Timestamping.Authority"));
531+ System.out.println();
532+ System.out.println(rb.getString
533+ (".altsigner.class.class.name.of.an.alternative.signing.mechanism"));
534+ System.out.println();
535+ System.out.println(rb.getString
536+ (".altsignerpath.pathlist.location.of.an.alternative.signing.mechanism"));
537+ System.out.println();
538+ System.out.println(rb.getString
539+ (".internalsf.include.the.SF.file.inside.the.signature.block"));
540+ System.out.println();
541+ System.out.println(rb.getString
542+ (".sectionsonly.don.t.compute.hash.of.entire.manifest"));
543+ System.out.println();
544+ System.out.println(rb.getString
545+ (".protected.keystore.has.protected.authentication.path"));
546+ System.out.println();
547+ System.out.println(rb.getString
548+ (".providerName.name.provider.name"));
549+ System.out.println();
550+ System.out.println(rb.getString
551+ (".providerClass.class.name.of.cryptographic.service.provider.s"));
552+ System.out.println(rb.getString
553+ (".providerArg.arg.master.class.file.and.constructor.argument"));
554+ System.out.println();
555+ System.out.println(rb.getString
556+ (".strict.treat.warnings.as.errors"));
557+ System.out.println();
558+
559+ System.exit(0);
560+ }
561+
562+ void verifyJar(String jarName)
563+ throws Exception
564+ {
565+ boolean anySigned = false; // if there exists entry inside jar signed
566+ JarFile jf = null;
567+
568+ try {
569+ jf = new JarFile(jarName, true);
570+ Vector<JarEntry> entriesVec = new Vector<>();
571+ byte[] buffer = new byte[8192];
572+
573+ Enumeration<JarEntry> entries = jf.entries();
574+ while (entries.hasMoreElements()) {
575+ JarEntry je = entries.nextElement();
576+ entriesVec.addElement(je);
577+ InputStream is = null;
578+ try {
579+ is = jf.getInputStream(je);
580+ int n;
581+ while ((n = is.read(buffer, 0, buffer.length)) != -1) {
582+ // we just read. this will throw a SecurityException
583+ // if a signature/digest check fails.
584+ }
585+ } finally {
586+ if (is != null) {
587+ is.close();
588+ }
589+ }
590+ }
591+
592+ Manifest man = jf.getManifest();
593+
594+ // The map to record display info, only used when -verbose provided
595+ // key: signer info string
596+ // value: the list of files with common key
597+ Map<String,List<String>> output = new LinkedHashMap<>();
598+
599+ if (man != null) {
600+ if (verbose != null) System.out.println();
601+ Enumeration<JarEntry> e = entriesVec.elements();
602+
603+ long now = System.currentTimeMillis();
604+ String tab = rb.getString("6SPACE");
605+
606+ while (e.hasMoreElements()) {
607+ JarEntry je = e.nextElement();
608+ String name = je.getName();
609+ CodeSigner[] signers = je.getCodeSigners();
610+ boolean isSigned = (signers != null);
611+ anySigned |= isSigned;
612+ hasUnsignedEntry |= !je.isDirectory() && !isSigned
613+ && !signatureRelated(name);
614+
615+ int inStoreOrScope = inKeyStore(signers);
616+
617+ boolean inStore = (inStoreOrScope & IN_KEYSTORE) != 0;
618+ boolean inScope = (inStoreOrScope & IN_SCOPE) != 0;
619+
620+ notSignedByAlias |= (inStoreOrScope & NOT_ALIAS) != 0;
621+ if (keystore != null) {
622+ aliasNotInStore |= isSigned && (!inStore && !inScope);
623+ }
624+
625+ // Only used when -verbose provided
626+ StringBuffer sb = null;
627+ if (verbose != null) {
628+ sb = new StringBuffer();
629+ boolean inManifest =
630+ ((man.getAttributes(name) != null) ||
631+ (man.getAttributes("./"+name) != null) ||
632+ (man.getAttributes("/"+name) != null));
633+ sb.append(
634+ (isSigned ? rb.getString("s") : rb.getString("SPACE")) +
635+ (inManifest ? rb.getString("m") : rb.getString("SPACE")) +
636+ (inStore ? rb.getString("k") : rb.getString("SPACE")) +
637+ (inScope ? rb.getString("i") : rb.getString("SPACE")) +
638+ ((inStoreOrScope & NOT_ALIAS) != 0 ?"X":" ") +
639+ rb.getString("SPACE"));
640+ sb.append("|");
641+ }
642+
643+ // When -certs provided, display info has extra empty
644+ // lines at the beginning and end.
645+ if (isSigned) {
646+ if (showcerts) sb.append('\n');
647+ for (CodeSigner signer: signers) {
648+ // signerInfo() must be called even if -verbose
649+ // not provided. The method updates various
650+ // warning flags.
651+ String si = signerInfo(signer, tab, now);
652+ if (showcerts) {
653+ sb.append(si);
654+ sb.append('\n');
655+ }
656+ }
657+ } else if (showcerts && !verbose.equals("all")) {
658+ // Print no info for unsigned entries when -verbose:all,
659+ // to be consistent with old behavior.
660+ if (signatureRelated(name)) {
661+ sb.append("\n" + tab + rb.getString(
662+ ".Signature.related.entries.") + "\n\n");
663+ } else {
664+ sb.append("\n" + tab + rb.getString(
665+ ".Unsigned.entries.") + "\n\n");
666+ }
667+ }
668+
669+ if (verbose != null) {
670+ String label = sb.toString();
671+ if (signatureRelated(name)) {
672+ // Entries inside META-INF and other unsigned
673+ // entries are grouped separately.
674+ label = "-" + label;
675+ }
676+
677+ // The label finally contains 2 parts separated by '|':
678+ // The legend displayed before the entry names, and
679+ // the cert info (if -certs specfied).
680+
681+ if (!output.containsKey(label)) {
682+ output.put(label, new ArrayList<String>());
683+ }
684+
685+ StringBuffer fb = new StringBuffer();
686+ String s = Long.toString(je.getSize());
687+ for (int i = 6 - s.length(); i > 0; --i) {
688+ fb.append(' ');
689+ }
690+ fb.append(s).append(' ').
691+ append(new Date(je.getTime()).toString());
692+ fb.append(' ').append(name);
693+
694+ output.get(label).add(fb.toString());
695+ }
696+ }
697+ }
698+ if (verbose != null) {
699+ for (Entry<String,List<String>> s: output.entrySet()) {
700+ List<String> files = s.getValue();
701+ String key = s.getKey();
702+ if (key.charAt(0) == '-') { // the signature-related group
703+ key = key.substring(1);
704+ }
705+ int pipe = key.indexOf('|');
706+ if (verbose.equals("all")) {
707+ for (String f: files) {
708+ System.out.println(key.substring(0, pipe) + f);
709+ System.out.printf(key.substring(pipe+1));
710+ }
711+ } else {
712+ if (verbose.equals("grouped")) {
713+ for (String f: files) {
714+ System.out.println(key.substring(0, pipe) + f);
715+ }
716+ } else if (verbose.equals("summary")) {
717+ System.out.print(key.substring(0, pipe));
718+ if (files.size() > 1) {
719+ System.out.println(files.get(0) + " " +
720+ String.format(rb.getString(
721+ ".and.d.more."), files.size()-1));
722+ } else {
723+ System.out.println(files.get(0));
724+ }
725+ }
726+ System.out.printf(key.substring(pipe+1));
727+ }
728+ }
729+ System.out.println();
730+ System.out.println(rb.getString(
731+ ".s.signature.was.verified."));
732+ System.out.println(rb.getString(
733+ ".m.entry.is.listed.in.manifest"));
734+ System.out.println(rb.getString(
735+ ".k.at.least.one.certificate.was.found.in.keystore"));
736+ System.out.println(rb.getString(
737+ ".i.at.least.one.certificate.was.found.in.identity.scope"));
738+ if (ckaliases.size() > 0) {
739+ System.out.println(rb.getString(
740+ ".X.not.signed.by.specified.alias.es."));
741+ }
742+ System.out.println();
743+ }
744+ if (man == null)
745+ System.out.println(rb.getString("no.manifest."));
746+
747+ if (!anySigned) {
748+ System.out.println(rb.getString(
749+ "jar.is.unsigned.signatures.missing.or.not.parsable."));
750+ } else {
751+ System.out.println(rb.getString("jar.verified."));
752+ if (hasUnsignedEntry || hasExpiredCert || hasExpiringCert ||
753+ badKeyUsage || badExtendedKeyUsage || badNetscapeCertType ||
754+ notYetValidCert || chainNotValidated ||
755+ aliasNotInStore || notSignedByAlias) {
756+
757+ System.out.println();
758+ System.out.println(rb.getString("Warning."));
759+ if (badKeyUsage) {
760+ System.out.println(
761+ rb.getString("This.jar.contains.entries.whose.signer.certificate.s.KeyUsage.extension.doesn.t.allow.code.signing."));
762+ }
763+
764+ if (badExtendedKeyUsage) {
765+ System.out.println(
766+ rb.getString("This.jar.contains.entries.whose.signer.certificate.s.ExtendedKeyUsage.extension.doesn.t.allow.code.signing."));
767+ }
768+
769+ if (badNetscapeCertType) {
770+ System.out.println(
771+ rb.getString("This.jar.contains.entries.whose.signer.certificate.s.NetscapeCertType.extension.doesn.t.allow.code.signing."));
772+ }
773+
774+ if (hasUnsignedEntry) {
775+ System.out.println(rb.getString(
776+ "This.jar.contains.unsigned.entries.which.have.not.been.integrity.checked."));
777+ }
778+ if (hasExpiredCert) {
779+ System.out.println(rb.getString(
780+ "This.jar.contains.entries.whose.signer.certificate.has.expired."));
781+ }
782+ if (hasExpiringCert) {
783+ System.out.println(rb.getString(
784+ "This.jar.contains.entries.whose.signer.certificate.will.expire.within.six.months."));
785+ }
786+ if (notYetValidCert) {
787+ System.out.println(rb.getString(
788+ "This.jar.contains.entries.whose.signer.certificate.is.not.yet.valid."));
789+ }
790+
791+ if (chainNotValidated) {
792+ System.out.println(
793+ rb.getString("This.jar.contains.entries.whose.certificate.chain.is.not.validated."));
794+ }
795+
796+ if (notSignedByAlias) {
797+ System.out.println(
798+ rb.getString("This.jar.contains.signed.entries.which.is.not.signed.by.the.specified.alias.es."));
799+ }
800+
801+ if (aliasNotInStore) {
802+ System.out.println(rb.getString("This.jar.contains.signed.entries.that.s.not.signed.by.alias.in.this.keystore."));
803+ }
804+ if (! (verbose != null && showcerts)) {
805+ System.out.println();
806+ System.out.println(rb.getString(
807+ "Re.run.with.the.verbose.and.certs.options.for.more.details."));
808+ }
809+ }
810+ }
811+ return;
812+ } catch (Exception e) {
813+ System.out.println(rb.getString("jarsigner.") + e);
814+ if (debug) {
815+ e.printStackTrace();
816+ }
817+ } finally { // close the resource
818+ if (jf != null) {
819+ jf.close();
820+ }
821+ }
822+
823+ System.exit(1);
824+ }
825+
826+ private static MessageFormat validityTimeForm = null;
827+ private static MessageFormat notYetTimeForm = null;
828+ private static MessageFormat expiredTimeForm = null;
829+ private static MessageFormat expiringTimeForm = null;
830+
831+ /*
832+ * Display some details about a certificate:
833+ *
834+ * [<tab>] <cert-type> [", " <subject-DN>] [" (" <keystore-entry-alias> ")"]
835+ * [<validity-period> | <expiry-warning>]
836+ *
837+ * Note: no newline character at the end
838+ */
839+ String printCert(String tab, Certificate c, boolean checkValidityPeriod,
840+ long now, boolean checkUsage) {
841+
842+ StringBuilder certStr = new StringBuilder();
843+ String space = rb.getString("SPACE");
844+ X509Certificate x509Cert = null;
845+
846+ if (c instanceof X509Certificate) {
847+ x509Cert = (X509Certificate) c;
848+ certStr.append(tab).append(x509Cert.getType())
849+ .append(rb.getString("COMMA"))
850+ .append(x509Cert.getSubjectDN().getName());
851+ } else {
852+ certStr.append(tab).append(c.getType());
853+ }
854+
855+ String alias = storeHash.get(c);
856+ if (alias != null) {
857+ certStr.append(space).append(alias);
858+ }
859+
860+ if (checkValidityPeriod && x509Cert != null) {
861+
862+ certStr.append("\n").append(tab).append("[");
863+ Date notAfter = x509Cert.getNotAfter();
864+ try {
865+ x509Cert.checkValidity();
866+ // test if cert will expire within six months
867+ if (now == 0) {
868+ now = System.currentTimeMillis();
869+ }
870+ if (notAfter.getTime() < now + SIX_MONTHS) {
871+ hasExpiringCert = true;
872+
873+ if (expiringTimeForm == null) {
874+ expiringTimeForm = new MessageFormat(
875+ rb.getString("certificate.will.expire.on"));
876+ }
877+ Object[] source = { notAfter };
878+ certStr.append(expiringTimeForm.format(source));
879+
880+ } else {
881+ if (validityTimeForm == null) {
882+ validityTimeForm = new MessageFormat(
883+ rb.getString("certificate.is.valid.from"));
884+ }
885+ Object[] source = { x509Cert.getNotBefore(), notAfter };
886+ certStr.append(validityTimeForm.format(source));
887+ }
888+ } catch (CertificateExpiredException cee) {
889+ hasExpiredCert = true;
890+
891+ if (expiredTimeForm == null) {
892+ expiredTimeForm = new MessageFormat(
893+ rb.getString("certificate.expired.on"));
894+ }
895+ Object[] source = { notAfter };
896+ certStr.append(expiredTimeForm.format(source));
897+
898+ } catch (CertificateNotYetValidException cnyve) {
899+ notYetValidCert = true;
900+
901+ if (notYetTimeForm == null) {
902+ notYetTimeForm = new MessageFormat(
903+ rb.getString("certificate.is.not.valid.until"));
904+ }
905+ Object[] source = { x509Cert.getNotBefore() };
906+ certStr.append(notYetTimeForm.format(source));
907+ }
908+ certStr.append("]");
909+
910+ if (checkUsage) {
911+ boolean[] bad = new boolean[3];
912+ checkCertUsage(x509Cert, bad);
913+ if (bad[0] || bad[1] || bad[2]) {
914+ String x = "";
915+ if (bad[0]) {
916+ x ="KeyUsage";
917+ }
918+ if (bad[1]) {
919+ if (x.length() > 0) x = x + ", ";
920+ x = x + "ExtendedKeyUsage";
921+ }
922+ if (bad[2]) {
923+ if (x.length() > 0) x = x + ", ";
924+ x = x + "NetscapeCertType";
925+ }
926+ certStr.append("\n").append(tab)
927+ .append(MessageFormat.format(rb.getString(
928+ ".{0}.extension.does.not.support.code.signing."), x));
929+ }
930+ }
931+ }
932+ return certStr.toString();
933+ }
934+
935+ private static MessageFormat signTimeForm = null;
936+
937+ private String printTimestamp(String tab, Timestamp timestamp) {
938+
939+ if (signTimeForm == null) {
940+ signTimeForm =
941+ new MessageFormat(rb.getString("entry.was.signed.on"));
942+ }
943+ Object[] source = { timestamp.getTimestamp() };
944+
945+ return new StringBuilder().append(tab).append("[")
946+ .append(signTimeForm.format(source)).append("]").toString();
947+ }
948+
949+ private Map<CodeSigner,Integer> cacheForInKS = new IdentityHashMap<>();
950+
951+ private int inKeyStoreForOneSigner(CodeSigner signer) {
952+ if (cacheForInKS.containsKey(signer)) {
953+ return cacheForInKS.get(signer);
954+ }
955+
956+ boolean found = false;
957+ int result = 0;
958+ List<? extends Certificate> certs = signer.getSignerCertPath().getCertificates();
959+ for (Certificate c : certs) {
960+ String alias = storeHash.get(c);
961+ if (alias != null) {
962+ if (alias.startsWith("(")) {
963+ result |= IN_KEYSTORE;
964+ } else if (alias.startsWith("[")) {
965+ result |= IN_SCOPE;
966+ }
967+ if (ckaliases.contains(alias.substring(1, alias.length() - 1))) {
968+ result |= SIGNED_BY_ALIAS;
969+ }
970+ } else {
971+ if (store != null) {
972+ try {
973+ alias = store.getCertificateAlias(c);
974+ } catch (KeyStoreException kse) {
975+ // never happens, because keystore has been loaded
976+ }
977+ if (alias != null) {
978+ storeHash.put(c, "(" + alias + ")");
979+ found = true;
980+ result |= IN_KEYSTORE;
981+ }
982+ }
983+ if (ckaliases.contains(alias)) {
984+ result |= SIGNED_BY_ALIAS;
985+ }
986+ }
987+ }
988+ cacheForInKS.put(signer, result);
989+ return result;
990+ }
991+
992+ Hashtable<Certificate, String> storeHash = new Hashtable<>();
993+
994+ int inKeyStore(CodeSigner[] signers) {
995+
996+ if (signers == null)
997+ return 0;
998+
999+ int output = 0;
1000+
1001+ for (CodeSigner signer: signers) {
1002+ int result = inKeyStoreForOneSigner(signer);
1003+ output |= result;
1004+ }
1005+ if (ckaliases.size() > 0 && (output & SIGNED_BY_ALIAS) == 0) {
1006+ output |= NOT_ALIAS;
1007+ }
1008+ return output;
1009+ }
1010+
1011+ void signJar(String jarName, String alias, String[] args)
1012+ throws Exception {
1013+ boolean aliasUsed = false;
1014+ X509Certificate tsaCert = null;
1015+
1016+ if (sigfile == null) {
1017+ sigfile = alias;
1018+ aliasUsed = true;
1019+ }
1020+
1021+ if (sigfile.length() > 8) {
1022+ sigfile = sigfile.substring(0, 8).toUpperCase(Locale.ENGLISH);
1023+ } else {
1024+ sigfile = sigfile.toUpperCase(Locale.ENGLISH);
1025+ }
1026+
1027+ StringBuilder tmpSigFile = new StringBuilder(sigfile.length());
1028+ for (int j = 0; j < sigfile.length(); j++) {
1029+ char c = sigfile.charAt(j);
1030+ if (!
1031+ ((c>= 'A' && c<= 'Z') ||
1032+ (c>= '0' && c<= '9') ||
1033+ (c == '-') ||
1034+ (c == '_'))) {
1035+ if (aliasUsed) {
1036+ // convert illegal characters from the alias to be _'s
1037+ c = '_';
1038+ } else {
1039+ throw new
1040+ RuntimeException(rb.getString
1041+ ("signature.filename.must.consist.of.the.following.characters.A.Z.0.9.or."));
1042+ }
1043+ }
1044+ tmpSigFile.append(c);
1045+ }
1046+
1047+ sigfile = tmpSigFile.toString();
1048+
1049+ String tmpJarName;
1050+ if (signedjar == null) tmpJarName = jarName+".sig";
1051+ else tmpJarName = signedjar;
1052+
1053+ File jarFile = new File(jarName);
1054+ File signedJarFile = new File(tmpJarName);
1055+
1056+ // Open the jar (zip) file
1057+ try {
1058+ zipFile = new ZipFile(jarName);
1059+ } catch (IOException ioe) {
1060+ error(rb.getString("unable.to.open.jar.file.")+jarName, ioe);
1061+ }
1062+
1063+ FileOutputStream fos = null;
1064+ try {
1065+ fos = new FileOutputStream(signedJarFile);
1066+ } catch (IOException ioe) {
1067+ error(rb.getString("unable.to.create.")+tmpJarName, ioe);
1068+ }
1069+
1070+ PrintStream ps = new PrintStream(fos);
1071+ ZipOutputStream zos = new ZipOutputStream(ps);
1072+
1073+ /* First guess at what they might be - we don't xclude RSA ones. */
1074+ String sfFilename = (META_INF + sigfile + ".SF").toUpperCase(Locale.ENGLISH);
1075+ String bkFilename = (META_INF + sigfile + ".DSA").toUpperCase(Locale.ENGLISH);
1076+
1077+ Manifest manifest = new Manifest();
1078+ Map<String,Attributes> mfEntries = manifest.getEntries();
1079+
1080+ // The Attributes of manifest before updating
1081+ Attributes oldAttr = null;
1082+
1083+ boolean mfModified = false;
1084+ boolean mfCreated = false;
1085+ byte[] mfRawBytes = null;
1086+
1087+ try {
1088+ MessageDigest digests[] = { MessageDigest.getInstance(digestalg) };
1089+
1090+ // Check if manifest exists
1091+ ZipEntry mfFile;
1092+ if ((mfFile = getManifestFile(zipFile)) != null) {
1093+ // Manifest exists. Read its raw bytes.
1094+ mfRawBytes = getBytes(zipFile, mfFile);
1095+ manifest.read(new ByteArrayInputStream(mfRawBytes));
1096+ oldAttr = (Attributes)(manifest.getMainAttributes().clone());
1097+ } else {
1098+ // Create new manifest
1099+ Attributes mattr = manifest.getMainAttributes();
1100+ mattr.putValue(Attributes.Name.MANIFEST_VERSION.toString(),
1101+ "1.0");
1102+ String javaVendor = System.getProperty("java.vendor");
1103+ String jdkVersion = System.getProperty("java.version");
1104+ mattr.putValue("Created-By", jdkVersion + " (" +javaVendor
1105+ + ")");
1106+ mfFile = new ZipEntry(JarFile.MANIFEST_NAME);
1107+ mfCreated = true;
1108+ }
1109+
1110+ /*
1111+ * For each entry in jar
1112+ * (except for signature-related META-INF entries),
1113+ * do the following:
1114+ *
1115+ * - if entry is not contained in manifest, add it to manifest;
1116+ * - if entry is contained in manifest, calculate its hash and
1117+ * compare it with the one in the manifest; if they are
1118+ * different, replace the hash in the manifest with the newly
1119+ * generated one. (This may invalidate existing signatures!)
1120+ */
1121+ BASE64Encoder encoder = new JarBASE64Encoder();
1122+ Vector<ZipEntry> mfFiles = new Vector<>();
1123+
1124+ boolean wasSigned = false;
1125+
1126+ for (Enumeration<? extends ZipEntry> enum_=zipFile.entries();
1127+ enum_.hasMoreElements();) {
1128+ ZipEntry ze = enum_.nextElement();
1129+
1130+ if (ze.getName().startsWith(META_INF)) {
1131+ // Store META-INF files in vector, so they can be written
1132+ // out first
1133+ mfFiles.addElement(ze);
1134+
1135+ if (SignatureFileVerifier.isBlockOrSF(
1136+ ze.getName().toUpperCase(Locale.ENGLISH))) {
1137+ wasSigned = true;
1138+ }
1139+
1140+ if (signatureRelated(ze.getName())) {
1141+ // ignore signature-related and manifest files
1142+ continue;
1143+ }
1144+ }
1145+
1146+ if (manifest.getAttributes(ze.getName()) != null) {
1147+ // jar entry is contained in manifest, check and
1148+ // possibly update its digest attributes
1149+ if (updateDigests(ze, zipFile, digests, encoder,
1150+ manifest) == true) {
1151+ mfModified = true;
1152+ }
1153+ } else if (!ze.isDirectory()) {
1154+ // Add entry to manifest
1155+ Attributes attrs = getDigestAttributes(ze, zipFile,
1156+ digests,
1157+ encoder);
1158+ mfEntries.put(ze.getName(), attrs);
1159+ mfModified = true;
1160+ }
1161+ }
1162+
1163+ // Recalculate the manifest raw bytes if necessary
1164+ if (mfModified) {
1165+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
1166+ manifest.write(baos);
1167+ if (wasSigned) {
1168+ byte[] newBytes = baos.toByteArray();
1169+ if (mfRawBytes != null
1170+ && oldAttr.equals(manifest.getMainAttributes())) {
1171+
1172+ /*
1173+ * Note:
1174+ *
1175+ * The Attributes object is based on HashMap and can handle
1176+ * continuation columns. Therefore, even if the contents are
1177+ * not changed (in a Map view), the bytes that it write()
1178+ * may be different from the original bytes that it read()
1179+ * from. Since the signature on the main attributes is based
1180+ * on raw bytes, we must retain the exact bytes.
1181+ */
1182+
1183+ int newPos = findHeaderEnd(newBytes);
1184+ int oldPos = findHeaderEnd(mfRawBytes);
1185+
1186+ if (newPos == oldPos) {
1187+ System.arraycopy(mfRawBytes, 0, newBytes, 0, oldPos);
1188+ } else {
1189+ // cat oldHead newTail > newBytes
1190+ byte[] lastBytes = new byte[oldPos +
1191+ newBytes.length - newPos];
1192+ System.arraycopy(mfRawBytes, 0, lastBytes, 0, oldPos);
1193+ System.arraycopy(newBytes, newPos, lastBytes, oldPos,
1194+ newBytes.length - newPos);
1195+ newBytes = lastBytes;
1196+ }
1197+ }
1198+ mfRawBytes = newBytes;
1199+ } else {
1200+ mfRawBytes = baos.toByteArray();
1201+ }
1202+ }
1203+
1204+ // Write out the manifest
1205+ if (mfModified) {
1206+ // manifest file has new length
1207+ mfFile = new ZipEntry(JarFile.MANIFEST_NAME);
1208+ }
1209+ if (verbose != null) {
1210+ if (mfCreated) {
1211+ System.out.println(rb.getString(".adding.") +
1212+ mfFile.getName());
1213+ } else if (mfModified) {
1214+ System.out.println(rb.getString(".updating.") +
1215+ mfFile.getName());
1216+ }
1217+ }
1218+ zos.putNextEntry(mfFile);
1219+ zos.write(mfRawBytes);
1220+
1221+ // Calculate SignatureFile (".SF") and SignatureBlockFile
1222+ ManifestDigester manDig = new ManifestDigester(mfRawBytes);
1223+ SignatureFile sf = new SignatureFile(digests, manifest, manDig,
1224+ sigfile, signManifest);
1225+
1226+ if (tsaAlias != null) {
1227+ tsaCert = getTsaCert(tsaAlias);
1228+ }
1229+
1230+ SignatureFile.Block block = null;
1231+
1232+ try {
1233+ block =
1234+ sf.generateBlock(privateKey, sigalg, certChain,
1235+ externalSF, tsaUrl, tsaCert, signingMechanism, args,
1236+ zipFile);
1237+ } catch (SocketTimeoutException e) {
1238+ // Provide a helpful message when TSA is beyond a firewall
1239+ error(rb.getString("unable.to.sign.jar.") +
1240+ rb.getString("no.response.from.the.Timestamping.Authority.") +
1241+ "\n -J-Dhttp.proxyHost=<hostname>" +
1242+ "\n -J-Dhttp.proxyPort=<portnumber>\n" +
1243+ rb.getString("or") +
1244+ "\n -J-Dhttps.proxyHost=<hostname> " +
1245+ "\n -J-Dhttps.proxyPort=<portnumber> ", e);
1246+ }
1247+
1248+ sfFilename = sf.getMetaName();
1249+ bkFilename = block.getMetaName();
1250+
1251+ ZipEntry sfFile = new ZipEntry(sfFilename);
1252+ ZipEntry bkFile = new ZipEntry(bkFilename);
1253+
1254+ long time = System.currentTimeMillis();
1255+ sfFile.setTime(time);
1256+ bkFile.setTime(time);
1257+
1258+ // signature file
1259+ zos.putNextEntry(sfFile);
1260+ sf.write(zos);
1261+ if (verbose != null) {
1262+ if (zipFile.getEntry(sfFilename) != null) {
1263+ System.out.println(rb.getString(".updating.") +
1264+ sfFilename);
1265+ } else {
1266+ System.out.println(rb.getString(".adding.") +
1267+ sfFilename);
1268+ }
1269+ }
1270+
1271+ if (verbose != null) {
1272+ if (tsaUrl != null || tsaCert != null) {
1273+ System.out.println(
1274+ rb.getString("requesting.a.signature.timestamp"));
1275+ }
1276+ if (tsaUrl != null) {
1277+ System.out.println(rb.getString("TSA.location.") + tsaUrl);
1278+ }
1279+ if (tsaCert != null) {
1280+ String certUrl =
1281+ TimestampedSigner.getTimestampingUrl(tsaCert);
1282+ if (certUrl != null) {
1283+ System.out.println(rb.getString("TSA.location.") +
1284+ certUrl);
1285+ }
1286+ System.out.println(rb.getString("TSA.certificate.") +
1287+ printCert("", tsaCert, false, 0, false));
1288+ }
1289+ if (signingMechanism != null) {
1290+ System.out.println(
1291+ rb.getString("using.an.alternative.signing.mechanism"));
1292+ }
1293+ }
1294+
1295+ // signature block file
1296+ zos.putNextEntry(bkFile);
1297+ block.write(zos);
1298+ if (verbose != null) {
1299+ if (zipFile.getEntry(bkFilename) != null) {
1300+ System.out.println(rb.getString(".updating.") +
1301+ bkFilename);
1302+ } else {
1303+ System.out.println(rb.getString(".adding.") +
1304+ bkFilename);
1305+ }
1306+ }
1307+
1308+ // Write out all other META-INF files that we stored in the
1309+ // vector
1310+ for (int i=0; i<mfFiles.size(); i++) {
1311+ ZipEntry ze = mfFiles.elementAt(i);
1312+ if (!ze.getName().equalsIgnoreCase(JarFile.MANIFEST_NAME)
1313+ && !ze.getName().equalsIgnoreCase(sfFilename)
1314+ && !ze.getName().equalsIgnoreCase(bkFilename)) {
1315+ writeEntry(zipFile, zos, ze);
1316+ }
1317+ }
1318+
1319+ // Write out all other files
1320+ for (Enumeration<? extends ZipEntry> enum_=zipFile.entries();
1321+ enum_.hasMoreElements();) {
1322+ ZipEntry ze = enum_.nextElement();
1323+
1324+ if (!ze.getName().startsWith(META_INF)) {
1325+ if (verbose != null) {
1326+ if (manifest.getAttributes(ze.getName()) != null)
1327+ System.out.println(rb.getString(".signing.") +
1328+ ze.getName());
1329+ else
1330+ System.out.println(rb.getString(".adding.") +
1331+ ze.getName());
1332+ }
1333+ writeEntry(zipFile, zos, ze);
1334+ }
1335+ }
1336+ } catch(IOException ioe) {
1337+ error(rb.getString("unable.to.sign.jar.")+ioe, ioe);
1338+ } finally {
1339+ // close the resouces
1340+ if (zipFile != null) {
1341+ zipFile.close();
1342+ zipFile = null;
1343+ }
1344+
1345+ if (zos != null) {
1346+ zos.close();
1347+ }
1348+ }
1349+
1350+ // no IOException thrown in the follow try clause, so disable
1351+ // the try clause.
1352+ // try {
1353+ if (signedjar == null) {
1354+ // attempt an atomic rename. If that fails,
1355+ // rename the original jar file, then the signed
1356+ // one, then delete the original.
1357+ if (!signedJarFile.renameTo(jarFile)) {
1358+ File origJar = new File(jarName+".orig");
1359+
1360+ if (jarFile.renameTo(origJar)) {
1361+ if (signedJarFile.renameTo(jarFile)) {
1362+ origJar.delete();
1363+ } else {
1364+ MessageFormat form = new MessageFormat(rb.getString
1365+ ("attempt.to.rename.signedJarFile.to.jarFile.failed"));
1366+ Object[] source = {signedJarFile, jarFile};
1367+ error(form.format(source));
1368+ }
1369+ } else {
1370+ MessageFormat form = new MessageFormat(rb.getString
1371+ ("attempt.to.rename.jarFile.to.origJar.failed"));
1372+ Object[] source = {jarFile, origJar};
1373+ error(form.format(source));
1374+ }
1375+ }
1376+ }
1377+
1378+ if (hasExpiredCert || hasExpiringCert || notYetValidCert
1379+ || badKeyUsage || badExtendedKeyUsage
1380+ || badNetscapeCertType || chainNotValidated) {
1381+ System.out.println();
1382+
1383+ System.out.println(rb.getString("Warning."));
1384+ if (badKeyUsage) {
1385+ System.out.println(
1386+ rb.getString("The.signer.certificate.s.KeyUsage.extension.doesn.t.allow.code.signing."));
1387+ }
1388+
1389+ if (badExtendedKeyUsage) {
1390+ System.out.println(
1391+ rb.getString("The.signer.certificate.s.ExtendedKeyUsage.extension.doesn.t.allow.code.signing."));
1392+ }
1393+
1394+ if (badNetscapeCertType) {
1395+ System.out.println(
1396+ rb.getString("The.signer.certificate.s.NetscapeCertType.extension.doesn.t.allow.code.signing."));
1397+ }
1398+
1399+ if (hasExpiredCert) {
1400+ System.out.println(
1401+ rb.getString("The.signer.certificate.has.expired."));
1402+ } else if (hasExpiringCert) {
1403+ System.out.println(
1404+ rb.getString("The.signer.certificate.will.expire.within.six.months."));
1405+ } else if (notYetValidCert) {
1406+ System.out.println(
1407+ rb.getString("The.signer.certificate.is.not.yet.valid."));
1408+ }
1409+
1410+ if (chainNotValidated) {
1411+ System.out.println(
1412+ rb.getString("The.signer.s.certificate.chain.is.not.validated."));
1413+ }
1414+ }
1415+
1416+ // no IOException thrown in the above try clause, so disable
1417+ // the catch clause.
1418+ // } catch(IOException ioe) {
1419+ // error(rb.getString("unable.to.sign.jar.")+ioe, ioe);
1420+ // }
1421+ }
1422+
1423+ /**
1424+ * Find the length of header inside bs. The header is a multiple (>=0)
1425+ * lines of attributes plus an empty line. The empty line is included
1426+ * in the header.
1427+ */
1428+ private int findHeaderEnd(byte[] bs) {
1429+ // Initial state true to deal with empty header
1430+ boolean newline = true; // just met a newline
1431+ int len = bs.length;
1432+ for (int i=0; i<len; i++) {
1433+ switch (bs[i]) {
1434+ case '\r':
1435+ if (i < len - 1 && bs[i+1] == '\n') i++;
1436+ // fallthrough
1437+ case '\n':
1438+ if (newline) return i+1; //+1 to get length
1439+ newline = true;
1440+ break;
1441+ default:
1442+ newline = false;
1443+ }
1444+ }
1445+ // If header end is not found, it means the MANIFEST.MF has only
1446+ // the main attributes section and it does not end with 2 newlines.
1447+ // Returns the whole length so that it can be completely replaced.
1448+ return len;
1449+ }
1450+
1451+ /**
1452+ * signature-related files include:
1453+ * . META-INF/MANIFEST.MF
1454+ * . META-INF/SIG-*
1455+ * . META-INF/*.SF
1456+ * . META-INF/*.DSA
1457+ * . META-INF/*.RSA
1458+ * . META-INF/*.EC
1459+ */
1460+ private boolean signatureRelated(String name) {
1461+ String ucName = name.toUpperCase(Locale.ENGLISH);
1462+ if (ucName.equals(JarFile.MANIFEST_NAME) ||
1463+ ucName.equals(META_INF) ||
1464+ (ucName.startsWith(SIG_PREFIX) &&
1465+ ucName.indexOf("/") == ucName.lastIndexOf("/"))) {
1466+ return true;
1467+ }
1468+
1469+ if (ucName.startsWith(META_INF) &&
1470+ SignatureFileVerifier.isBlockOrSF(ucName)) {
1471+ // .SF/.DSA/.RSA/.EC files in META-INF subdirs
1472+ // are not considered signature-related
1473+ return (ucName.indexOf("/") == ucName.lastIndexOf("/"));
1474+ }
1475+
1476+ return false;
1477+ }
1478+
1479+ Map<CodeSigner,String> cacheForSignerInfo = new IdentityHashMap<>();
1480+
1481+ /**
1482+ * Returns a string of singer info, with a newline at the end
1483+ */
1484+ private String signerInfo(CodeSigner signer, String tab, long now) {
1485+ if (cacheForSignerInfo.containsKey(signer)) {
1486+ return cacheForSignerInfo.get(signer);
1487+ }
1488+ StringBuffer s = new StringBuffer();
1489+ List<? extends Certificate> certs = signer.getSignerCertPath().getCertificates();
1490+ // display the signature timestamp, if present
1491+ Timestamp timestamp = signer.getTimestamp();
1492+ if (timestamp != null) {
1493+ s.append(printTimestamp(tab, timestamp));
1494+ s.append('\n');
1495+ }
1496+ // display the certificate(s). The first one is end-enity cert and
1497+ // its KeyUsage should be checked.
1498+ boolean first = true;
1499+ for (Certificate c : certs) {
1500+ s.append(printCert(tab, c, true, now, first));
1501+ s.append('\n');
1502+ first = false;
1503+ }
1504+ try {
1505+ CertPath cp = certificateFactory.generateCertPath(certs);
1506+ validator.validate(cp, pkixParameters);
1507+ } catch (Exception e) {
1508+ chainNotValidated = true;
1509+ s.append(tab + rb.getString(".CertPath.not.validated.") +
1510+ e.getLocalizedMessage() + "]\n"); // TODO
1511+ }
1512+ String result = s.toString();
1513+ cacheForSignerInfo.put(signer, result);
1514+ return result;
1515+ }
1516+
1517+ private void writeEntry(ZipFile zf, ZipOutputStream os, ZipEntry ze)
1518+ throws IOException
1519+ {
1520+ ZipEntry ze2 = new ZipEntry(ze.getName());
1521+ ze2.setMethod(ze.getMethod());
1522+ ze2.setTime(ze.getTime());
1523+ ze2.setComment(ze.getComment());
1524+ ze2.setExtra(ze.getExtra());
1525+ if (ze.getMethod() == ZipEntry.STORED) {
1526+ ze2.setSize(ze.getSize());
1527+ ze2.setCrc(ze.getCrc());
1528+ }
1529+ os.putNextEntry(ze2);
1530+ writeBytes(zf, ze, os);
1531+ }
1532+
1533+ /**
1534+ * Writes all the bytes for a given entry to the specified output stream.
1535+ */
1536+ private synchronized void writeBytes
1537+ (ZipFile zf, ZipEntry ze, ZipOutputStream os) throws IOException {
1538+ int n;
1539+
1540+ InputStream is = null;
1541+ try {
1542+ is = zf.getInputStream(ze);
1543+ long left = ze.getSize();
1544+
1545+ while((left > 0) && (n = is.read(buffer, 0, buffer.length)) != -1) {
1546+ os.write(buffer, 0, n);
1547+ left -= n;
1548+ }
1549+ } finally {
1550+ if (is != null) {
1551+ is.close();
1552+ }
1553+ }
1554+ }
1555+
1556+ void loadKeyStore(String keyStoreName, boolean prompt) {
1557+
1558+ if (!nullStream && keyStoreName == null) {
1559+ keyStoreName = System.getProperty("user.home") + File.separator
1560+ + ".keystore";
1561+ }
1562+
1563+ try {
1564+ if (providerName == null) {
1565+ store = KeyStore.getInstance(storetype);
1566+ } else {
1567+ store = KeyStore.getInstance(storetype, providerName);
1568+ }
1569+
1570+ // Get pass phrase
1571+ // XXX need to disable echo; on UNIX, call getpass(char *prompt)Z
1572+ // and on NT call ??
1573+ if (token && storepass == null && !protectedPath
1574+ && !KeyStoreUtil.isWindowsKeyStore(storetype)) {
1575+ storepass = getPass
1576+ (rb.getString("Enter.Passphrase.for.keystore."));
1577+ } else if (!token && storepass == null && prompt) {
1578+ storepass = getPass
1579+ (rb.getString("Enter.Passphrase.for.keystore."));
1580+ }
1581+
1582+ if (nullStream) {
1583+ store.load(null, storepass);
1584+ } else {
1585+ keyStoreName = keyStoreName.replace(File.separatorChar, '/');
1586+ URL url = null;
1587+ try {
1588+ url = new URL(keyStoreName);
1589+ } catch (java.net.MalformedURLException e) {
1590+ // try as file
1591+ url = new File(keyStoreName).toURI().toURL();
1592+ }
1593+ InputStream is = null;
1594+ try {
1595+ is = url.openStream();
1596+ store.load(is, storepass);
1597+ } finally {
1598+ if (is != null) {
1599+ is.close();
1600+ }
1601+ }
1602+ }
1603+ Set<TrustAnchor> tas = new HashSet<>();
1604+ try {
1605+ KeyStore caks = KeyTool.getCacertsKeyStore();
1606+ if (caks != null) {
1607+ Enumeration<String> aliases = caks.aliases();
1608+ while (aliases.hasMoreElements()) {
1609+ String a = aliases.nextElement();
1610+ try {
1611+ tas.add(new TrustAnchor((X509Certificate)caks.getCertificate(a), null));
1612+ } catch (Exception e2) {
1613+ // ignore, when a SecretkeyEntry does not include a cert
1614+ }
1615+ }
1616+ }
1617+ } catch (Exception e) {
1618+ // Ignore, if cacerts cannot be loaded
1619+ }
1620+ if (store != null) {
1621+ Enumeration<String> aliases = store.aliases();
1622+ while (aliases.hasMoreElements()) {
1623+ String a = aliases.nextElement();
1624+ try {
1625+ X509Certificate c = (X509Certificate)store.getCertificate(a);
1626+ // Only add TrustedCertificateEntry and self-signed
1627+ // PrivateKeyEntry
1628+ if (store.isCertificateEntry(a) ||
1629+ c.getSubjectDN().equals(c.getIssuerDN())) {
1630+ tas.add(new TrustAnchor(c, null));
1631+ }
1632+ } catch (Exception e2) {
1633+ // ignore, when a SecretkeyEntry does not include a cert
1634+ }
1635+ }
1636+ }
1637+ certificateFactory = CertificateFactory.getInstance("X.509");
1638+ validator = CertPathValidator.getInstance("PKIX");
1639+ try {
1640+ pkixParameters = new PKIXParameters(tas);
1641+ pkixParameters.setRevocationEnabled(false);
1642+ } catch (InvalidAlgorithmParameterException ex) {
1643+ // Only if tas is empty
1644+ }
1645+ } catch (IOException ioe) {
1646+ throw new RuntimeException(rb.getString("keystore.load.") +
1647+ ioe.getMessage());
1648+ } catch (java.security.cert.CertificateException ce) {
1649+ throw new RuntimeException(rb.getString("certificate.exception.") +
1650+ ce.getMessage());
1651+ } catch (NoSuchProviderException pe) {
1652+ throw new RuntimeException(rb.getString("keystore.load.") +
1653+ pe.getMessage());
1654+ } catch (NoSuchAlgorithmException nsae) {
1655+ throw new RuntimeException(rb.getString("keystore.load.") +
1656+ nsae.getMessage());
1657+ } catch (KeyStoreException kse) {
1658+ throw new RuntimeException
1659+ (rb.getString("unable.to.instantiate.keystore.class.") +
1660+ kse.getMessage());
1661+ }
1662+ }
1663+
1664+ X509Certificate getTsaCert(String alias) {
1665+
1666+ java.security.cert.Certificate cs = null;
1667+
1668+ try {
1669+ cs = store.getCertificate(alias);
1670+ } catch (KeyStoreException kse) {
1671+ // this never happens, because keystore has been loaded
1672+ }
1673+ if (cs == null || (!(cs instanceof X509Certificate))) {
1674+ MessageFormat form = new MessageFormat(rb.getString
1675+ ("Certificate.not.found.for.alias.alias.must.reference.a.valid.KeyStore.entry.containing.an.X.509.public.key.certificate.for.the"));
1676+ Object[] source = {alias, alias};
1677+ error(form.format(source));
1678+ }
1679+ return (X509Certificate) cs;
1680+ }
1681+
1682+ /**
1683+ * Check if userCert is designed to be a code signer
1684+ * @param userCert the certificate to be examined
1685+ * @param bad 3 booleans to show if the KeyUsage, ExtendedKeyUsage,
1686+ * NetscapeCertType has codeSigning flag turned on.
1687+ * If null, the class field badKeyUsage, badExtendedKeyUsage,
1688+ * badNetscapeCertType will be set.
1689+ */
1690+ void checkCertUsage(X509Certificate userCert, boolean[] bad) {
1691+
1692+ // Can act as a signer?
1693+ // 1. if KeyUsage, then [0:digitalSignature] or
1694+ // [1:nonRepudiation] should be true
1695+ // 2. if ExtendedKeyUsage, then should contains ANY or CODE_SIGNING
1696+ // 3. if NetscapeCertType, then should contains OBJECT_SIGNING
1697+ // 1,2,3 must be true
1698+
1699+ if (bad != null) {
1700+ bad[0] = bad[1] = bad[2] = false;
1701+ }
1702+
1703+ boolean[] keyUsage = userCert.getKeyUsage();
1704+ if (keyUsage != null) {
1705+ keyUsage = Arrays.copyOf(keyUsage, 9);
1706+ if (!keyUsage[0] && !keyUsage[1]) {
1707+ if (bad != null) {
1708+ bad[0] = true;
1709+ badKeyUsage = true;
1710+ }
1711+ }
1712+ }
1713+
1714+ try {
1715+ List<String> xKeyUsage = userCert.getExtendedKeyUsage();
1716+ if (xKeyUsage != null) {
1717+ if (!xKeyUsage.contains("2.5.29.37.0") // anyExtendedKeyUsage
1718+ && !xKeyUsage.contains("1.3.6.1.5.5.7.3.3")) { // codeSigning
1719+ if (bad != null) {
1720+ bad[1] = true;
1721+ badExtendedKeyUsage = true;
1722+ }
1723+ }
1724+ }
1725+ } catch (java.security.cert.CertificateParsingException e) {
1726+ // shouldn't happen
1727+ }
1728+
1729+ try {
1730+ // OID_NETSCAPE_CERT_TYPE
1731+ byte[] netscapeEx = userCert.getExtensionValue
1732+ ("2.16.840.1.113730.1.1");
1733+ if (netscapeEx != null) {
1734+ DerInputStream in = new DerInputStream(netscapeEx);
1735+ byte[] encoded = in.getOctetString();
1736+ encoded = new DerValue(encoded).getUnalignedBitString()
1737+ .toByteArray();
1738+
1739+ NetscapeCertTypeExtension extn =
1740+ new NetscapeCertTypeExtension(encoded);
1741+
1742+ Boolean val = (Boolean)extn.get(
1743+ NetscapeCertTypeExtension.OBJECT_SIGNING);
1744+ if (!val) {
1745+ if (bad != null) {
1746+ bad[2] = true;
1747+ badNetscapeCertType = true;
1748+ }
1749+ }
1750+ }
1751+ } catch (IOException e) {
1752+ //
1753+ }
1754+ }
1755+
1756+ void getAliasInfo(String alias) {
1757+
1758+ Key key = null;
1759+
1760+ try {
1761+ java.security.cert.Certificate[] cs = null;
1762+ if (altCertChain != null) {
1763+ try {
1764+ cs = CertificateFactory.getInstance("X.509").
1765+ generateCertificates(new FileInputStream(altCertChain)).
1766+ toArray(new Certificate[0]);
1767+ } catch (CertificateException ex) {
1768+ error(rb.getString("Cannot.restore.certchain.from.file.specified"));
1769+ } catch (FileNotFoundException ex) {
1770+ error(rb.getString("File.specified.by.certchain.does.not.exist"));
1771+ }
1772+ } else {
1773+ try {
1774+ cs = store.getCertificateChain(alias);
1775+ } catch (KeyStoreException kse) {
1776+ // this never happens, because keystore has been loaded
1777+ }
1778+ }
1779+ if (cs == null || cs.length == 0) {
1780+ if (altCertChain != null) {
1781+ error(rb.getString
1782+ ("Certificate.chain.not.found.in.the.file.specified."));
1783+ } else {
1784+ MessageFormat form = new MessageFormat(rb.getString
1785+ ("Certificate.chain.not.found.for.alias.alias.must.reference.a.valid.KeyStore.key.entry.containing.a.private.key.and"));
1786+ Object[] source = {alias, alias};
1787+ error(form.format(source));
1788+ }
1789+ }
1790+
1791+ certChain = new X509Certificate[cs.length];
1792+ for (int i=0; i<cs.length; i++) {
1793+ if (!(cs[i] instanceof X509Certificate)) {
1794+ error(rb.getString
1795+ ("found.non.X.509.certificate.in.signer.s.chain"));
1796+ }
1797+ certChain[i] = (X509Certificate)cs[i];
1798+ }
1799+
1800+ // We don't meant to print anything, the next call
1801+ // checks validity and keyUsage etc
1802+ printCert("", certChain[0], true, 0, true);
1803+
1804+ try {
1805+ CertPath cp = certificateFactory.generateCertPath(Arrays.asList(certChain));
1806+ validator.validate(cp, pkixParameters);
1807+ } catch (Exception e) {
1808+ chainNotValidated = true;
1809+ }
1810+
1811+ try {
1812+ if (!token && keypass == null)
1813+ key = store.getKey(alias, storepass);
1814+ else
1815+ key = store.getKey(alias, keypass);
1816+ } catch (UnrecoverableKeyException e) {
1817+ if (token) {
1818+ throw e;
1819+ } else if (keypass == null) {
1820+ // Did not work out, so prompt user for key password
1821+ MessageFormat form = new MessageFormat(rb.getString
1822+ ("Enter.key.password.for.alias."));
1823+ Object[] source = {alias};
1824+ keypass = getPass(form.format(source));
1825+ key = store.getKey(alias, keypass);
1826+ }
1827+ }
1828+ } catch (NoSuchAlgorithmException e) {
1829+ error(e.getMessage());
1830+ } catch (UnrecoverableKeyException e) {
1831+ error(rb.getString("unable.to.recover.key.from.keystore"));
1832+ } catch (KeyStoreException kse) {
1833+ // this never happens, because keystore has been loaded
1834+ }
1835+
1836+ if (!(key instanceof PrivateKey)) {
1837+ MessageFormat form = new MessageFormat(rb.getString
1838+ ("key.associated.with.alias.not.a.private.key"));
1839+ Object[] source = {alias};
1840+ error(form.format(source));
1841+ } else {
1842+ privateKey = (PrivateKey)key;
1843+ }
1844+ }
1845+
1846+ void error(String message)
1847+ {
1848+ System.out.println(rb.getString("jarsigner.")+message);
1849+ System.exit(1);
1850+ }
1851+
1852+
1853+ void error(String message, Exception e)
1854+ {
1855+ System.out.println(rb.getString("jarsigner.")+message);
1856+ if (debug) {
1857+ e.printStackTrace();
1858+ }
1859+ System.exit(1);
1860+ }
1861+
1862+ char[] getPass(String prompt)
1863+ {
1864+ System.err.print(prompt);
1865+ System.err.flush();
1866+ try {
1867+ char[] pass = Password.readPassword(System.in);
1868+
1869+ if (pass == null) {
1870+ error(rb.getString("you.must.enter.key.password"));
1871+ } else {
1872+ return pass;
1873+ }
1874+ } catch (IOException ioe) {
1875+ error(rb.getString("unable.to.read.password.")+ioe.getMessage());
1876+ }
1877+ // this shouldn't happen
1878+ return null;
1879+ }
1880+
1881+ /*
1882+ * Reads all the bytes for a given zip entry.
1883+ */
1884+ private synchronized byte[] getBytes(ZipFile zf,
1885+ ZipEntry ze) throws IOException {
1886+ int n;
1887+
1888+ InputStream is = null;
1889+ try {
1890+ is = zf.getInputStream(ze);
1891+ baos.reset();
1892+ long left = ze.getSize();
1893+
1894+ while((left > 0) && (n = is.read(buffer, 0, buffer.length)) != -1) {
1895+ baos.write(buffer, 0, n);
1896+ left -= n;
1897+ }
1898+ } finally {
1899+ if (is != null) {
1900+ is.close();
1901+ }
1902+ }
1903+
1904+ return baos.toByteArray();
1905+ }
1906+
1907+ /*
1908+ * Returns manifest entry from given jar file, or null if given jar file
1909+ * does not have a manifest entry.
1910+ */
1911+ private ZipEntry getManifestFile(ZipFile zf) {
1912+ ZipEntry ze = zf.getEntry(JarFile.MANIFEST_NAME);
1913+ if (ze == null) {
1914+ // Check all entries for matching name
1915+ Enumeration<? extends ZipEntry> enum_ = zf.entries();
1916+ while (enum_.hasMoreElements() && ze == null) {
1917+ ze = enum_.nextElement();
1918+ if (!JarFile.MANIFEST_NAME.equalsIgnoreCase
1919+ (ze.getName())) {
1920+ ze = null;
1921+ }
1922+ }
1923+ }
1924+ return ze;
1925+ }
1926+
1927+ /*
1928+ * Computes the digests of a zip entry, and returns them as an array
1929+ * of base64-encoded strings.
1930+ */
1931+ private synchronized String[] getDigests(ZipEntry ze, ZipFile zf,
1932+ MessageDigest[] digests,
1933+ BASE64Encoder encoder)
1934+ throws IOException {
1935+
1936+ int n, i;
1937+ InputStream is = null;
1938+ try {
1939+ is = zf.getInputStream(ze);
1940+ long left = ze.getSize();
1941+ while((left > 0)
1942+ && (n = is.read(buffer, 0, buffer.length)) != -1) {
1943+ for (i=0; i<digests.length; i++) {
1944+ digests[i].update(buffer, 0, n);
1945+ }
1946+ left -= n;
1947+ }
1948+ } finally {
1949+ if (is != null) {
1950+ is.close();
1951+ }
1952+ }
1953+
1954+ // complete the digests
1955+ String[] base64Digests = new String[digests.length];
1956+ for (i=0; i<digests.length; i++) {
1957+ base64Digests[i] = encoder.encode(digests[i].digest());
1958+ }
1959+ return base64Digests;
1960+ }
1961+
1962+ /*
1963+ * Computes the digests of a zip entry, and returns them as a list of
1964+ * attributes
1965+ */
1966+ private Attributes getDigestAttributes(ZipEntry ze, ZipFile zf,
1967+ MessageDigest[] digests,
1968+ BASE64Encoder encoder)
1969+ throws IOException {
1970+
1971+ String[] base64Digests = getDigests(ze, zf, digests, encoder);
1972+ Attributes attrs = new Attributes();
1973+
1974+ for (int i=0; i<digests.length; i++) {
1975+ attrs.putValue(digests[i].getAlgorithm()+"-Digest",
1976+ base64Digests[i]);
1977+ }
1978+ return attrs;
1979+ }
1980+
1981+ /*
1982+ * Updates the digest attributes of a manifest entry, by adding or
1983+ * replacing digest values.
1984+ * A digest value is added if the manifest entry does not contain a digest
1985+ * for that particular algorithm.
1986+ * A digest value is replaced if it is obsolete.
1987+ *
1988+ * Returns true if the manifest entry has been changed, and false
1989+ * otherwise.
1990+ */
1991+ private boolean updateDigests(ZipEntry ze, ZipFile zf,
1992+ MessageDigest[] digests,
1993+ BASE64Encoder encoder,
1994+ Manifest mf) throws IOException {
1995+ boolean update = false;
1996+
1997+ Attributes attrs = mf.getAttributes(ze.getName());
1998+ String[] base64Digests = getDigests(ze, zf, digests, encoder);
1999+
2000+ for (int i=0; i<digests.length; i++) {
2001+ // The entry name to be written into attrs
2002+ String name = null;
2003+ try {
2004+ // Find if the digest already exists
2005+ AlgorithmId aid = AlgorithmId.get(digests[i].getAlgorithm());
2006+ for (Object key: attrs.keySet()) {
2007+ if (key instanceof Attributes.Name) {
2008+ String n = ((Attributes.Name)key).toString();
2009+ if (n.toUpperCase(Locale.ENGLISH).endsWith("-DIGEST")) {
2010+ String tmp = n.substring(0, n.length() - 7);
2011+ if (AlgorithmId.get(tmp).equals(aid)) {
2012+ name = n;
2013+ break;
2014+ }
2015+ }
2016+ }
2017+ }
2018+ } catch (NoSuchAlgorithmException nsae) {
2019+ // Ignored. Writing new digest entry.
2020+ }
2021+
2022+ if (name == null) {
2023+ name = digests[i].getAlgorithm()+"-Digest";
2024+ attrs.putValue(name, base64Digests[i]);
2025+ update=true;
2026+ } else {
2027+ // compare digests, and replace the one in the manifest
2028+ // if they are different
2029+ String mfDigest = attrs.getValue(name);
2030+ if (!mfDigest.equalsIgnoreCase(base64Digests[i])) {
2031+ attrs.putValue(name, base64Digests[i]);
2032+ update=true;
2033+ }
2034+ }
2035+ }
2036+ return update;
2037+ }
2038+
2039+ /*
2040+ * Try to load the specified signing mechanism.
2041+ * The URL class loader is used.
2042+ */
2043+ private ContentSigner loadSigningMechanism(String signerClassName,
2044+ String signerClassPath) throws Exception {
2045+
2046+ // construct class loader
2047+ String cpString = null; // make sure env.class.path defaults to dot
2048+
2049+ // do prepends to get correct ordering
2050+ cpString = PathList.appendPath(System.getProperty("env.class.path"), cpString);
2051+ cpString = PathList.appendPath(System.getProperty("java.class.path"), cpString);
2052+ cpString = PathList.appendPath(signerClassPath, cpString);
2053+ URL[] urls = PathList.pathToURLs(cpString);
2054+ ClassLoader appClassLoader = new URLClassLoader(urls);
2055+
2056+ // attempt to find signer
2057+ Class signerClass = appClassLoader.loadClass(signerClassName);
2058+
2059+ // Check that it implements ContentSigner
2060+ Object signer = signerClass.newInstance();
2061+ if (!(signer instanceof ContentSigner)) {
2062+ MessageFormat form = new MessageFormat(
2063+ rb.getString("signerClass.is.not.a.signing.mechanism"));
2064+ Object[] source = {signerClass.getName()};
2065+ throw new IllegalArgumentException(form.format(source));
2066+ }
2067+ return (ContentSigner)signer;
2068+ }
2069+}
2070+
2071+/**
2072+ * This is a BASE64Encoder that does not insert a default newline at the end of
2073+ * every output line. This is necessary because java.util.jar does its own
2074+ * line management (see Manifest.make72Safe()). Inserting additional new lines
2075+ * can cause line-wrapping problems (see CR 6219522).
2076+ */
2077+class JarBASE64Encoder extends BASE64Encoder {
2078+ /**
2079+ * Encode the suffix that ends every output line.
2080+ */
2081+ protected void encodeLineSuffix(OutputStream aStream) throws IOException { }
2082+}
2083+
2084+class SignatureFile {
2085+
2086+ /** SignatureFile */
2087+ Manifest sf;
2088+
2089+ /** .SF base name */
2090+ String baseName;
2091+
2092+ public SignatureFile(MessageDigest digests[],
2093+ Manifest mf,
2094+ ManifestDigester md,
2095+ String baseName,
2096+ boolean signManifest)
2097+
2098+ {
2099+ this.baseName = baseName;
2100+
2101+ String version = System.getProperty("java.version");
2102+ String javaVendor = System.getProperty("java.vendor");
2103+
2104+ sf = new Manifest();
2105+ Attributes mattr = sf.getMainAttributes();
2106+ BASE64Encoder encoder = new JarBASE64Encoder();
2107+
2108+ mattr.putValue(Attributes.Name.SIGNATURE_VERSION.toString(), "1.0");
2109+ mattr.putValue("Created-By", version + " (" + javaVendor + ")");
2110+
2111+ if (signManifest) {
2112+ // sign the whole manifest
2113+ for (int i=0; i < digests.length; i++) {
2114+ mattr.putValue(digests[i].getAlgorithm()+"-Digest-Manifest",
2115+ encoder.encode(md.manifestDigest(digests[i])));
2116+ }
2117+ }
2118+
2119+ // create digest of the manifest main attributes
2120+ ManifestDigester.Entry mde =
2121+ md.get(ManifestDigester.MF_MAIN_ATTRS, false);
2122+ if (mde != null) {
2123+ for (int i=0; i < digests.length; i++) {
2124+ mattr.putValue(digests[i].getAlgorithm() +
2125+ "-Digest-" + ManifestDigester.MF_MAIN_ATTRS,
2126+ encoder.encode(mde.digest(digests[i])));
2127+ }
2128+ } else {
2129+ throw new IllegalStateException
2130+ ("ManifestDigester failed to create " +
2131+ "Manifest-Main-Attribute entry");
2132+ }
2133+
2134+ /* go through the manifest entries and create the digests */
2135+
2136+ Map<String,Attributes> entries = sf.getEntries();
2137+ Iterator<Map.Entry<String,Attributes>> mit =
2138+ mf.getEntries().entrySet().iterator();
2139+ while(mit.hasNext()) {
2140+ Map.Entry<String,Attributes> e = mit.next();
2141+ String name = e.getKey();
2142+ mde = md.get(name, false);
2143+ if (mde != null) {
2144+ Attributes attr = new Attributes();
2145+ for (int i=0; i < digests.length; i++) {
2146+ attr.putValue(digests[i].getAlgorithm()+"-Digest",
2147+ encoder.encode(mde.digest(digests[i])));
2148+ }
2149+ entries.put(name, attr);
2150+ }
2151+ }
2152+ }
2153+
2154+ /**
2155+ * Writes the SignatureFile to the specified OutputStream.
2156+ *
2157+ * @param out the output stream
2158+ * @exception IOException if an I/O error has occurred
2159+ */
2160+
2161+ public void write(OutputStream out) throws IOException
2162+ {
2163+ sf.write(out);
2164+ }
2165+
2166+ /**
2167+ * get .SF file name
2168+ */
2169+ public String getMetaName()
2170+ {
2171+ return "META-INF/"+ baseName + ".SF";
2172+ }
2173+
2174+ /**
2175+ * get base file name
2176+ */
2177+ public String getBaseName()
2178+ {
2179+ return baseName;
2180+ }
2181+
2182+ /*
2183+ * Generate a signed data block.
2184+ * If a URL or a certificate (containing a URL) for a Timestamping
2185+ * Authority is supplied then a signature timestamp is generated and
2186+ * inserted into the signed data block.
2187+ *
2188+ * @param sigalg signature algorithm to use, or null to use default
2189+ * @param tsaUrl The location of the Timestamping Authority. If null
2190+ * then no timestamp is requested.
2191+ * @param tsaCert The certificate for the Timestamping Authority. If null
2192+ * then no timestamp is requested.
2193+ * @param signingMechanism The signing mechanism to use.
2194+ * @param args The command-line arguments to jarsigner.
2195+ * @param zipFile The original source Zip file.
2196+ */
2197+ public Block generateBlock(PrivateKey privateKey,
2198+ String sigalg,
2199+ X509Certificate[] certChain,
2200+ boolean externalSF, String tsaUrl,
2201+ X509Certificate tsaCert,
2202+ ContentSigner signingMechanism,
2203+ String[] args, ZipFile zipFile)
2204+ throws NoSuchAlgorithmException, InvalidKeyException, IOException,
2205+ SignatureException, CertificateException
2206+ {
2207+ return new Block(this, privateKey, sigalg, certChain, externalSF,
2208+ tsaUrl, tsaCert, signingMechanism, args, zipFile);
2209+ }
2210+
2211+
2212+ public static class Block {
2213+
2214+ private byte[] block;
2215+ private String blockFileName;
2216+
2217+ /*
2218+ * Construct a new signature block.
2219+ */
2220+ Block(SignatureFile sfg, PrivateKey privateKey, String sigalg,
2221+ X509Certificate[] certChain, boolean externalSF, String tsaUrl,
2222+ X509Certificate tsaCert, ContentSigner signingMechanism,
2223+ String[] args, ZipFile zipFile)
2224+ throws NoSuchAlgorithmException, InvalidKeyException, IOException,
2225+ SignatureException, CertificateException {
2226+
2227+ Principal issuerName = certChain[0].getIssuerDN();
2228+ if (!(issuerName instanceof X500Name)) {
2229+ // must extract the original encoded form of DN for subsequent
2230+ // name comparison checks (converting to a String and back to
2231+ // an encoded DN could cause the types of String attribute
2232+ // values to be changed)
2233+ X509CertInfo tbsCert = new
2234+ X509CertInfo(certChain[0].getTBSCertificate());
2235+ issuerName = (Principal)
2236+ tbsCert.get(CertificateIssuerName.NAME + "." +
2237+ CertificateIssuerName.DN_NAME);
2238+ }
2239+ BigInteger serial = certChain[0].getSerialNumber();
2240+
2241+ String signatureAlgorithm;
2242+ String keyAlgorithm = privateKey.getAlgorithm();
2243+ /*
2244+ * If no signature algorithm was specified, we choose a
2245+ * default that is compatible with the private key algorithm.
2246+ */
2247+ if (sigalg == null) {
2248+
2249+ if (keyAlgorithm.equalsIgnoreCase("DSA"))
2250+ signatureAlgorithm = "SHA1withDSA";
2251+ else if (keyAlgorithm.equalsIgnoreCase("RSA"))
2252+ signatureAlgorithm = "SHA256withRSA";
2253+ else if (keyAlgorithm.equalsIgnoreCase("EC"))
2254+ signatureAlgorithm = "SHA256withECDSA";
2255+ else
2256+ throw new RuntimeException("private key is not a DSA or "
2257+ + "RSA key");
2258+ } else {
2259+ signatureAlgorithm = sigalg;
2260+ }
2261+
2262+ // check common invalid key/signature algorithm combinations
2263+ String sigAlgUpperCase = signatureAlgorithm.toUpperCase(Locale.ENGLISH);
2264+ if ((sigAlgUpperCase.endsWith("WITHRSA") &&
2265+ !keyAlgorithm.equalsIgnoreCase("RSA")) ||
2266+ (sigAlgUpperCase.endsWith("WITHECDSA") &&
2267+ !keyAlgorithm.equalsIgnoreCase("EC")) ||
2268+ (sigAlgUpperCase.endsWith("WITHDSA") &&
2269+ !keyAlgorithm.equalsIgnoreCase("DSA"))) {
2270+ throw new SignatureException
2271+ ("private key algorithm is not compatible with signature algorithm");
2272+ }
2273+
2274+ blockFileName = "META-INF/"+sfg.getBaseName()+"."+keyAlgorithm;
2275+
2276+ AlgorithmId sigAlg = AlgorithmId.get(signatureAlgorithm);
2277+ AlgorithmId digEncrAlg = AlgorithmId.get(keyAlgorithm);
2278+
2279+ Signature sig = Signature.getInstance(signatureAlgorithm);
2280+ sig.initSign(privateKey);
2281+
2282+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
2283+ sfg.write(baos);
2284+
2285+ byte[] content = baos.toByteArray();
2286+
2287+ sig.update(content);
2288+ byte[] signature = sig.sign();
2289+
2290+ // Timestamp the signature and generate the signature block file
2291+ if (signingMechanism == null) {
2292+ signingMechanism = new TimestampedSigner();
2293+ }
2294+ URI tsaUri = null;
2295+ try {
2296+ if (tsaUrl != null) {
2297+ tsaUri = new URI(tsaUrl);
2298+ }
2299+ } catch (URISyntaxException e) {
2300+ IOException ioe = new IOException();
2301+ ioe.initCause(e);
2302+ throw ioe;
2303+ }
2304+
2305+ // Assemble parameters for the signing mechanism
2306+ ContentSignerParameters params =
2307+ new JarSignerParameters(args, tsaUri, tsaCert, signature,
2308+ signatureAlgorithm, certChain, content, zipFile);
2309+
2310+ // Generate the signature block
2311+ block = signingMechanism.generateSignedData(
2312+ params, externalSF, (tsaUrl != null || tsaCert != null));
2313+ }
2314+
2315+ /*
2316+ * get block file name.
2317+ */
2318+ public String getMetaName()
2319+ {
2320+ return blockFileName;
2321+ }
2322+
2323+ /**
2324+ * Writes the block file to the specified OutputStream.
2325+ *
2326+ * @param out the output stream
2327+ * @exception IOException if an I/O error has occurred
2328+ */
2329+
2330+ public void write(OutputStream out) throws IOException
2331+ {
2332+ out.write(block);
2333+ }
2334+ }
2335+}
2336+
2337+
2338+/*
2339+ * This object encapsulates the parameters used to perform content signing.
2340+ */
2341+class JarSignerParameters implements ContentSignerParameters {
2342+
2343+ private String[] args;
2344+ private URI tsa;
2345+ private X509Certificate tsaCertificate;
2346+ private byte[] signature;
2347+ private String signatureAlgorithm;
2348+ private X509Certificate[] signerCertificateChain;
2349+ private byte[] content;
2350+ private ZipFile source;
2351+
2352+ /**
2353+ * Create a new object.
2354+ */
2355+ JarSignerParameters(String[] args, URI tsa, X509Certificate tsaCertificate,
2356+ byte[] signature, String signatureAlgorithm,
2357+ X509Certificate[] signerCertificateChain, byte[] content,
2358+ ZipFile source) {
2359+
2360+ if (signature == null || signatureAlgorithm == null ||
2361+ signerCertificateChain == null) {
2362+ throw new NullPointerException();
2363+ }
2364+ this.args = args;
2365+ this.tsa = tsa;
2366+ this.tsaCertificate = tsaCertificate;
2367+ this.signature = signature;
2368+ this.signatureAlgorithm = signatureAlgorithm;
2369+ this.signerCertificateChain = signerCertificateChain;
2370+ this.content = content;
2371+ this.source = source;
2372+ }
2373+
2374+ /**
2375+ * Retrieves the command-line arguments.
2376+ *
2377+ * @return The command-line arguments. May be null.
2378+ */
2379+ public String[] getCommandLine() {
2380+ return args;
2381+ }
2382+
2383+ /**
2384+ * Retrieves the identifier for a Timestamping Authority (TSA).
2385+ *
2386+ * @return The TSA identifier. May be null.
2387+ */
2388+ public URI getTimestampingAuthority() {
2389+ return tsa;
2390+ }
2391+
2392+ /**
2393+ * Retrieves the certificate for a Timestamping Authority (TSA).
2394+ *
2395+ * @return The TSA certificate. May be null.
2396+ */
2397+ public X509Certificate getTimestampingAuthorityCertificate() {
2398+ return tsaCertificate;
2399+ }
2400+
2401+ /**
2402+ * Retrieves the signature.
2403+ *
2404+ * @return The non-null signature bytes.
2405+ */
2406+ public byte[] getSignature() {
2407+ return signature;
2408+ }
2409+
2410+ /**
2411+ * Retrieves the name of the signature algorithm.
2412+ *
2413+ * @return The non-null string name of the signature algorithm.
2414+ */
2415+ public String getSignatureAlgorithm() {
2416+ return signatureAlgorithm;
2417+ }
2418+
2419+ /**
2420+ * Retrieves the signer's X.509 certificate chain.
2421+ *
2422+ * @return The non-null array of X.509 public-key certificates.
2423+ */
2424+ public X509Certificate[] getSignerCertificateChain() {
2425+ return signerCertificateChain;
2426+ }
2427+
2428+ /**
2429+ * Retrieves the content that was signed.
2430+ *
2431+ * @return The content bytes. May be null.
2432+ */
2433+ public byte[] getContent() {
2434+ return content;
2435+ }
2436+
2437+ /**
2438+ * Retrieves the original source ZIP file before it was signed.
2439+ *
2440+ * @return The original ZIP file. May be null.
2441+ */
2442+ public ZipFile getSource() {
2443+ return source;
2444+ }
2445+}