Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openjdk-multiarch-jdk8u
Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/security/tools/jarsigner/Main.java
38923 views
1
/*
2
* Copyright (c) 1997, 2017, 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.security.cert.CertPathValidatorException;
30
import java.security.cert.PKIXBuilderParameters;
31
import java.util.*;
32
import java.util.stream.Collectors;
33
import java.util.zip.*;
34
import java.util.jar.*;
35
import java.math.BigInteger;
36
import java.net.URI;
37
import java.net.URISyntaxException;
38
import java.text.Collator;
39
import java.text.MessageFormat;
40
import java.security.cert.Certificate;
41
import java.security.cert.X509Certificate;
42
import java.security.cert.CertificateException;
43
import java.security.*;
44
import java.lang.reflect.Constructor;
45
46
import com.sun.jarsigner.ContentSigner;
47
import com.sun.jarsigner.ContentSignerParameters;
48
import java.net.SocketTimeoutException;
49
import java.net.URL;
50
import java.net.URLClassLoader;
51
import java.security.cert.CertPath;
52
import java.security.cert.CertificateExpiredException;
53
import java.security.cert.CertificateFactory;
54
import java.security.cert.CertificateNotYetValidException;
55
import java.security.cert.TrustAnchor;
56
import java.util.Map.Entry;
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.tools.PathList;
62
import sun.security.validator.Validator;
63
import sun.security.validator.ValidatorException;
64
import sun.security.x509.*;
65
import sun.security.util.*;
66
import java.util.Base64;
67
68
69
/**
70
* <p>The jarsigner utility.
71
*
72
* The exit codes for the main method are:
73
*
74
* 0: success
75
* 1: any error that the jar cannot be signed or verified, including:
76
* keystore loading error
77
* TSP communication error
78
* jarsigner command line error...
79
* otherwise: error codes from -strict
80
*
81
* @author Roland Schemers
82
* @author Jan Luehe
83
*/
84
85
public class Main {
86
87
// for i18n
88
private static final java.util.ResourceBundle rb =
89
java.util.ResourceBundle.getBundle
90
("sun.security.tools.jarsigner.Resources");
91
private static final Collator collator = Collator.getInstance();
92
static {
93
// this is for case insensitive string comparisions
94
collator.setStrength(Collator.PRIMARY);
95
}
96
97
private static final String META_INF = "META-INF/";
98
99
private static final Class<?>[] PARAM_STRING = { String.class };
100
101
private static final String NONE = "NONE";
102
private static final String P11KEYSTORE = "PKCS11";
103
104
private static final long SIX_MONTHS = 180*24*60*60*1000L; //milliseconds
105
private static final long ONE_YEAR = 366*24*60*60*1000L;
106
107
private static final DisabledAlgorithmConstraints DISABLED_CHECK =
108
new DisabledAlgorithmConstraints(
109
DisabledAlgorithmConstraints.PROPERTY_JAR_DISABLED_ALGS);
110
111
private static final DisabledAlgorithmConstraints LEGACY_CHECK =
112
new DisabledAlgorithmConstraints(
113
DisabledAlgorithmConstraints.PROPERTY_SECURITY_LEGACY_ALGS);
114
115
private static final Set<CryptoPrimitive> DIGEST_PRIMITIVE_SET = Collections
116
.unmodifiableSet(EnumSet.of(CryptoPrimitive.MESSAGE_DIGEST));
117
private static final Set<CryptoPrimitive> SIG_PRIMITIVE_SET = Collections
118
.unmodifiableSet(EnumSet.of(CryptoPrimitive.SIGNATURE));
119
120
static final String VERSION = "1.0";
121
122
static final int IN_KEYSTORE = 0x01; // signer is in keystore
123
static final int IN_SCOPE = 0x02;
124
static final int NOT_ALIAS = 0x04; // alias list is NOT empty and
125
// signer is not in alias list
126
static final int SIGNED_BY_ALIAS = 0x08; // signer is in alias list
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
Vector<String> providers = null; // list of providers
151
// arguments for provider constructors
152
HashMap<String,String> providerArgs = new HashMap<>();
153
char[] keypass; // private key password
154
String sigfile; // name of .SF file
155
String sigalg; // name of signature algorithm
156
String digestalg = "SHA-256"; // name of digest algorithm
157
String signedjar; // output filename
158
String tsaUrl; // location of the Timestamping Authority
159
String tsaAlias; // alias for the Timestamping Authority's certificate
160
String altCertChain; // file to read alternative cert chain from
161
String tSAPolicyID;
162
String tSADigestAlg = "SHA-256";
163
boolean verify = false; // verify the jar
164
String verbose = null; // verbose output when signing/verifying
165
boolean showcerts = false; // show certs when verifying
166
boolean debug = false; // debug
167
boolean signManifest = true; // "sign" the whole manifest
168
boolean externalSF = true; // leave the .SF out of the PKCS7 block
169
boolean strict = false; // treat warnings as error
170
171
// read zip entry raw bytes
172
private ByteArrayOutputStream baos = new ByteArrayOutputStream(2048);
173
private byte[] buffer = new byte[8192];
174
private ContentSigner signingMechanism = null;
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
parseArgs(args);
230
231
// Try to load and install the specified providers
232
if (providers != null) {
233
ClassLoader cl = ClassLoader.getSystemClassLoader();
234
Enumeration<String> e = providers.elements();
235
while (e.hasMoreElements()) {
236
String provName = e.nextElement();
237
Class<?> provClass;
238
if (cl != null) {
239
provClass = cl.loadClass(provName);
240
} else {
241
provClass = Class.forName(provName);
242
}
243
244
String provArg = providerArgs.get(provName);
245
Object obj;
246
if (provArg == null) {
247
obj = provClass.newInstance();
248
} else {
249
Constructor<?> c =
250
provClass.getConstructor(PARAM_STRING);
251
obj = c.newInstance(provArg);
252
}
253
254
if (!(obj instanceof Provider)) {
255
MessageFormat form = new MessageFormat(rb.getString
256
("provName.not.a.provider"));
257
Object[] source = {provName};
258
throw new Exception(form.format(source));
259
}
260
Security.addProvider((Provider)obj);
261
}
262
}
263
264
if (verify) {
265
try {
266
loadKeyStore(keystore, false);
267
} catch (Exception e) {
268
if ((keystore != null) || (storepass != null)) {
269
System.out.println(rb.getString("jarsigner.error.") +
270
e.getMessage());
271
System.exit(1);
272
}
273
}
274
/* if (debug) {
275
SignatureFileVerifier.setDebug(true);
276
ManifestEntryVerifier.setDebug(true);
277
}
278
*/
279
verifyJar(jarfile);
280
} else {
281
loadKeyStore(keystore, true);
282
getAliasInfo(alias);
283
284
// load the alternative signing mechanism
285
if (altSignerClass != null) {
286
signingMechanism = loadSigningMechanism(altSignerClass,
287
altSignerClasspath);
288
}
289
signJar(jarfile, alias, args);
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
}
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
void parseArgs(String args[]) {
338
/* parse flags */
339
int n = 0;
340
341
if (args.length == 0) fullusage();
342
for (n=0; n < args.length; n++) {
343
344
String flags = args[n];
345
String modifier = null;
346
347
if (flags.startsWith("-")) {
348
int pos = flags.indexOf(':');
349
if (pos > 0) {
350
modifier = flags.substring(pos+1);
351
flags = flags.substring(0, pos);
352
}
353
}
354
355
if (!flags.startsWith("-")) {
356
if (jarfile == null) {
357
jarfile = flags;
358
} else {
359
alias = flags;
360
ckaliases.add(alias);
361
}
362
} else if (collator.compare(flags, "-keystore") == 0) {
363
if (++n == args.length) usageNoArg();
364
keystore = args[n];
365
} else if (collator.compare(flags, "-storepass") ==0) {
366
if (++n == args.length) usageNoArg();
367
storepass = getPass(modifier, args[n]);
368
} else if (collator.compare(flags, "-storetype") ==0) {
369
if (++n == args.length) usageNoArg();
370
storetype = args[n];
371
} else if (collator.compare(flags, "-providerName") ==0) {
372
if (++n == args.length) usageNoArg();
373
providerName = args[n];
374
} else if ((collator.compare(flags, "-provider") == 0) ||
375
(collator.compare(flags, "-providerClass") == 0)) {
376
if (++n == args.length) usageNoArg();
377
if (providers == null) {
378
providers = new Vector<String>(3);
379
}
380
providers.add(args[n]);
381
382
if (args.length > (n+1)) {
383
flags = args[n+1];
384
if (collator.compare(flags, "-providerArg") == 0) {
385
if (args.length == (n+2)) usageNoArg();
386
providerArgs.put(args[n], args[n+2]);
387
n += 2;
388
}
389
}
390
} else if (collator.compare(flags, "-protected") ==0) {
391
protectedPath = true;
392
} else if (collator.compare(flags, "-certchain") ==0) {
393
if (++n == args.length) usageNoArg();
394
altCertChain = args[n];
395
} else if (collator.compare(flags, "-tsapolicyid") ==0) {
396
if (++n == args.length) usageNoArg();
397
tSAPolicyID = args[n];
398
} else if (collator.compare(flags, "-tsadigestalg") ==0) {
399
if (++n == args.length) usageNoArg();
400
tSADigestAlg = args[n];
401
} else if (collator.compare(flags, "-debug") ==0) {
402
debug = true;
403
} else if (collator.compare(flags, "-keypass") ==0) {
404
if (++n == args.length) usageNoArg();
405
keypass = getPass(modifier, args[n]);
406
} else if (collator.compare(flags, "-sigfile") ==0) {
407
if (++n == args.length) usageNoArg();
408
sigfile = args[n];
409
} else if (collator.compare(flags, "-signedjar") ==0) {
410
if (++n == args.length) usageNoArg();
411
signedjar = args[n];
412
} else if (collator.compare(flags, "-tsa") ==0) {
413
if (++n == args.length) usageNoArg();
414
tsaUrl = args[n];
415
} else if (collator.compare(flags, "-tsacert") ==0) {
416
if (++n == args.length) usageNoArg();
417
tsaAlias = args[n];
418
} else if (collator.compare(flags, "-altsigner") ==0) {
419
if (++n == args.length) usageNoArg();
420
altSignerClass = args[n];
421
} else if (collator.compare(flags, "-altsignerpath") ==0) {
422
if (++n == args.length) usageNoArg();
423
altSignerClasspath = args[n];
424
} else if (collator.compare(flags, "-sectionsonly") ==0) {
425
signManifest = false;
426
} else if (collator.compare(flags, "-internalsf") ==0) {
427
externalSF = false;
428
} else if (collator.compare(flags, "-verify") ==0) {
429
verify = true;
430
} else if (collator.compare(flags, "-verbose") ==0) {
431
verbose = (modifier != null) ? modifier : "all";
432
} else if (collator.compare(flags, "-sigalg") ==0) {
433
if (++n == args.length) usageNoArg();
434
sigalg = args[n];
435
} else if (collator.compare(flags, "-digestalg") ==0) {
436
if (++n == args.length) usageNoArg();
437
digestalg = args[n];
438
} else if (collator.compare(flags, "-certs") ==0) {
439
showcerts = true;
440
} else if (collator.compare(flags, "-strict") ==0) {
441
strict = true;
442
} else if (collator.compare(flags, "-h") == 0 ||
443
collator.compare(flags, "-help") == 0) {
444
fullusage();
445
} else {
446
System.err.println(
447
rb.getString("Illegal.option.") + flags);
448
usage();
449
}
450
}
451
452
// -certs must always be specified with -verbose
453
if (verbose == null) showcerts = false;
454
455
if (jarfile == null) {
456
System.err.println(rb.getString("Please.specify.jarfile.name"));
457
usage();
458
}
459
if (!verify && alias == null) {
460
System.err.println(rb.getString("Please.specify.alias.name"));
461
usage();
462
}
463
if (!verify && ckaliases.size() > 1) {
464
System.err.println(rb.getString("Only.one.alias.can.be.specified"));
465
usage();
466
}
467
468
if (storetype == null) {
469
storetype = KeyStore.getDefaultType();
470
}
471
storetype = KeyStoreUtil.niceStoreTypeName(storetype);
472
473
try {
474
if (signedjar != null && new File(signedjar).getCanonicalPath().equals(
475
new File(jarfile).getCanonicalPath())) {
476
signedjar = null;
477
}
478
} catch (IOException ioe) {
479
// File system error?
480
// Just ignore it.
481
}
482
483
if (P11KEYSTORE.equalsIgnoreCase(storetype) ||
484
KeyStoreUtil.isWindowsKeyStore(storetype)) {
485
token = true;
486
if (keystore == null) {
487
keystore = NONE;
488
}
489
}
490
491
if (NONE.equals(keystore)) {
492
nullStream = true;
493
}
494
495
if (token && !nullStream) {
496
System.err.println(MessageFormat.format(rb.getString
497
(".keystore.must.be.NONE.if.storetype.is.{0}"), storetype));
498
usage();
499
}
500
501
if (token && keypass != null) {
502
System.err.println(MessageFormat.format(rb.getString
503
(".keypass.can.not.be.specified.if.storetype.is.{0}"), storetype));
504
usage();
505
}
506
507
if (protectedPath) {
508
if (storepass != null || keypass != null) {
509
System.err.println(rb.getString
510
("If.protected.is.specified.then.storepass.and.keypass.must.not.be.specified"));
511
usage();
512
}
513
}
514
if (KeyStoreUtil.isWindowsKeyStore(storetype)) {
515
if (storepass != null || keypass != null) {
516
System.err.println(rb.getString
517
("If.keystore.is.not.password.protected.then.storepass.and.keypass.must.not.be.specified"));
518
usage();
519
}
520
}
521
}
522
523
static char[] getPass(String modifier, String arg) {
524
char[] output = KeyStoreUtil.getPassWithModifier(modifier, arg, rb);
525
if (output != null) return output;
526
usage();
527
return null; // Useless, usage() already exit
528
}
529
530
static void usageNoArg() {
531
System.out.println(rb.getString("Option.lacks.argument"));
532
usage();
533
}
534
535
static void usage() {
536
System.out.println();
537
System.out.println(rb.getString("Please.type.jarsigner.help.for.usage"));
538
System.exit(1);
539
}
540
541
static void fullusage() {
542
System.out.println(rb.getString
543
("Usage.jarsigner.options.jar.file.alias"));
544
System.out.println(rb.getString
545
(".jarsigner.verify.options.jar.file.alias."));
546
System.out.println();
547
System.out.println(rb.getString
548
(".keystore.url.keystore.location"));
549
System.out.println();
550
System.out.println(rb.getString
551
(".storepass.password.password.for.keystore.integrity"));
552
System.out.println();
553
System.out.println(rb.getString
554
(".storetype.type.keystore.type"));
555
System.out.println();
556
System.out.println(rb.getString
557
(".keypass.password.password.for.private.key.if.different."));
558
System.out.println();
559
System.out.println(rb.getString
560
(".certchain.file.name.of.alternative.certchain.file"));
561
System.out.println();
562
System.out.println(rb.getString
563
(".sigfile.file.name.of.SF.DSA.file"));
564
System.out.println();
565
System.out.println(rb.getString
566
(".signedjar.file.name.of.signed.JAR.file"));
567
System.out.println();
568
System.out.println(rb.getString
569
(".digestalg.algorithm.name.of.digest.algorithm"));
570
System.out.println();
571
System.out.println(rb.getString
572
(".sigalg.algorithm.name.of.signature.algorithm"));
573
System.out.println();
574
System.out.println(rb.getString
575
(".verify.verify.a.signed.JAR.file"));
576
System.out.println();
577
System.out.println(rb.getString
578
(".verbose.suboptions.verbose.output.when.signing.verifying."));
579
System.out.println(rb.getString
580
(".suboptions.can.be.all.grouped.or.summary"));
581
System.out.println();
582
System.out.println(rb.getString
583
(".certs.display.certificates.when.verbose.and.verifying"));
584
System.out.println();
585
System.out.println(rb.getString
586
(".tsa.url.location.of.the.Timestamping.Authority"));
587
System.out.println();
588
System.out.println(rb.getString
589
(".tsacert.alias.public.key.certificate.for.Timestamping.Authority"));
590
System.out.println();
591
System.out.println(rb.getString
592
(".tsapolicyid.tsapolicyid.for.Timestamping.Authority"));
593
System.out.println();
594
System.out.println(rb.getString
595
(".tsadigestalg.algorithm.of.digest.data.in.timestamping.request"));
596
System.out.println();
597
System.out.println(rb.getString
598
(".altsigner.class.class.name.of.an.alternative.signing.mechanism"));
599
System.out.println();
600
System.out.println(rb.getString
601
(".altsignerpath.pathlist.location.of.an.alternative.signing.mechanism"));
602
System.out.println();
603
System.out.println(rb.getString
604
(".internalsf.include.the.SF.file.inside.the.signature.block"));
605
System.out.println();
606
System.out.println(rb.getString
607
(".sectionsonly.don.t.compute.hash.of.entire.manifest"));
608
System.out.println();
609
System.out.println(rb.getString
610
(".protected.keystore.has.protected.authentication.path"));
611
System.out.println();
612
System.out.println(rb.getString
613
(".providerName.name.provider.name"));
614
System.out.println();
615
System.out.println(rb.getString
616
(".providerClass.class.name.of.cryptographic.service.provider.s"));
617
System.out.println(rb.getString
618
(".providerArg.arg.master.class.file.and.constructor.argument"));
619
System.out.println();
620
System.out.println(rb.getString
621
(".strict.treat.warnings.as.errors"));
622
System.out.println();
623
624
System.exit(0);
625
}
626
627
void verifyJar(String jarName)
628
throws Exception
629
{
630
boolean anySigned = false; // if there exists entry inside jar signed
631
JarFile jf = null;
632
Map<String,String> digestMap = new HashMap<>();
633
Map<String,PKCS7> sigMap = new HashMap<>();
634
Map<String,String> sigNameMap = new HashMap<>();
635
Map<String,String> unparsableSignatures = new HashMap<>();
636
637
try {
638
jf = new JarFile(jarName, true);
639
Vector<JarEntry> entriesVec = new Vector<>();
640
byte[] buffer = new byte[8192];
641
642
Enumeration<JarEntry> entries = jf.entries();
643
while (entries.hasMoreElements()) {
644
JarEntry je = entries.nextElement();
645
entriesVec.addElement(je);
646
try (InputStream is = jf.getInputStream(je)) {
647
String name = je.getName();
648
if (signatureRelated(name)
649
&& SignatureFileVerifier.isBlockOrSF(name)) {
650
String alias = name.substring(name.lastIndexOf('/') + 1,
651
name.lastIndexOf('.'));
652
try {
653
if (name.endsWith(".SF")) {
654
Manifest sf = new Manifest(is);
655
boolean found = false;
656
for (Object obj : sf.getMainAttributes().keySet()) {
657
String key = obj.toString();
658
if (key.endsWith("-Digest-Manifest")) {
659
digestMap.put(alias,
660
key.substring(0, key.length() - 16));
661
found = true;
662
break;
663
}
664
}
665
if (!found) {
666
unparsableSignatures.putIfAbsent(alias,
667
String.format(
668
rb.getString("history.unparsable"),
669
name));
670
}
671
} else {
672
sigNameMap.put(alias, name);
673
sigMap.put(alias, new PKCS7(is));
674
}
675
} catch (IOException ioe) {
676
unparsableSignatures.putIfAbsent(alias, String.format(
677
rb.getString("history.unparsable"), name));
678
}
679
} else {
680
while (is.read(buffer, 0, buffer.length) != -1) {
681
// we just read. this will throw a SecurityException
682
// if a signature/digest check fails.
683
}
684
}
685
}
686
}
687
688
Manifest man = jf.getManifest();
689
boolean hasSignature = false;
690
691
// The map to record display info, only used when -verbose provided
692
// key: signer info string
693
// value: the list of files with common key
694
Map<String,List<String>> output = new LinkedHashMap<>();
695
696
if (man != null) {
697
if (verbose != null) System.out.println();
698
Enumeration<JarEntry> e = entriesVec.elements();
699
700
String tab = rb.getString("6SPACE");
701
702
while (e.hasMoreElements()) {
703
JarEntry je = e.nextElement();
704
String name = je.getName();
705
706
hasSignature = hasSignature
707
|| SignatureFileVerifier.isBlockOrSF(name);
708
709
CodeSigner[] signers = je.getCodeSigners();
710
boolean isSigned = (signers != null);
711
anySigned |= isSigned;
712
hasUnsignedEntry |= !je.isDirectory() && !isSigned
713
&& !signatureRelated(name);
714
715
int inStoreOrScope = inKeyStore(signers);
716
717
boolean inStore = (inStoreOrScope & IN_KEYSTORE) != 0;
718
boolean inScope = (inStoreOrScope & IN_SCOPE) != 0;
719
720
notSignedByAlias |= (inStoreOrScope & NOT_ALIAS) != 0;
721
if (keystore != null) {
722
aliasNotInStore |= isSigned && (!inStore && !inScope);
723
}
724
725
// Only used when -verbose provided
726
StringBuffer sb = null;
727
if (verbose != null) {
728
sb = new StringBuffer();
729
boolean inManifest =
730
((man.getAttributes(name) != null) ||
731
(man.getAttributes("./"+name) != null) ||
732
(man.getAttributes("/"+name) != null));
733
sb.append(
734
(isSigned ? rb.getString("s") : rb.getString("SPACE")) +
735
(inManifest ? rb.getString("m") : rb.getString("SPACE")) +
736
(inStore ? rb.getString("k") : rb.getString("SPACE")) +
737
(inScope ? rb.getString("i") : rb.getString("SPACE")) +
738
((inStoreOrScope & NOT_ALIAS) != 0 ?"X":" ") +
739
rb.getString("SPACE"));
740
sb.append("|");
741
}
742
743
// When -certs provided, display info has extra empty
744
// lines at the beginning and end.
745
if (isSigned) {
746
if (showcerts) sb.append('\n');
747
for (CodeSigner signer: signers) {
748
// signerInfo() must be called even if -verbose
749
// not provided. The method updates various
750
// warning flags.
751
String si = signerInfo(signer, tab);
752
if (showcerts) {
753
sb.append(si);
754
sb.append('\n');
755
}
756
}
757
} else if (showcerts && !verbose.equals("all")) {
758
// Print no info for unsigned entries when -verbose:all,
759
// to be consistent with old behavior.
760
if (signatureRelated(name)) {
761
sb.append("\n" + tab + rb.getString(
762
".Signature.related.entries.") + "\n\n");
763
} else {
764
sb.append("\n" + tab + rb.getString(
765
".Unsigned.entries.") + "\n\n");
766
}
767
}
768
769
if (verbose != null) {
770
String label = sb.toString();
771
if (signatureRelated(name)) {
772
// Entries inside META-INF and other unsigned
773
// entries are grouped separately.
774
label = "-" + label;
775
}
776
777
// The label finally contains 2 parts separated by '|':
778
// The legend displayed before the entry names, and
779
// the cert info (if -certs specified).
780
781
if (!output.containsKey(label)) {
782
output.put(label, new ArrayList<String>());
783
}
784
785
StringBuffer fb = new StringBuffer();
786
String s = Long.toString(je.getSize());
787
for (int i = 6 - s.length(); i > 0; --i) {
788
fb.append(' ');
789
}
790
fb.append(s).append(' ').
791
append(new Date(je.getTime()).toString());
792
fb.append(' ').append(name);
793
794
output.get(label).add(fb.toString());
795
}
796
}
797
}
798
if (verbose != null) {
799
for (Entry<String,List<String>> s: output.entrySet()) {
800
List<String> files = s.getValue();
801
String key = s.getKey();
802
if (key.charAt(0) == '-') { // the signature-related group
803
key = key.substring(1);
804
}
805
int pipe = key.indexOf('|');
806
if (verbose.equals("all")) {
807
for (String f: files) {
808
System.out.println(key.substring(0, pipe) + f);
809
System.out.printf(key.substring(pipe+1));
810
}
811
} else {
812
if (verbose.equals("grouped")) {
813
for (String f: files) {
814
System.out.println(key.substring(0, pipe) + f);
815
}
816
} else if (verbose.equals("summary")) {
817
System.out.print(key.substring(0, pipe));
818
if (files.size() > 1) {
819
System.out.println(files.get(0) + " " +
820
String.format(rb.getString(
821
".and.d.more."), files.size()-1));
822
} else {
823
System.out.println(files.get(0));
824
}
825
}
826
System.out.printf(key.substring(pipe+1));
827
}
828
}
829
System.out.println();
830
System.out.println(rb.getString(
831
".s.signature.was.verified."));
832
System.out.println(rb.getString(
833
".m.entry.is.listed.in.manifest"));
834
System.out.println(rb.getString(
835
".k.at.least.one.certificate.was.found.in.keystore"));
836
System.out.println(rb.getString(
837
".i.at.least.one.certificate.was.found.in.identity.scope"));
838
if (ckaliases.size() > 0) {
839
System.out.println(rb.getString(
840
".X.not.signed.by.specified.alias.es."));
841
}
842
}
843
if (man == null) {
844
System.out.println();
845
System.out.println(rb.getString("no.manifest."));
846
}
847
848
// Even if the verbose option is not specified, all out strings
849
// must be generated so disabledAlgFound can be updated.
850
if (!digestMap.isEmpty()
851
|| !sigMap.isEmpty()
852
|| !unparsableSignatures.isEmpty()) {
853
if (verbose != null) {
854
System.out.println();
855
}
856
for (String s : sigMap.keySet()) {
857
if (!digestMap.containsKey(s)) {
858
unparsableSignatures.putIfAbsent(s, String.format(
859
rb.getString("history.nosf"), s));
860
}
861
}
862
for (String s : digestMap.keySet()) {
863
PKCS7 p7 = sigMap.get(s);
864
if (p7 != null) {
865
String history;
866
try {
867
SignerInfo si = p7.getSignerInfos()[0];
868
X509Certificate signer = si.getCertificate(p7);
869
String digestAlg = digestMap.get(s);
870
String sigAlg = AlgorithmId.makeSigAlg(
871
si.getDigestAlgorithmId().getName(),
872
si.getDigestEncryptionAlgorithmId().getName());
873
PublicKey key = signer.getPublicKey();
874
PKCS7 tsToken = si.getTsToken();
875
if (tsToken != null) {
876
hasTimestampBlock = true;
877
SignerInfo tsSi = tsToken.getSignerInfos()[0];
878
X509Certificate tsSigner = tsSi.getCertificate(tsToken);
879
byte[] encTsTokenInfo = tsToken.getContentInfo().getData();
880
TimestampToken tsTokenInfo = new TimestampToken(encTsTokenInfo);
881
PublicKey tsKey = tsSigner.getPublicKey();
882
String tsDigestAlg = tsTokenInfo.getHashAlgorithm().getName();
883
String tsSigAlg = AlgorithmId.makeSigAlg(
884
tsSi.getDigestAlgorithmId().getName(),
885
tsSi.getDigestEncryptionAlgorithmId().getName());
886
Calendar c = Calendar.getInstance(
887
TimeZone.getTimeZone("UTC"),
888
Locale.getDefault(Locale.Category.FORMAT));
889
c.setTime(tsTokenInfo.getDate());
890
history = String.format(
891
rb.getString("history.with.ts"),
892
signer.getSubjectX500Principal(),
893
verifyWithWeak(digestAlg, DIGEST_PRIMITIVE_SET, false),
894
verifyWithWeak(sigAlg, SIG_PRIMITIVE_SET, false),
895
verifyWithWeak(key),
896
c,
897
tsSigner.getSubjectX500Principal(),
898
verifyWithWeak(tsDigestAlg, DIGEST_PRIMITIVE_SET, true),
899
verifyWithWeak(tsSigAlg, SIG_PRIMITIVE_SET, true),
900
verifyWithWeak(tsKey));
901
} else {
902
history = String.format(
903
rb.getString("history.without.ts"),
904
signer.getSubjectX500Principal(),
905
verifyWithWeak(digestAlg, DIGEST_PRIMITIVE_SET, false),
906
verifyWithWeak(sigAlg, SIG_PRIMITIVE_SET, false),
907
verifyWithWeak(key));
908
}
909
} catch (Exception e) {
910
// The only usage of sigNameMap, remember the name
911
// of the block file if it's invalid.
912
history = String.format(
913
rb.getString("history.unparsable"),
914
sigNameMap.get(s));
915
}
916
if (verbose != null) {
917
System.out.println(history);
918
}
919
} else {
920
unparsableSignatures.putIfAbsent(s, String.format(
921
rb.getString("history.nobk"), s));
922
}
923
}
924
if (verbose != null) {
925
for (String s : unparsableSignatures.keySet()) {
926
System.out.println(unparsableSignatures.get(s));
927
}
928
}
929
}
930
System.out.println();
931
932
// If signer is a trusted cert or private entry in user's own
933
// keystore, it can be self-signed. Please note aliasNotInStore
934
// is always false when ~/.keystore is used.
935
if (!aliasNotInStore && keystore != null) {
936
signerSelfSigned = false;
937
}
938
939
if (!anySigned) {
940
if (disabledAlgFound) {
941
if (verbose != null) {
942
System.out.println(rb.getString("jar.treated.unsigned.see.weak.verbose"));
943
System.out.println("\n " +
944
DisabledAlgorithmConstraints.PROPERTY_JAR_DISABLED_ALGS +
945
"=" + Security.getProperty(DisabledAlgorithmConstraints.PROPERTY_JAR_DISABLED_ALGS));
946
} else {
947
System.out.println(rb.getString("jar.treated.unsigned.see.weak"));
948
}
949
} else if (hasSignature) {
950
System.out.println(rb.getString("jar.treated.unsigned"));
951
} else {
952
System.out.println(rb.getString("jar.is.unsigned"));
953
}
954
} else {
955
displayMessagesAndResult(false);
956
}
957
return;
958
} catch (Exception e) {
959
System.out.println(rb.getString("jarsigner.") + e);
960
if (debug) {
961
e.printStackTrace();
962
}
963
} finally { // close the resource
964
if (jf != null) {
965
jf.close();
966
}
967
}
968
969
System.exit(1);
970
}
971
972
private void displayMessagesAndResult(boolean isSigning) {
973
String result;
974
List<String> errors = new ArrayList<>();
975
List<String> warnings = new ArrayList<>();
976
List<String> info = new ArrayList<>();
977
978
boolean signerNotExpired = expireDate == null
979
|| expireDate.after(new Date());
980
981
if (badKeyUsage || badExtendedKeyUsage || badNetscapeCertType ||
982
notYetValidCert || chainNotValidated || hasExpiredCert ||
983
hasUnsignedEntry || signerSelfSigned || (legacyAlg != 0) ||
984
(disabledAlg != 0) || aliasNotInStore || notSignedByAlias ||
985
tsaChainNotValidated ||
986
(hasExpiredTsaCert && !signerNotExpired)) {
987
988
if (strict) {
989
result = rb.getString(isSigning
990
? "jar.signed.with.signer.errors."
991
: "jar.verified.with.signer.errors.");
992
} else {
993
result = rb.getString(isSigning
994
? "jar.signed."
995
: "jar.verified.");
996
}
997
998
if (badKeyUsage) {
999
errors.add(rb.getString(isSigning
1000
? "The.signer.certificate.s.KeyUsage.extension.doesn.t.allow.code.signing."
1001
: "This.jar.contains.entries.whose.signer.certificate.s.KeyUsage.extension.doesn.t.allow.code.signing."));
1002
}
1003
1004
if (badExtendedKeyUsage) {
1005
errors.add(rb.getString(isSigning
1006
? "The.signer.certificate.s.ExtendedKeyUsage.extension.doesn.t.allow.code.signing."
1007
: "This.jar.contains.entries.whose.signer.certificate.s.ExtendedKeyUsage.extension.doesn.t.allow.code.signing."));
1008
}
1009
1010
if (badNetscapeCertType) {
1011
errors.add(rb.getString(isSigning
1012
? "The.signer.certificate.s.NetscapeCertType.extension.doesn.t.allow.code.signing."
1013
: "This.jar.contains.entries.whose.signer.certificate.s.NetscapeCertType.extension.doesn.t.allow.code.signing."));
1014
}
1015
1016
// only in verifying
1017
if (hasUnsignedEntry) {
1018
errors.add(rb.getString(
1019
"This.jar.contains.unsigned.entries.which.have.not.been.integrity.checked."));
1020
}
1021
if (hasExpiredCert) {
1022
errors.add(rb.getString(isSigning
1023
? "The.signer.certificate.has.expired."
1024
: "This.jar.contains.entries.whose.signer.certificate.has.expired."));
1025
}
1026
if (notYetValidCert) {
1027
errors.add(rb.getString(isSigning
1028
? "The.signer.certificate.is.not.yet.valid."
1029
: "This.jar.contains.entries.whose.signer.certificate.is.not.yet.valid."));
1030
}
1031
1032
if (chainNotValidated) {
1033
errors.add(String.format(rb.getString(isSigning
1034
? "The.signer.s.certificate.chain.is.invalid.reason.1"
1035
: "This.jar.contains.entries.whose.certificate.chain.is.invalid.reason.1"),
1036
chainNotValidatedReason.getLocalizedMessage()));
1037
}
1038
1039
if (hasExpiredTsaCert) {
1040
errors.add(rb.getString("The.timestamp.has.expired."));
1041
}
1042
if (tsaChainNotValidated) {
1043
errors.add(String.format(rb.getString(isSigning
1044
? "The.tsa.certificate.chain.is.invalid.reason.1"
1045
: "This.jar.contains.entries.whose.tsa.certificate.chain.is.invalid.reason.1"),
1046
tsaChainNotValidatedReason.getLocalizedMessage()));
1047
}
1048
1049
// only in verifying
1050
if (notSignedByAlias) {
1051
errors.add(
1052
rb.getString("This.jar.contains.signed.entries.which.is.not.signed.by.the.specified.alias.es."));
1053
}
1054
1055
// only in verifying
1056
if (aliasNotInStore) {
1057
errors.add(rb.getString("This.jar.contains.signed.entries.that.s.not.signed.by.alias.in.this.keystore."));
1058
}
1059
1060
if (signerSelfSigned) {
1061
errors.add(rb.getString(isSigning
1062
? "The.signer.s.certificate.is.self.signed."
1063
: "This.jar.contains.entries.whose.signer.certificate.is.self.signed."));
1064
}
1065
1066
if (isSigning) {
1067
if ((legacyAlg & 1) == 1) {
1068
warnings.add(String.format(
1069
rb.getString("The.1.algorithm.specified.for.the.2.option.is.considered.a.security.risk..This.algorithm.will.be.disabled.in.a.future.update."),
1070
digestalg, "-digestalg"));
1071
}
1072
1073
if ((disabledAlg & 1) == 1) {
1074
errors.add(String.format(
1075
rb.getString("The.1.algorithm.specified.for.the.2.option.is.considered.a.security.risk.and.is.disabled."),
1076
digestalg, "-digestalg"));
1077
}
1078
1079
if ((legacyAlg & 2) == 2) {
1080
warnings.add(String.format(
1081
rb.getString("The.1.algorithm.specified.for.the.2.option.is.considered.a.security.risk..This.algorithm.will.be.disabled.in.a.future.update."),
1082
sigalg, "-sigalg"));
1083
}
1084
if ((disabledAlg & 2) == 2) {
1085
errors.add(String.format(
1086
rb.getString("The.1.algorithm.specified.for.the.2.option.is.considered.a.security.risk.and.is.disabled."),
1087
sigalg, "-sigalg"));
1088
}
1089
1090
if ((legacyAlg & 4) == 4) {
1091
warnings.add(String.format(
1092
rb.getString("The.1.algorithm.specified.for.the.2.option.is.considered.a.security.risk..This.algorithm.will.be.disabled.in.a.future.update."),
1093
tSADigestAlg, "-tsadigestalg"));
1094
}
1095
if ((disabledAlg & 4) == 4) {
1096
errors.add(String.format(
1097
rb.getString("The.1.algorithm.specified.for.the.2.option.is.considered.a.security.risk.and.is.disabled."),
1098
tSADigestAlg, "-tsadigestalg"));
1099
}
1100
1101
if ((legacyAlg & 8) == 8) {
1102
warnings.add(String.format(
1103
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."),
1104
privateKey.getAlgorithm(), KeyUtil.getKeySize(privateKey)));
1105
}
1106
if ((disabledAlg & 8) == 8) {
1107
errors.add(String.format(
1108
rb.getString("The.1.signing.key.has.a.keysize.of.2.which.is.considered.a.security.risk.and.is.disabled."),
1109
privateKey.getAlgorithm(), KeyUtil.getKeySize(privateKey)));
1110
}
1111
} else {
1112
if ((legacyAlg & 1) != 0) {
1113
warnings.add(String.format(
1114
rb.getString("The.digest.algorithm.1.is.considered.a.security.risk..This.algorithm.will.be.disabled.in.a.future.update."),
1115
legacyDigestAlg));
1116
}
1117
1118
if ((legacyAlg & 2) == 2) {
1119
warnings.add(String.format(
1120
rb.getString("The.signature.algorithm.1.is.considered.a.security.risk..This.algorithm.will.be.disabled.in.a.future.update."),
1121
legacySigAlg));
1122
}
1123
1124
if ((legacyAlg & 4) != 0) {
1125
warnings.add(String.format(
1126
rb.getString("The.digest.algorithm.1.is.considered.a.security.risk..This.algorithm.will.be.disabled.in.a.future.update."),
1127
legacyTsaDigestAlg));
1128
}
1129
1130
if ((legacyAlg & 8) == 8) {
1131
warnings.add(String.format(
1132
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."),
1133
weakPublicKey.getAlgorithm(), KeyUtil.getKeySize(weakPublicKey)));
1134
}
1135
}
1136
} else {
1137
result = rb.getString(isSigning ? "jar.signed." : "jar.verified.");
1138
}
1139
1140
if (hasExpiredTsaCert) {
1141
// No need to warn about expiring if already expired
1142
hasExpiringTsaCert = false;
1143
}
1144
1145
if (hasExpiringCert ||
1146
(hasExpiringTsaCert && expireDate != null) ||
1147
(noTimestamp && expireDate != null) ||
1148
(hasExpiredTsaCert && signerNotExpired)) {
1149
1150
if (hasExpiredTsaCert && signerNotExpired) {
1151
if (expireDate != null) {
1152
warnings.add(String.format(
1153
rb.getString("The.timestamp.expired.1.but.usable.2"),
1154
tsaExpireDate,
1155
expireDate));
1156
}
1157
// Reset the flag so exit code is 0
1158
hasExpiredTsaCert = false;
1159
}
1160
if (hasExpiringCert) {
1161
warnings.add(rb.getString(isSigning
1162
? "The.signer.certificate.will.expire.within.six.months."
1163
: "This.jar.contains.entries.whose.signer.certificate.will.expire.within.six.months."));
1164
}
1165
if (hasExpiringTsaCert && expireDate != null) {
1166
if (expireDate.after(tsaExpireDate)) {
1167
warnings.add(String.format(rb.getString(
1168
"The.timestamp.will.expire.within.one.year.on.1.but.2"), tsaExpireDate, expireDate));
1169
} else {
1170
warnings.add(String.format(rb.getString(
1171
"The.timestamp.will.expire.within.one.year.on.1"), tsaExpireDate));
1172
}
1173
}
1174
if (noTimestamp && expireDate != null) {
1175
if (hasTimestampBlock) {
1176
warnings.add(String.format(rb.getString(isSigning
1177
? "invalid.timestamp.signing"
1178
: "bad.timestamp.verifying"), expireDate));
1179
} else {
1180
warnings.add(String.format(rb.getString(isSigning
1181
? "no.timestamp.signing"
1182
: "no.timestamp.verifying"), expireDate));
1183
}
1184
}
1185
}
1186
1187
System.out.println(result);
1188
if (strict) {
1189
if (!errors.isEmpty()) {
1190
System.out.println();
1191
System.out.println(rb.getString("Error."));
1192
errors.forEach(System.out::println);
1193
}
1194
if (!warnings.isEmpty()) {
1195
System.out.println();
1196
System.out.println(rb.getString("Warning."));
1197
warnings.forEach(System.out::println);
1198
}
1199
} else {
1200
if (!errors.isEmpty() || !warnings.isEmpty()) {
1201
System.out.println();
1202
System.out.println(rb.getString("Warning."));
1203
errors.forEach(System.out::println);
1204
warnings.forEach(System.out::println);
1205
}
1206
}
1207
if (!isSigning && (!errors.isEmpty() || !warnings.isEmpty())) {
1208
if (! (verbose != null && showcerts)) {
1209
System.out.println();
1210
System.out.println(rb.getString(
1211
"Re.run.with.the.verbose.and.certs.options.for.more.details."));
1212
}
1213
}
1214
1215
if (isSigning || verbose != null) {
1216
// Always print out expireDate, unless expired or expiring.
1217
if (!hasExpiringCert && !hasExpiredCert
1218
&& expireDate != null && signerNotExpired) {
1219
info.add(String.format(rb.getString(
1220
"The.signer.certificate.will.expire.on.1."), expireDate));
1221
}
1222
if (!noTimestamp) {
1223
if (!hasExpiringTsaCert && !hasExpiredTsaCert && tsaExpireDate != null) {
1224
if (signerNotExpired) {
1225
info.add(String.format(rb.getString(
1226
"The.timestamp.will.expire.on.1."), tsaExpireDate));
1227
} else {
1228
info.add(String.format(rb.getString(
1229
"signer.cert.expired.1.but.timestamp.good.2."),
1230
expireDate,
1231
tsaExpireDate));
1232
}
1233
}
1234
}
1235
}
1236
1237
if (!info.isEmpty()) {
1238
System.out.println();
1239
info.forEach(System.out::println);
1240
}
1241
}
1242
1243
private String verifyWithWeak(String alg, Set<CryptoPrimitive> primitiveSet, boolean tsa) {
1244
if (DISABLED_CHECK.permits(primitiveSet, alg, null)) {
1245
if (LEGACY_CHECK.permits(primitiveSet, alg, null)) {
1246
return alg;
1247
} else {
1248
if (primitiveSet == SIG_PRIMITIVE_SET) {
1249
legacyAlg |= 2;
1250
legacySigAlg = alg;
1251
} else {
1252
if (tsa) {
1253
legacyAlg |= 4;
1254
legacyTsaDigestAlg = alg;
1255
} else {
1256
legacyAlg |= 1;
1257
legacyDigestAlg = alg;
1258
}
1259
}
1260
return String.format(rb.getString("with.weak"), alg);
1261
}
1262
} else {
1263
disabledAlgFound = true;
1264
return String.format(rb.getString("with.disabled"), alg);
1265
}
1266
}
1267
1268
private String verifyWithWeak(PublicKey key) {
1269
int kLen = KeyUtil.getKeySize(key);
1270
if (DISABLED_CHECK.permits(SIG_PRIMITIVE_SET, key)) {
1271
if (LEGACY_CHECK.permits(SIG_PRIMITIVE_SET, key)) {
1272
if (kLen >= 0) {
1273
return String.format(rb.getString("key.bit"), kLen);
1274
} else {
1275
return rb.getString("unknown.size");
1276
}
1277
} else {
1278
weakPublicKey = key;
1279
legacyAlg |= 8;
1280
return String.format(rb.getString("key.bit.weak"), kLen);
1281
}
1282
} else {
1283
disabledAlgFound = true;
1284
return String.format(rb.getString("key.bit.disabled"), kLen);
1285
}
1286
}
1287
1288
private void checkWeakSign(String alg, Set<CryptoPrimitive> primitiveSet, boolean tsa) {
1289
if (DISABLED_CHECK.permits(primitiveSet, alg, null)) {
1290
if (!LEGACY_CHECK.permits(primitiveSet, alg, null)) {
1291
if (primitiveSet == SIG_PRIMITIVE_SET) {
1292
legacyAlg |= 2;
1293
} else {
1294
if (tsa) {
1295
legacyAlg |= 4;
1296
} else {
1297
legacyAlg |= 1;
1298
}
1299
}
1300
}
1301
} else {
1302
if (primitiveSet == SIG_PRIMITIVE_SET) {
1303
disabledAlg |= 2;
1304
} else {
1305
if (tsa) {
1306
disabledAlg |= 4;
1307
} else {
1308
disabledAlg |= 1;
1309
}
1310
}
1311
}
1312
}
1313
1314
private void checkWeakSign(PrivateKey key) {
1315
if (DISABLED_CHECK.permits(SIG_PRIMITIVE_SET, key)) {
1316
if (!LEGACY_CHECK.permits(SIG_PRIMITIVE_SET, key)) {
1317
legacyAlg |= 8;
1318
}
1319
} else {
1320
disabledAlg |= 8;
1321
}
1322
}
1323
1324
private static MessageFormat validityTimeForm = null;
1325
private static MessageFormat notYetTimeForm = null;
1326
private static MessageFormat expiredTimeForm = null;
1327
private static MessageFormat expiringTimeForm = null;
1328
1329
/**
1330
* Returns a string about a certificate:
1331
*
1332
* [<tab>] <cert-type> [", " <subject-DN>] [" (" <keystore-entry-alias> ")"]
1333
* [<validity-period> | <expiry-warning>]
1334
* [<key-usage-warning>]
1335
*
1336
* Note: no newline character at the end.
1337
*
1338
* This method sets global flags like hasExpiringCert, hasExpiredCert,
1339
* notYetValidCert, badKeyUsage, badExtendedKeyUsage, badNetscapeCertType,
1340
* hasExpiringTsaCert, hasExpiredTsaCert.
1341
*
1342
* @param isTsCert true if c is in the TSA cert chain, false otherwise.
1343
* @param checkUsage true to check code signer keyUsage
1344
*/
1345
String printCert(boolean isTsCert, String tab, Certificate c,
1346
Date timestamp, boolean checkUsage) throws Exception {
1347
1348
StringBuilder certStr = new StringBuilder();
1349
String space = rb.getString("SPACE");
1350
X509Certificate x509Cert = null;
1351
1352
if (c instanceof X509Certificate) {
1353
x509Cert = (X509Certificate) c;
1354
certStr.append(tab).append(x509Cert.getType())
1355
.append(rb.getString("COMMA"))
1356
.append(x509Cert.getSubjectDN().getName());
1357
} else {
1358
certStr.append(tab).append(c.getType());
1359
}
1360
1361
String alias = storeHash.get(c);
1362
if (alias != null) {
1363
certStr.append(space).append(alias);
1364
}
1365
1366
if (x509Cert != null) {
1367
1368
certStr.append("\n").append(tab).append("[");
1369
1370
if (trustedCerts.contains(x509Cert)) {
1371
certStr.append(rb.getString("trusted.certificate"));
1372
} else {
1373
Date notAfter = x509Cert.getNotAfter();
1374
try {
1375
boolean printValidity = true;
1376
if (isTsCert) {
1377
if (tsaExpireDate == null || tsaExpireDate.after(notAfter)) {
1378
tsaExpireDate = notAfter;
1379
}
1380
} else {
1381
if (expireDate == null || expireDate.after(notAfter)) {
1382
expireDate = notAfter;
1383
}
1384
}
1385
if (timestamp == null) {
1386
x509Cert.checkValidity();
1387
// test if cert will expire within six months (or one year for tsa)
1388
long age = isTsCert ? ONE_YEAR : SIX_MONTHS;
1389
if (notAfter.getTime() < System.currentTimeMillis() + age) {
1390
if (isTsCert) {
1391
hasExpiringTsaCert = true;
1392
} else {
1393
hasExpiringCert = true;
1394
}
1395
if (expiringTimeForm == null) {
1396
expiringTimeForm = new MessageFormat(
1397
rb.getString("certificate.will.expire.on"));
1398
}
1399
Object[] source = {notAfter};
1400
certStr.append(expiringTimeForm.format(source));
1401
printValidity = false;
1402
}
1403
} else {
1404
x509Cert.checkValidity(timestamp);
1405
}
1406
if (printValidity) {
1407
if (validityTimeForm == null) {
1408
validityTimeForm = new MessageFormat(
1409
rb.getString("certificate.is.valid.from"));
1410
}
1411
Object[] source = {x509Cert.getNotBefore(), notAfter};
1412
certStr.append(validityTimeForm.format(source));
1413
}
1414
} catch (CertificateExpiredException cee) {
1415
if (isTsCert) {
1416
hasExpiredTsaCert = true;
1417
} else {
1418
hasExpiredCert = true;
1419
}
1420
1421
if (expiredTimeForm == null) {
1422
expiredTimeForm = new MessageFormat(
1423
rb.getString("certificate.expired.on"));
1424
}
1425
Object[] source = {notAfter};
1426
certStr.append(expiredTimeForm.format(source));
1427
1428
} catch (CertificateNotYetValidException cnyve) {
1429
if (!isTsCert) notYetValidCert = true;
1430
1431
if (notYetTimeForm == null) {
1432
notYetTimeForm = new MessageFormat(
1433
rb.getString("certificate.is.not.valid.until"));
1434
}
1435
Object[] source = {x509Cert.getNotBefore()};
1436
certStr.append(notYetTimeForm.format(source));
1437
}
1438
}
1439
certStr.append("]");
1440
1441
if (checkUsage) {
1442
boolean[] bad = new boolean[3];
1443
checkCertUsage(x509Cert, bad);
1444
if (bad[0] || bad[1] || bad[2]) {
1445
String x = "";
1446
if (bad[0]) {
1447
x ="KeyUsage";
1448
}
1449
if (bad[1]) {
1450
if (x.length() > 0) x = x + ", ";
1451
x = x + "ExtendedKeyUsage";
1452
}
1453
if (bad[2]) {
1454
if (x.length() > 0) x = x + ", ";
1455
x = x + "NetscapeCertType";
1456
}
1457
certStr.append("\n").append(tab)
1458
.append(MessageFormat.format(rb.getString(
1459
".{0}.extension.does.not.support.code.signing."), x));
1460
}
1461
}
1462
}
1463
return certStr.toString();
1464
}
1465
1466
private static MessageFormat signTimeForm = null;
1467
1468
private String printTimestamp(String tab, Timestamp timestamp) {
1469
1470
if (signTimeForm == null) {
1471
signTimeForm =
1472
new MessageFormat(rb.getString("entry.was.signed.on"));
1473
}
1474
Object[] source = { timestamp.getTimestamp() };
1475
1476
return new StringBuilder().append(tab).append("[")
1477
.append(signTimeForm.format(source)).append("]").toString();
1478
}
1479
1480
private Map<CodeSigner,Integer> cacheForInKS = new IdentityHashMap<>();
1481
1482
private int inKeyStoreForOneSigner(CodeSigner signer) {
1483
if (cacheForInKS.containsKey(signer)) {
1484
return cacheForInKS.get(signer);
1485
}
1486
1487
boolean found = false;
1488
int result = 0;
1489
List<? extends Certificate> certs = signer.getSignerCertPath().getCertificates();
1490
for (Certificate c : certs) {
1491
String alias = storeHash.get(c);
1492
if (alias != null) {
1493
if (alias.startsWith("(")) {
1494
result |= IN_KEYSTORE;
1495
} else if (alias.startsWith("[")) {
1496
result |= IN_SCOPE;
1497
}
1498
if (ckaliases.contains(alias.substring(1, alias.length() - 1))) {
1499
result |= SIGNED_BY_ALIAS;
1500
}
1501
} else {
1502
if (store != null) {
1503
try {
1504
alias = store.getCertificateAlias(c);
1505
} catch (KeyStoreException kse) {
1506
// never happens, because keystore has been loaded
1507
}
1508
if (alias != null) {
1509
storeHash.put(c, "(" + alias + ")");
1510
found = true;
1511
result |= IN_KEYSTORE;
1512
}
1513
}
1514
if (ckaliases.contains(alias)) {
1515
result |= SIGNED_BY_ALIAS;
1516
}
1517
}
1518
}
1519
cacheForInKS.put(signer, result);
1520
return result;
1521
}
1522
1523
Hashtable<Certificate, String> storeHash = new Hashtable<>();
1524
1525
int inKeyStore(CodeSigner[] signers) {
1526
1527
if (signers == null)
1528
return 0;
1529
1530
int output = 0;
1531
1532
for (CodeSigner signer: signers) {
1533
int result = inKeyStoreForOneSigner(signer);
1534
output |= result;
1535
}
1536
if (ckaliases.size() > 0 && (output & SIGNED_BY_ALIAS) == 0) {
1537
output |= NOT_ALIAS;
1538
}
1539
return output;
1540
}
1541
1542
void signJar(String jarName, String alias, String[] args)
1543
throws Exception {
1544
1545
checkWeakSign(digestalg, DIGEST_PRIMITIVE_SET, false);
1546
checkWeakSign(tSADigestAlg, DIGEST_PRIMITIVE_SET, true);
1547
/*
1548
* If no signature algorithm was specified, we choose a
1549
* default that is compatible with the private key algorithm.
1550
*/
1551
if (sigalg == null) {
1552
sigalg = getDefaultSignatureAlgorithm(privateKey);
1553
}
1554
checkWeakSign(sigalg, SIG_PRIMITIVE_SET, false);
1555
1556
checkWeakSign(privateKey);
1557
1558
boolean aliasUsed = false;
1559
X509Certificate tsaCert = null;
1560
1561
if (sigfile == null) {
1562
sigfile = alias;
1563
aliasUsed = true;
1564
}
1565
1566
if (sigfile.length() > 8) {
1567
sigfile = sigfile.substring(0, 8).toUpperCase(Locale.ENGLISH);
1568
} else {
1569
sigfile = sigfile.toUpperCase(Locale.ENGLISH);
1570
}
1571
1572
StringBuilder tmpSigFile = new StringBuilder(sigfile.length());
1573
for (int j = 0; j < sigfile.length(); j++) {
1574
char c = sigfile.charAt(j);
1575
if (!
1576
((c>= 'A' && c<= 'Z') ||
1577
(c>= '0' && c<= '9') ||
1578
(c == '-') ||
1579
(c == '_'))) {
1580
if (aliasUsed) {
1581
// convert illegal characters from the alias to be _'s
1582
c = '_';
1583
} else {
1584
throw new
1585
RuntimeException(rb.getString
1586
("signature.filename.must.consist.of.the.following.characters.A.Z.0.9.or."));
1587
}
1588
}
1589
tmpSigFile.append(c);
1590
}
1591
1592
sigfile = tmpSigFile.toString();
1593
1594
String tmpJarName;
1595
if (signedjar == null) tmpJarName = jarName+".sig";
1596
else tmpJarName = signedjar;
1597
1598
File jarFile = new File(jarName);
1599
File signedJarFile = new File(tmpJarName);
1600
1601
// Open the jar (zip) file
1602
try {
1603
zipFile = new ZipFile(jarName);
1604
} catch (IOException ioe) {
1605
error(rb.getString("unable.to.open.jar.file.")+jarName, ioe);
1606
}
1607
1608
FileOutputStream fos = null;
1609
try {
1610
fos = new FileOutputStream(signedJarFile);
1611
} catch (IOException ioe) {
1612
error(rb.getString("unable.to.create.")+tmpJarName, ioe);
1613
}
1614
1615
PrintStream ps = new PrintStream(fos);
1616
ZipOutputStream zos = new ZipOutputStream(ps);
1617
1618
/* First guess at what they might be - we don't xclude RSA ones. */
1619
String sfFilename = (META_INF + sigfile + ".SF").toUpperCase(Locale.ENGLISH);
1620
String bkFilename = (META_INF + sigfile + ".DSA").toUpperCase(Locale.ENGLISH);
1621
1622
Manifest manifest = new Manifest();
1623
Map<String,Attributes> mfEntries = manifest.getEntries();
1624
1625
// The Attributes of manifest before updating
1626
Attributes oldAttr = null;
1627
1628
boolean mfModified = false;
1629
boolean mfCreated = false;
1630
byte[] mfRawBytes = null;
1631
1632
try {
1633
MessageDigest digests[] = { MessageDigest.getInstance(digestalg) };
1634
1635
// Check if manifest exists
1636
ZipEntry mfFile;
1637
if ((mfFile = getManifestFile(zipFile)) != null) {
1638
// Manifest exists. Read its raw bytes.
1639
mfRawBytes = getBytes(zipFile, mfFile);
1640
manifest.read(new ByteArrayInputStream(mfRawBytes));
1641
oldAttr = (Attributes)(manifest.getMainAttributes().clone());
1642
} else {
1643
// Create new manifest
1644
Attributes mattr = manifest.getMainAttributes();
1645
mattr.putValue(Attributes.Name.MANIFEST_VERSION.toString(),
1646
"1.0");
1647
String javaVendor = System.getProperty("java.vendor");
1648
String jdkVersion = System.getProperty("java.version");
1649
mattr.putValue("Created-By", jdkVersion + " (" +javaVendor
1650
+ ")");
1651
mfFile = new ZipEntry(JarFile.MANIFEST_NAME);
1652
mfCreated = true;
1653
}
1654
1655
/*
1656
* For each entry in jar
1657
* (except for signature-related META-INF entries),
1658
* do the following:
1659
*
1660
* - if entry is not contained in manifest, add it to manifest;
1661
* - if entry is contained in manifest, calculate its hash and
1662
* compare it with the one in the manifest; if they are
1663
* different, replace the hash in the manifest with the newly
1664
* generated one. (This may invalidate existing signatures!)
1665
*/
1666
Vector<ZipEntry> mfFiles = new Vector<>();
1667
1668
boolean wasSigned = false;
1669
1670
for (Enumeration<? extends ZipEntry> enum_=zipFile.entries();
1671
enum_.hasMoreElements();) {
1672
ZipEntry ze = enum_.nextElement();
1673
1674
if (ze.getName().startsWith(META_INF)) {
1675
// Store META-INF files in vector, so they can be written
1676
// out first
1677
mfFiles.addElement(ze);
1678
1679
if (SignatureFileVerifier.isBlockOrSF(
1680
ze.getName().toUpperCase(Locale.ENGLISH))) {
1681
wasSigned = true;
1682
}
1683
1684
if (signatureRelated(ze.getName())) {
1685
// ignore signature-related and manifest files
1686
continue;
1687
}
1688
}
1689
1690
if (manifest.getAttributes(ze.getName()) != null) {
1691
// jar entry is contained in manifest, check and
1692
// possibly update its digest attributes
1693
if (updateDigests(ze, zipFile, digests,
1694
manifest) == true) {
1695
mfModified = true;
1696
}
1697
} else if (!ze.isDirectory()) {
1698
// Add entry to manifest
1699
Attributes attrs = getDigestAttributes(ze, zipFile,
1700
digests);
1701
mfEntries.put(ze.getName(), attrs);
1702
mfModified = true;
1703
}
1704
}
1705
1706
// Recalculate the manifest raw bytes if necessary
1707
if (mfModified) {
1708
ByteArrayOutputStream baos = new ByteArrayOutputStream();
1709
manifest.write(baos);
1710
if (wasSigned) {
1711
byte[] newBytes = baos.toByteArray();
1712
if (mfRawBytes != null
1713
&& oldAttr.equals(manifest.getMainAttributes())) {
1714
1715
/*
1716
* Note:
1717
*
1718
* The Attributes object is based on HashMap and can handle
1719
* continuation columns. Therefore, even if the contents are
1720
* not changed (in a Map view), the bytes that it write()
1721
* may be different from the original bytes that it read()
1722
* from. Since the signature on the main attributes is based
1723
* on raw bytes, we must retain the exact bytes.
1724
*/
1725
1726
int newPos = findHeaderEnd(newBytes);
1727
int oldPos = findHeaderEnd(mfRawBytes);
1728
1729
if (newPos == oldPos) {
1730
System.arraycopy(mfRawBytes, 0, newBytes, 0, oldPos);
1731
} else {
1732
// cat oldHead newTail > newBytes
1733
byte[] lastBytes = new byte[oldPos +
1734
newBytes.length - newPos];
1735
System.arraycopy(mfRawBytes, 0, lastBytes, 0, oldPos);
1736
System.arraycopy(newBytes, newPos, lastBytes, oldPos,
1737
newBytes.length - newPos);
1738
newBytes = lastBytes;
1739
}
1740
}
1741
mfRawBytes = newBytes;
1742
} else {
1743
mfRawBytes = baos.toByteArray();
1744
}
1745
}
1746
1747
// Write out the manifest
1748
if (mfModified) {
1749
// manifest file has new length
1750
mfFile = new ZipEntry(JarFile.MANIFEST_NAME);
1751
}
1752
if (verbose != null) {
1753
if (mfCreated) {
1754
System.out.println(rb.getString(".adding.") +
1755
mfFile.getName());
1756
} else if (mfModified) {
1757
System.out.println(rb.getString(".updating.") +
1758
mfFile.getName());
1759
}
1760
}
1761
zos.putNextEntry(mfFile);
1762
zos.write(mfRawBytes);
1763
1764
// Calculate SignatureFile (".SF") and SignatureBlockFile
1765
ManifestDigester manDig = new ManifestDigester(mfRawBytes);
1766
SignatureFile sf = new SignatureFile(digests, manifest, manDig,
1767
sigfile, signManifest);
1768
1769
if (tsaAlias != null) {
1770
tsaCert = getTsaCert(tsaAlias);
1771
}
1772
1773
if (tsaUrl == null && tsaCert == null) {
1774
noTimestamp = true;
1775
}
1776
1777
SignatureFile.Block block = null;
1778
1779
try {
1780
block =
1781
sf.generateBlock(privateKey, sigalg, certChain,
1782
externalSF, tsaUrl, tsaCert, tSAPolicyID, tSADigestAlg,
1783
signingMechanism, args, zipFile);
1784
} catch (SocketTimeoutException e) {
1785
// Provide a helpful message when TSA is beyond a firewall
1786
error(rb.getString("unable.to.sign.jar.") +
1787
rb.getString("no.response.from.the.Timestamping.Authority.") +
1788
"\n -J-Dhttp.proxyHost=<hostname>" +
1789
"\n -J-Dhttp.proxyPort=<portnumber>\n" +
1790
rb.getString("or") +
1791
"\n -J-Dhttps.proxyHost=<hostname> " +
1792
"\n -J-Dhttps.proxyPort=<portnumber> ", e);
1793
}
1794
1795
sfFilename = sf.getMetaName();
1796
bkFilename = block.getMetaName();
1797
1798
ZipEntry sfFile = new ZipEntry(sfFilename);
1799
ZipEntry bkFile = new ZipEntry(bkFilename);
1800
1801
long time = System.currentTimeMillis();
1802
sfFile.setTime(time);
1803
bkFile.setTime(time);
1804
1805
// signature file
1806
zos.putNextEntry(sfFile);
1807
sf.write(zos);
1808
if (verbose != null) {
1809
if (zipFile.getEntry(sfFilename) != null) {
1810
System.out.println(rb.getString(".updating.") +
1811
sfFilename);
1812
} else {
1813
System.out.println(rb.getString(".adding.") +
1814
sfFilename);
1815
}
1816
}
1817
1818
if (verbose != null) {
1819
if (tsaUrl != null || tsaCert != null) {
1820
System.out.println(
1821
rb.getString("requesting.a.signature.timestamp"));
1822
}
1823
if (tsaUrl != null) {
1824
System.out.println(rb.getString("TSA.location.") + tsaUrl);
1825
}
1826
if (tsaCert != null) {
1827
URI tsaURI = TimestampedSigner.getTimestampingURI(tsaCert);
1828
if (tsaURI != null) {
1829
System.out.println(rb.getString("TSA.location.") +
1830
tsaURI);
1831
}
1832
System.out.println(rb.getString("TSA.certificate.") +
1833
printCert(true, "", tsaCert, null, false));
1834
}
1835
if (signingMechanism != null) {
1836
System.out.println(
1837
rb.getString("using.an.alternative.signing.mechanism"));
1838
}
1839
}
1840
1841
// signature block file
1842
zos.putNextEntry(bkFile);
1843
block.write(zos);
1844
if (verbose != null) {
1845
if (zipFile.getEntry(bkFilename) != null) {
1846
System.out.println(rb.getString(".updating.") +
1847
bkFilename);
1848
} else {
1849
System.out.println(rb.getString(".adding.") +
1850
bkFilename);
1851
}
1852
}
1853
1854
// Write out all other META-INF files that we stored in the
1855
// vector
1856
for (int i=0; i<mfFiles.size(); i++) {
1857
ZipEntry ze = mfFiles.elementAt(i);
1858
if (!ze.getName().equalsIgnoreCase(JarFile.MANIFEST_NAME)
1859
&& !ze.getName().equalsIgnoreCase(sfFilename)
1860
&& !ze.getName().equalsIgnoreCase(bkFilename)) {
1861
writeEntry(zipFile, zos, ze);
1862
}
1863
}
1864
1865
// Write out all other files
1866
for (Enumeration<? extends ZipEntry> enum_=zipFile.entries();
1867
enum_.hasMoreElements();) {
1868
ZipEntry ze = enum_.nextElement();
1869
1870
if (!ze.getName().startsWith(META_INF)) {
1871
if (verbose != null) {
1872
if (manifest.getAttributes(ze.getName()) != null)
1873
System.out.println(rb.getString(".signing.") +
1874
ze.getName());
1875
else
1876
System.out.println(rb.getString(".adding.") +
1877
ze.getName());
1878
}
1879
writeEntry(zipFile, zos, ze);
1880
}
1881
}
1882
} catch(IOException ioe) {
1883
error(rb.getString("unable.to.sign.jar.")+ioe, ioe);
1884
} finally {
1885
// close the resouces
1886
if (zipFile != null) {
1887
zipFile.close();
1888
zipFile = null;
1889
}
1890
1891
if (zos != null) {
1892
zos.close();
1893
}
1894
}
1895
1896
// The JarSigner API always accepts the timestamp received.
1897
// We need to extract the certs from the signed jar to
1898
// validate it.
1899
try (JarFile check = new JarFile(signedJarFile)) {
1900
PKCS7 p7 = new PKCS7(check.getInputStream(check.getEntry(
1901
"META-INF/" + sigfile + "." + privateKey.getAlgorithm())));
1902
Timestamp ts = null;
1903
try {
1904
SignerInfo si = p7.getSignerInfos()[0];
1905
if (si.getTsToken() != null) {
1906
hasTimestampBlock = true;
1907
}
1908
ts = si.getTimestamp();
1909
} catch (Exception e) {
1910
tsaChainNotValidated = true;
1911
tsaChainNotValidatedReason = e;
1912
}
1913
// Spaces before the ">>> Signer" and other lines are different
1914
String result = certsAndTSInfo("", " ", Arrays.asList(certChain), ts);
1915
if (verbose != null) {
1916
System.out.println(result);
1917
}
1918
} catch (Exception e) {
1919
if (debug) {
1920
e.printStackTrace();
1921
}
1922
}
1923
1924
if (signedjar == null) {
1925
// attempt an atomic rename. If that fails,
1926
// rename the original jar file, then the signed
1927
// one, then delete the original.
1928
if (!signedJarFile.renameTo(jarFile)) {
1929
File origJar = new File(jarName+".orig");
1930
1931
if (jarFile.renameTo(origJar)) {
1932
if (signedJarFile.renameTo(jarFile)) {
1933
origJar.delete();
1934
} else {
1935
MessageFormat form = new MessageFormat(rb.getString
1936
("attempt.to.rename.signedJarFile.to.jarFile.failed"));
1937
Object[] source = {signedJarFile, jarFile};
1938
error(form.format(source));
1939
}
1940
} else {
1941
MessageFormat form = new MessageFormat(rb.getString
1942
("attempt.to.rename.jarFile.to.origJar.failed"));
1943
Object[] source = {jarFile, origJar};
1944
error(form.format(source));
1945
}
1946
}
1947
}
1948
displayMessagesAndResult(true);
1949
}
1950
1951
private static String getDefaultSignatureAlgorithm(PrivateKey privateKey) {
1952
String keyAlgorithm = privateKey.getAlgorithm();
1953
if (keyAlgorithm.equalsIgnoreCase("DSA"))
1954
return "SHA256withDSA";
1955
else if (keyAlgorithm.equalsIgnoreCase("RSA"))
1956
return "SHA256withRSA";
1957
else if (keyAlgorithm.equalsIgnoreCase("EC"))
1958
return "SHA256withECDSA";
1959
throw new RuntimeException("private key is not a DSA or "
1960
+ "RSA key");
1961
}
1962
1963
1964
/**
1965
* Find the length of header inside bs. The header is a multiple (>=0)
1966
* lines of attributes plus an empty line. The empty line is included
1967
* in the header.
1968
*/
1969
@SuppressWarnings("fallthrough")
1970
private int findHeaderEnd(byte[] bs) {
1971
// Initial state true to deal with empty header
1972
boolean newline = true; // just met a newline
1973
int len = bs.length;
1974
for (int i=0; i<len; i++) {
1975
switch (bs[i]) {
1976
case '\r':
1977
if (i < len - 1 && bs[i+1] == '\n') i++;
1978
// fallthrough
1979
case '\n':
1980
if (newline) return i+1; //+1 to get length
1981
newline = true;
1982
break;
1983
default:
1984
newline = false;
1985
}
1986
}
1987
// If header end is not found, it means the MANIFEST.MF has only
1988
// the main attributes section and it does not end with 2 newlines.
1989
// Returns the whole length so that it can be completely replaced.
1990
return len;
1991
}
1992
1993
/**
1994
* signature-related files include:
1995
* . META-INF/MANIFEST.MF
1996
* . META-INF/SIG-*
1997
* . META-INF/*.SF
1998
* . META-INF/*.DSA
1999
* . META-INF/*.RSA
2000
* . META-INF/*.EC
2001
*/
2002
private boolean signatureRelated(String name) {
2003
return SignatureFileVerifier.isSigningRelated(name);
2004
}
2005
2006
Map<CodeSigner,String> cacheForSignerInfo = new IdentityHashMap<>();
2007
2008
/**
2009
* Returns a string of signer info, with a newline at the end.
2010
* Called by verifyJar().
2011
*/
2012
private String signerInfo(CodeSigner signer, String tab) throws Exception {
2013
if (cacheForSignerInfo.containsKey(signer)) {
2014
return cacheForSignerInfo.get(signer);
2015
}
2016
List<? extends Certificate> certs = signer.getSignerCertPath().getCertificates();
2017
// signing time is only displayed on verification
2018
Timestamp ts = signer.getTimestamp();
2019
String tsLine = "";
2020
if (ts != null) {
2021
tsLine = printTimestamp(tab, ts) + "\n";
2022
}
2023
// Spaces before the ">>> Signer" and other lines are the same.
2024
2025
String result = certsAndTSInfo(tab, tab, certs, ts);
2026
cacheForSignerInfo.put(signer, tsLine + result);
2027
return result;
2028
}
2029
2030
/**
2031
* Fills info on certs and timestamp into a StringBuilder, sets
2032
* warning flags (through printCert) and validates cert chains.
2033
*
2034
* @param tab1 spaces before the ">>> Signer" line
2035
* @param tab2 spaces before the other lines
2036
* @param certs the signer cert
2037
* @param ts the timestamp, can be null
2038
* @return the info as a string
2039
*/
2040
private String certsAndTSInfo(
2041
String tab1,
2042
String tab2,
2043
List<? extends Certificate> certs, Timestamp ts)
2044
throws Exception {
2045
2046
Date timestamp;
2047
if (ts != null) {
2048
timestamp = ts.getTimestamp();
2049
noTimestamp = false;
2050
} else {
2051
timestamp = null;
2052
}
2053
// display the certificate(s). The first one is end-entity cert and
2054
// its KeyUsage should be checked.
2055
boolean first = true;
2056
StringBuilder sb = new StringBuilder();
2057
sb.append(tab1).append(rb.getString("...Signer")).append('\n');
2058
for (Certificate c : certs) {
2059
sb.append(printCert(false, tab2, c, timestamp, first));
2060
sb.append('\n');
2061
first = false;
2062
}
2063
try {
2064
validateCertChain(Validator.VAR_CODE_SIGNING, certs, ts);
2065
} catch (Exception e) {
2066
chainNotValidated = true;
2067
chainNotValidatedReason = e;
2068
sb.append(tab2).append(rb.getString(".Invalid.certificate.chain."))
2069
.append(e.getLocalizedMessage()).append("]\n");
2070
}
2071
if (ts != null) {
2072
sb.append(tab1).append(rb.getString("...TSA")).append('\n');
2073
for (Certificate c : ts.getSignerCertPath().getCertificates()) {
2074
sb.append(printCert(true, tab2, c, null, false));
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 void writeEntry(ZipFile zf, ZipOutputStream os, ZipEntry ze)
2096
throws IOException
2097
{
2098
ZipEntry ze2 = new ZipEntry(ze.getName());
2099
ze2.setMethod(ze.getMethod());
2100
ze2.setTime(ze.getTime());
2101
ze2.setComment(ze.getComment());
2102
ze2.setExtra(ze.getExtra());
2103
if (ze.getMethod() == ZipEntry.STORED) {
2104
ze2.setSize(ze.getSize());
2105
ze2.setCrc(ze.getCrc());
2106
}
2107
os.putNextEntry(ze2);
2108
writeBytes(zf, ze, os);
2109
}
2110
2111
/**
2112
* Writes all the bytes for a given entry to the specified output stream.
2113
*/
2114
private synchronized void writeBytes
2115
(ZipFile zf, ZipEntry ze, ZipOutputStream os) throws IOException {
2116
int n;
2117
2118
InputStream is = null;
2119
try {
2120
is = zf.getInputStream(ze);
2121
long left = ze.getSize();
2122
2123
while((left > 0) && (n = is.read(buffer, 0, buffer.length)) != -1) {
2124
os.write(buffer, 0, n);
2125
left -= n;
2126
}
2127
} finally {
2128
if (is != null) {
2129
is.close();
2130
}
2131
}
2132
}
2133
2134
void loadKeyStore(String keyStoreName, boolean prompt) {
2135
2136
if (!nullStream && keyStoreName == null) {
2137
keyStoreName = System.getProperty("user.home") + File.separator
2138
+ ".keystore";
2139
}
2140
2141
try {
2142
try {
2143
KeyStore caks = KeyStoreUtil.getCacertsKeyStore();
2144
if (caks != null) {
2145
Enumeration<String> aliases = caks.aliases();
2146
while (aliases.hasMoreElements()) {
2147
String a = aliases.nextElement();
2148
try {
2149
trustedCerts.add((X509Certificate)caks.getCertificate(a));
2150
} catch (Exception e2) {
2151
// ignore, when a SecretkeyEntry does not include a cert
2152
}
2153
}
2154
}
2155
} catch (Exception e) {
2156
// Ignore, if cacerts cannot be loaded
2157
}
2158
2159
if (providerName == null) {
2160
store = KeyStore.getInstance(storetype);
2161
} else {
2162
store = KeyStore.getInstance(storetype, providerName);
2163
}
2164
2165
// Get pass phrase
2166
// XXX need to disable echo; on UNIX, call getpass(char *prompt)Z
2167
// and on NT call ??
2168
if (token && storepass == null && !protectedPath
2169
&& !KeyStoreUtil.isWindowsKeyStore(storetype)) {
2170
storepass = getPass
2171
(rb.getString("Enter.Passphrase.for.keystore."));
2172
} else if (!token && storepass == null && prompt) {
2173
storepass = getPass
2174
(rb.getString("Enter.Passphrase.for.keystore."));
2175
}
2176
2177
try {
2178
if (nullStream) {
2179
store.load(null, storepass);
2180
} else {
2181
keyStoreName = keyStoreName.replace(File.separatorChar, '/');
2182
URL url = null;
2183
try {
2184
url = new URL(keyStoreName);
2185
} catch (java.net.MalformedURLException e) {
2186
// try as file
2187
url = new File(keyStoreName).toURI().toURL();
2188
}
2189
InputStream is = null;
2190
try {
2191
is = url.openStream();
2192
store.load(is, storepass);
2193
} finally {
2194
if (is != null) {
2195
is.close();
2196
}
2197
}
2198
}
2199
Enumeration<String> aliases = store.aliases();
2200
while (aliases.hasMoreElements()) {
2201
String a = aliases.nextElement();
2202
try {
2203
X509Certificate c = (X509Certificate)store.getCertificate(a);
2204
// Only add TrustedCertificateEntry and self-signed
2205
// PrivateKeyEntry
2206
if (store.isCertificateEntry(a) ||
2207
c.getSubjectDN().equals(c.getIssuerDN())) {
2208
trustedCerts.add(c);
2209
}
2210
} catch (Exception e2) {
2211
// ignore, when a SecretkeyEntry does not include a cert
2212
}
2213
}
2214
} finally {
2215
try {
2216
pkixParameters = new PKIXBuilderParameters(
2217
trustedCerts.stream()
2218
.map(c -> new TrustAnchor(c, null))
2219
.collect(Collectors.toSet()),
2220
null);
2221
pkixParameters.setRevocationEnabled(false);
2222
} catch (InvalidAlgorithmParameterException ex) {
2223
// Only if tas is empty
2224
}
2225
}
2226
} catch (IOException ioe) {
2227
throw new RuntimeException(rb.getString("keystore.load.") +
2228
ioe.getMessage());
2229
} catch (java.security.cert.CertificateException ce) {
2230
throw new RuntimeException(rb.getString("certificate.exception.") +
2231
ce.getMessage());
2232
} catch (NoSuchProviderException pe) {
2233
throw new RuntimeException(rb.getString("keystore.load.") +
2234
pe.getMessage());
2235
} catch (NoSuchAlgorithmException nsae) {
2236
throw new RuntimeException(rb.getString("keystore.load.") +
2237
nsae.getMessage());
2238
} catch (KeyStoreException kse) {
2239
throw new RuntimeException
2240
(rb.getString("unable.to.instantiate.keystore.class.") +
2241
kse.getMessage());
2242
}
2243
}
2244
2245
X509Certificate getTsaCert(String alias) {
2246
2247
java.security.cert.Certificate cs = null;
2248
2249
try {
2250
cs = store.getCertificate(alias);
2251
} catch (KeyStoreException kse) {
2252
// this never happens, because keystore has been loaded
2253
}
2254
if (cs == null || (!(cs instanceof X509Certificate))) {
2255
MessageFormat form = new MessageFormat(rb.getString
2256
("Certificate.not.found.for.alias.alias.must.reference.a.valid.KeyStore.entry.containing.an.X.509.public.key.certificate.for.the"));
2257
Object[] source = {alias, alias};
2258
error(form.format(source));
2259
}
2260
return (X509Certificate) cs;
2261
}
2262
2263
/**
2264
* Check if userCert is designed to be a code signer
2265
* @param userCert the certificate to be examined
2266
* @param bad 3 booleans to show if the KeyUsage, ExtendedKeyUsage,
2267
* NetscapeCertType has codeSigning flag turned on.
2268
* If null, the class field badKeyUsage, badExtendedKeyUsage,
2269
* badNetscapeCertType will be set.
2270
*/
2271
void checkCertUsage(X509Certificate userCert, boolean[] bad) {
2272
2273
// Can act as a signer?
2274
// 1. if KeyUsage, then [0:digitalSignature] or
2275
// [1:nonRepudiation] should be true
2276
// 2. if ExtendedKeyUsage, then should contains ANY or CODE_SIGNING
2277
// 3. if NetscapeCertType, then should contains OBJECT_SIGNING
2278
// 1,2,3 must be true
2279
2280
if (bad != null) {
2281
bad[0] = bad[1] = bad[2] = false;
2282
}
2283
2284
boolean[] keyUsage = userCert.getKeyUsage();
2285
if (keyUsage != null) {
2286
keyUsage = Arrays.copyOf(keyUsage, 9);
2287
if (!keyUsage[0] && !keyUsage[1]) {
2288
if (bad != null) {
2289
bad[0] = true;
2290
badKeyUsage = true;
2291
}
2292
}
2293
}
2294
2295
try {
2296
List<String> xKeyUsage = userCert.getExtendedKeyUsage();
2297
if (xKeyUsage != null) {
2298
if (!xKeyUsage.contains("2.5.29.37.0") // anyExtendedKeyUsage
2299
&& !xKeyUsage.contains("1.3.6.1.5.5.7.3.3")) { // codeSigning
2300
if (bad != null) {
2301
bad[1] = true;
2302
badExtendedKeyUsage = true;
2303
}
2304
}
2305
}
2306
} catch (java.security.cert.CertificateParsingException e) {
2307
// shouldn't happen
2308
}
2309
2310
try {
2311
// OID_NETSCAPE_CERT_TYPE
2312
byte[] netscapeEx = userCert.getExtensionValue
2313
("2.16.840.1.113730.1.1");
2314
if (netscapeEx != null) {
2315
DerInputStream in = new DerInputStream(netscapeEx);
2316
byte[] encoded = in.getOctetString();
2317
encoded = new DerValue(encoded).getUnalignedBitString()
2318
.toByteArray();
2319
2320
NetscapeCertTypeExtension extn =
2321
new NetscapeCertTypeExtension(encoded);
2322
2323
Boolean val = extn.get(NetscapeCertTypeExtension.OBJECT_SIGNING);
2324
if (!val) {
2325
if (bad != null) {
2326
bad[2] = true;
2327
badNetscapeCertType = true;
2328
}
2329
}
2330
}
2331
} catch (IOException e) {
2332
//
2333
}
2334
}
2335
2336
// Called by signJar().
2337
void getAliasInfo(String alias) throws Exception {
2338
2339
Key key = null;
2340
2341
try {
2342
java.security.cert.Certificate[] cs = null;
2343
if (altCertChain != null) {
2344
try (FileInputStream fis = new FileInputStream(altCertChain)) {
2345
cs = CertificateFactory.getInstance("X.509").
2346
generateCertificates(fis).
2347
toArray(new Certificate[0]);
2348
} catch (FileNotFoundException ex) {
2349
error(rb.getString("File.specified.by.certchain.does.not.exist"));
2350
} catch (CertificateException | IOException ex) {
2351
error(rb.getString("Cannot.restore.certchain.from.file.specified"));
2352
}
2353
} else {
2354
try {
2355
cs = store.getCertificateChain(alias);
2356
} catch (KeyStoreException kse) {
2357
// this never happens, because keystore has been loaded
2358
}
2359
}
2360
if (cs == null || cs.length == 0) {
2361
if (altCertChain != null) {
2362
error(rb.getString
2363
("Certificate.chain.not.found.in.the.file.specified."));
2364
} else {
2365
MessageFormat form = new MessageFormat(rb.getString
2366
("Certificate.chain.not.found.for.alias.alias.must.reference.a.valid.KeyStore.key.entry.containing.a.private.key.and"));
2367
Object[] source = {alias, alias};
2368
error(form.format(source));
2369
}
2370
}
2371
2372
certChain = new X509Certificate[cs.length];
2373
for (int i=0; i<cs.length; i++) {
2374
if (!(cs[i] instanceof X509Certificate)) {
2375
error(rb.getString
2376
("found.non.X.509.certificate.in.signer.s.chain"));
2377
}
2378
certChain[i] = (X509Certificate)cs[i];
2379
}
2380
2381
try {
2382
if (!token && keypass == null)
2383
key = store.getKey(alias, storepass);
2384
else
2385
key = store.getKey(alias, keypass);
2386
} catch (UnrecoverableKeyException e) {
2387
if (token) {
2388
throw e;
2389
} else if (keypass == null) {
2390
// Did not work out, so prompt user for key password
2391
MessageFormat form = new MessageFormat(rb.getString
2392
("Enter.key.password.for.alias."));
2393
Object[] source = {alias};
2394
keypass = getPass(form.format(source));
2395
key = store.getKey(alias, keypass);
2396
}
2397
}
2398
} catch (NoSuchAlgorithmException e) {
2399
error(e.getMessage());
2400
} catch (UnrecoverableKeyException e) {
2401
error(rb.getString("unable.to.recover.key.from.keystore"));
2402
} catch (KeyStoreException kse) {
2403
// this never happens, because keystore has been loaded
2404
}
2405
2406
if (!(key instanceof PrivateKey)) {
2407
MessageFormat form = new MessageFormat(rb.getString
2408
("key.associated.with.alias.not.a.private.key"));
2409
Object[] source = {alias};
2410
error(form.format(source));
2411
} else {
2412
privateKey = (PrivateKey)key;
2413
}
2414
}
2415
2416
void error(String message)
2417
{
2418
System.out.println(rb.getString("jarsigner.")+message);
2419
System.exit(1);
2420
}
2421
2422
2423
void error(String message, Exception e)
2424
{
2425
System.out.println(rb.getString("jarsigner.")+message);
2426
if (debug) {
2427
e.printStackTrace();
2428
}
2429
System.exit(1);
2430
}
2431
2432
/**
2433
* Validates a cert chain.
2434
*
2435
* @param parameter this might be a timestamp
2436
*/
2437
void validateCertChain(String variant, List<? extends Certificate> certs,
2438
Timestamp parameter)
2439
throws Exception {
2440
try {
2441
Validator.getInstance(Validator.TYPE_PKIX,
2442
variant,
2443
pkixParameters)
2444
.validate(certs.toArray(new X509Certificate[certs.size()]),
2445
null, parameter);
2446
} catch (Exception e) {
2447
if (debug) {
2448
e.printStackTrace();
2449
}
2450
2451
// Exception might be dismissed if another warning flag
2452
// is already set by printCert.
2453
2454
if (variant.equals(Validator.VAR_TSA_SERVER) &&
2455
e instanceof ValidatorException) {
2456
// Throw cause if it's CertPathValidatorException,
2457
if (e.getCause() != null &&
2458
e.getCause() instanceof CertPathValidatorException) {
2459
e = (Exception) e.getCause();
2460
Throwable t = e.getCause();
2461
if ((t instanceof CertificateExpiredException &&
2462
hasExpiredTsaCert)) {
2463
// we already have hasExpiredTsaCert
2464
return;
2465
}
2466
}
2467
}
2468
2469
if (variant.equals(Validator.VAR_CODE_SIGNING) &&
2470
e instanceof ValidatorException) {
2471
// Throw cause if it's CertPathValidatorException,
2472
if (e.getCause() != null &&
2473
e.getCause() instanceof CertPathValidatorException) {
2474
e = (Exception) e.getCause();
2475
Throwable t = e.getCause();
2476
if ((t instanceof CertificateExpiredException &&
2477
hasExpiredCert) ||
2478
(t instanceof CertificateNotYetValidException &&
2479
notYetValidCert)) {
2480
// we already have hasExpiredCert and notYetValidCert
2481
return;
2482
}
2483
}
2484
if (e instanceof ValidatorException) {
2485
ValidatorException ve = (ValidatorException)e;
2486
if (ve.getErrorType() == ValidatorException.T_EE_EXTENSIONS &&
2487
(badKeyUsage || badExtendedKeyUsage || badNetscapeCertType)) {
2488
// We already have badKeyUsage, badExtendedKeyUsage
2489
// and badNetscapeCertType
2490
return;
2491
}
2492
}
2493
}
2494
throw e;
2495
}
2496
}
2497
2498
char[] getPass(String prompt)
2499
{
2500
System.err.print(prompt);
2501
System.err.flush();
2502
try {
2503
char[] pass = Password.readPassword(System.in);
2504
2505
if (pass == null) {
2506
error(rb.getString("you.must.enter.key.password"));
2507
} else {
2508
return pass;
2509
}
2510
} catch (IOException ioe) {
2511
error(rb.getString("unable.to.read.password.")+ioe.getMessage());
2512
}
2513
// this shouldn't happen
2514
return null;
2515
}
2516
2517
/*
2518
* Reads all the bytes for a given zip entry.
2519
*/
2520
private synchronized byte[] getBytes(ZipFile zf,
2521
ZipEntry ze) throws IOException {
2522
int n;
2523
2524
InputStream is = null;
2525
try {
2526
is = zf.getInputStream(ze);
2527
baos.reset();
2528
long left = ze.getSize();
2529
2530
while((left > 0) && (n = is.read(buffer, 0, buffer.length)) != -1) {
2531
baos.write(buffer, 0, n);
2532
left -= n;
2533
}
2534
} finally {
2535
if (is != null) {
2536
is.close();
2537
}
2538
}
2539
2540
return baos.toByteArray();
2541
}
2542
2543
/*
2544
* Returns manifest entry from given jar file, or null if given jar file
2545
* does not have a manifest entry.
2546
*/
2547
private ZipEntry getManifestFile(ZipFile zf) {
2548
ZipEntry ze = zf.getEntry(JarFile.MANIFEST_NAME);
2549
if (ze == null) {
2550
// Check all entries for matching name
2551
Enumeration<? extends ZipEntry> enum_ = zf.entries();
2552
while (enum_.hasMoreElements() && ze == null) {
2553
ze = enum_.nextElement();
2554
if (!JarFile.MANIFEST_NAME.equalsIgnoreCase
2555
(ze.getName())) {
2556
ze = null;
2557
}
2558
}
2559
}
2560
return ze;
2561
}
2562
2563
/*
2564
* Computes the digests of a zip entry, and returns them as an array
2565
* of base64-encoded strings.
2566
*/
2567
private synchronized String[] getDigests(ZipEntry ze, ZipFile zf,
2568
MessageDigest[] digests)
2569
throws IOException {
2570
2571
int n, i;
2572
InputStream is = null;
2573
try {
2574
is = zf.getInputStream(ze);
2575
long left = ze.getSize();
2576
while((left > 0)
2577
&& (n = is.read(buffer, 0, buffer.length)) != -1) {
2578
for (i=0; i<digests.length; i++) {
2579
digests[i].update(buffer, 0, n);
2580
}
2581
left -= n;
2582
}
2583
} finally {
2584
if (is != null) {
2585
is.close();
2586
}
2587
}
2588
2589
// complete the digests
2590
String[] base64Digests = new String[digests.length];
2591
for (i=0; i<digests.length; i++) {
2592
base64Digests[i] = Base64.getEncoder().encodeToString(digests[i].digest());
2593
}
2594
return base64Digests;
2595
}
2596
2597
/*
2598
* Computes the digests of a zip entry, and returns them as a list of
2599
* attributes
2600
*/
2601
private Attributes getDigestAttributes(ZipEntry ze, ZipFile zf,
2602
MessageDigest[] digests)
2603
throws IOException {
2604
2605
String[] base64Digests = getDigests(ze, zf, digests);
2606
Attributes attrs = new Attributes();
2607
2608
for (int i=0; i<digests.length; i++) {
2609
attrs.putValue(digests[i].getAlgorithm()+"-Digest",
2610
base64Digests[i]);
2611
}
2612
return attrs;
2613
}
2614
2615
/*
2616
* Updates the digest attributes of a manifest entry, by adding or
2617
* replacing digest values.
2618
* A digest value is added if the manifest entry does not contain a digest
2619
* for that particular algorithm.
2620
* A digest value is replaced if it is obsolete.
2621
*
2622
* Returns true if the manifest entry has been changed, and false
2623
* otherwise.
2624
*/
2625
private boolean updateDigests(ZipEntry ze, ZipFile zf,
2626
MessageDigest[] digests,
2627
Manifest mf) throws IOException {
2628
boolean update = false;
2629
2630
Attributes attrs = mf.getAttributes(ze.getName());
2631
String[] base64Digests = getDigests(ze, zf, digests);
2632
2633
for (int i=0; i<digests.length; i++) {
2634
// The entry name to be written into attrs
2635
String name = null;
2636
try {
2637
// Find if the digest already exists
2638
AlgorithmId aid = AlgorithmId.get(digests[i].getAlgorithm());
2639
for (Object key: attrs.keySet()) {
2640
if (key instanceof Attributes.Name) {
2641
String n = ((Attributes.Name)key).toString();
2642
if (n.toUpperCase(Locale.ENGLISH).endsWith("-DIGEST")) {
2643
String tmp = n.substring(0, n.length() - 7);
2644
if (AlgorithmId.get(tmp).equals(aid)) {
2645
name = n;
2646
break;
2647
}
2648
}
2649
}
2650
}
2651
} catch (NoSuchAlgorithmException nsae) {
2652
// Ignored. Writing new digest entry.
2653
}
2654
2655
if (name == null) {
2656
name = digests[i].getAlgorithm()+"-Digest";
2657
attrs.putValue(name, base64Digests[i]);
2658
update=true;
2659
} else {
2660
// compare digests, and replace the one in the manifest
2661
// if they are different
2662
String mfDigest = attrs.getValue(name);
2663
if (!mfDigest.equalsIgnoreCase(base64Digests[i])) {
2664
attrs.putValue(name, base64Digests[i]);
2665
update=true;
2666
}
2667
}
2668
}
2669
return update;
2670
}
2671
2672
/*
2673
* Try to load the specified signing mechanism.
2674
* The URL class loader is used.
2675
*/
2676
private ContentSigner loadSigningMechanism(String signerClassName,
2677
String signerClassPath) throws Exception {
2678
2679
// construct class loader
2680
String cpString = null; // make sure env.class.path defaults to dot
2681
2682
// do prepends to get correct ordering
2683
cpString = PathList.appendPath(System.getProperty("env.class.path"), cpString);
2684
cpString = PathList.appendPath(System.getProperty("java.class.path"), cpString);
2685
cpString = PathList.appendPath(signerClassPath, cpString);
2686
URL[] urls = PathList.pathToURLs(cpString);
2687
ClassLoader appClassLoader = new URLClassLoader(urls);
2688
2689
// attempt to find signer
2690
Class<?> signerClass = appClassLoader.loadClass(signerClassName);
2691
2692
// Check that it implements ContentSigner
2693
Object signer = signerClass.newInstance();
2694
if (!(signer instanceof ContentSigner)) {
2695
MessageFormat form = new MessageFormat(
2696
rb.getString("signerClass.is.not.a.signing.mechanism"));
2697
Object[] source = {signerClass.getName()};
2698
throw new IllegalArgumentException(form.format(source));
2699
}
2700
return (ContentSigner)signer;
2701
}
2702
}
2703
2704
class SignatureFile {
2705
2706
/** SignatureFile */
2707
Manifest sf;
2708
2709
/** .SF base name */
2710
String baseName;
2711
2712
public SignatureFile(MessageDigest digests[],
2713
Manifest mf,
2714
ManifestDigester md,
2715
String baseName,
2716
boolean signManifest)
2717
2718
{
2719
this.baseName = baseName;
2720
2721
String version = System.getProperty("java.version");
2722
String javaVendor = System.getProperty("java.vendor");
2723
2724
sf = new Manifest();
2725
Attributes mattr = sf.getMainAttributes();
2726
2727
mattr.putValue(Attributes.Name.SIGNATURE_VERSION.toString(), "1.0");
2728
mattr.putValue("Created-By", version + " (" + javaVendor + ")");
2729
2730
if (signManifest) {
2731
// sign the whole manifest
2732
for (int i=0; i < digests.length; i++) {
2733
mattr.putValue(digests[i].getAlgorithm()+"-Digest-Manifest",
2734
Base64.getEncoder().encodeToString(md.manifestDigest(digests[i])));
2735
}
2736
}
2737
2738
// create digest of the manifest main attributes
2739
ManifestDigester.Entry mde =
2740
md.get(ManifestDigester.MF_MAIN_ATTRS, false);
2741
if (mde != null) {
2742
for (int i=0; i < digests.length; i++) {
2743
mattr.putValue(digests[i].getAlgorithm() +
2744
"-Digest-" + ManifestDigester.MF_MAIN_ATTRS,
2745
Base64.getEncoder().encodeToString(mde.digest(digests[i])));
2746
}
2747
} else {
2748
throw new IllegalStateException
2749
("ManifestDigester failed to create " +
2750
"Manifest-Main-Attribute entry");
2751
}
2752
2753
/* go through the manifest entries and create the digests */
2754
2755
Map<String,Attributes> entries = sf.getEntries();
2756
Iterator<Map.Entry<String,Attributes>> mit =
2757
mf.getEntries().entrySet().iterator();
2758
while(mit.hasNext()) {
2759
Map.Entry<String,Attributes> e = mit.next();
2760
String name = e.getKey();
2761
mde = md.get(name, false);
2762
if (mde != null) {
2763
Attributes attr = new Attributes();
2764
for (int i=0; i < digests.length; i++) {
2765
attr.putValue(digests[i].getAlgorithm()+"-Digest",
2766
Base64.getEncoder().encodeToString(mde.digest(digests[i])));
2767
}
2768
entries.put(name, attr);
2769
}
2770
}
2771
}
2772
2773
/**
2774
* Writes the SignatureFile to the specified OutputStream.
2775
*
2776
* @param out the output stream
2777
* @exception IOException if an I/O error has occurred
2778
*/
2779
2780
public void write(OutputStream out) throws IOException
2781
{
2782
sf.write(out);
2783
}
2784
2785
/**
2786
* get .SF file name
2787
*/
2788
public String getMetaName()
2789
{
2790
return "META-INF/"+ baseName + ".SF";
2791
}
2792
2793
/**
2794
* get base file name
2795
*/
2796
public String getBaseName()
2797
{
2798
return baseName;
2799
}
2800
2801
/*
2802
* Generate a signed data block.
2803
* If a URL or a certificate (containing a URL) for a Timestamping
2804
* Authority is supplied then a signature timestamp is generated and
2805
* inserted into the signed data block.
2806
*
2807
* @param sigalg signature algorithm to use, or null to use default
2808
* @param tsaUrl The location of the Timestamping Authority. If null
2809
* then no timestamp is requested.
2810
* @param tsaCert The certificate for the Timestamping Authority. If null
2811
* then no timestamp is requested.
2812
* @param signingMechanism The signing mechanism to use.
2813
* @param args The command-line arguments to jarsigner.
2814
* @param zipFile The original source Zip file.
2815
*/
2816
public Block generateBlock(PrivateKey privateKey,
2817
String sigalg,
2818
X509Certificate[] certChain,
2819
boolean externalSF, String tsaUrl,
2820
X509Certificate tsaCert,
2821
String tSAPolicyID,
2822
String tSADigestAlg,
2823
ContentSigner signingMechanism,
2824
String[] args, ZipFile zipFile)
2825
throws NoSuchAlgorithmException, InvalidKeyException, IOException,
2826
SignatureException, CertificateException
2827
{
2828
return new Block(this, privateKey, sigalg, certChain, externalSF,
2829
tsaUrl, tsaCert, tSAPolicyID, tSADigestAlg, signingMechanism, args, zipFile);
2830
}
2831
2832
2833
public static class Block {
2834
2835
private byte[] block;
2836
private String blockFileName;
2837
2838
/*
2839
* Construct a new signature block.
2840
*/
2841
Block(SignatureFile sfg, PrivateKey privateKey, String sigalg,
2842
X509Certificate[] certChain, boolean externalSF, String tsaUrl,
2843
X509Certificate tsaCert, String tSAPolicyID, String tSADigestAlg,
2844
ContentSigner signingMechanism, String[] args, ZipFile zipFile)
2845
throws NoSuchAlgorithmException, InvalidKeyException, IOException,
2846
SignatureException, CertificateException {
2847
2848
Principal issuerName = certChain[0].getIssuerDN();
2849
if (!(issuerName instanceof X500Name)) {
2850
// must extract the original encoded form of DN for subsequent
2851
// name comparison checks (converting to a String and back to
2852
// an encoded DN could cause the types of String attribute
2853
// values to be changed)
2854
X509CertInfo tbsCert = new
2855
X509CertInfo(certChain[0].getTBSCertificate());
2856
issuerName = (Principal)
2857
tbsCert.get(X509CertInfo.ISSUER + "." +
2858
X509CertInfo.DN_NAME);
2859
}
2860
BigInteger serial = certChain[0].getSerialNumber();
2861
2862
String signatureAlgorithm = sigalg;
2863
String keyAlgorithm = privateKey.getAlgorithm();
2864
2865
// check common invalid key/signature algorithm combinations
2866
String sigAlgUpperCase = signatureAlgorithm.toUpperCase(Locale.ENGLISH);
2867
if ((sigAlgUpperCase.endsWith("WITHRSA") &&
2868
!keyAlgorithm.equalsIgnoreCase("RSA")) ||
2869
(sigAlgUpperCase.endsWith("WITHECDSA") &&
2870
!keyAlgorithm.equalsIgnoreCase("EC")) ||
2871
(sigAlgUpperCase.endsWith("WITHDSA") &&
2872
!keyAlgorithm.equalsIgnoreCase("DSA"))) {
2873
throw new SignatureException
2874
("private key algorithm is not compatible with signature algorithm");
2875
}
2876
2877
blockFileName = "META-INF/"+sfg.getBaseName()+"."+keyAlgorithm;
2878
2879
AlgorithmId sigAlg = AlgorithmId.get(signatureAlgorithm);
2880
AlgorithmId digEncrAlg = AlgorithmId.get(keyAlgorithm);
2881
2882
Signature sig = Signature.getInstance(signatureAlgorithm);
2883
sig.initSign(privateKey);
2884
2885
ByteArrayOutputStream baos = new ByteArrayOutputStream();
2886
sfg.write(baos);
2887
2888
byte[] content = baos.toByteArray();
2889
2890
sig.update(content);
2891
byte[] signature = sig.sign();
2892
2893
// Timestamp the signature and generate the signature block file
2894
if (signingMechanism == null) {
2895
signingMechanism = new TimestampedSigner();
2896
}
2897
URI tsaUri = null;
2898
try {
2899
if (tsaUrl != null) {
2900
tsaUri = new URI(tsaUrl);
2901
}
2902
} catch (URISyntaxException e) {
2903
throw new IOException(e);
2904
}
2905
2906
// Assemble parameters for the signing mechanism
2907
ContentSignerParameters params =
2908
new JarSignerParameters(args, tsaUri, tsaCert, tSAPolicyID,
2909
tSADigestAlg, signature,
2910
signatureAlgorithm, certChain, content, zipFile);
2911
2912
// Generate the signature block
2913
block = signingMechanism.generateSignedData(
2914
params, externalSF, (tsaUrl != null || tsaCert != null));
2915
}
2916
2917
/*
2918
* get block file name.
2919
*/
2920
public String getMetaName()
2921
{
2922
return blockFileName;
2923
}
2924
2925
/**
2926
* Writes the block file to the specified OutputStream.
2927
*
2928
* @param out the output stream
2929
* @exception IOException if an I/O error has occurred
2930
*/
2931
2932
public void write(OutputStream out) throws IOException
2933
{
2934
out.write(block);
2935
}
2936
}
2937
}
2938
2939