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