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
26package sun.security.tools;
27
28import java.io.*;
29import java.util.*;
30import java.util.zip.*;
31import java.util.jar.*;
32import java.math.BigInteger;
33import java.net.URI;
34import java.net.URISyntaxException;
35import java.text.Collator;
36import java.text.MessageFormat;
37import java.security.cert.Certificate;
38import java.security.cert.X509Certificate;
39import java.security.cert.CertificateException;
40import java.security.*;
41import java.lang.reflect.Constructor;
42
43import com.sun.jarsigner.ContentSigner;
44import com.sun.jarsigner.ContentSignerParameters;
45import java.net.SocketTimeoutException;
46import java.net.URL;
47import java.net.URLClassLoader;
48import java.security.cert.CertPath;
49import java.security.cert.CertPathValidator;
50import java.security.cert.CertificateExpiredException;
51import java.security.cert.CertificateFactory;
52import java.security.cert.CertificateNotYetValidException;
53import java.security.cert.PKIXParameters;
54import java.security.cert.TrustAnchor;
55import java.util.Map.Entry;
56import sun.security.x509.*;
57import sun.security.util.*;
58import 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
77public 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 */
2077class JarBASE64Encoder extends BASE64Encoder {
2078 /**
2079 * Encode the suffix that ends every output line.
2080 */
2081 protected void encodeLineSuffix(OutputStream aStream) throws IOException { }
2082}
2083
2084class 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 */
2341class 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}