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