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/keytool/Main.java
38923 views
1
/*
2
* Copyright (c) 1997, 2020, 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.keytool;
27
28
import java.io.*;
29
import java.nio.file.Files;
30
import java.nio.file.Paths;
31
import java.security.CodeSigner;
32
import java.security.CryptoPrimitive;
33
import java.security.KeyStore;
34
import java.security.KeyStoreException;
35
import java.security.MessageDigest;
36
import java.security.Key;
37
import java.security.PublicKey;
38
import java.security.PrivateKey;
39
import java.security.Security;
40
import java.security.Signature;
41
import java.security.Timestamp;
42
import java.security.UnrecoverableEntryException;
43
import java.security.UnrecoverableKeyException;
44
import java.security.NoSuchAlgorithmException;
45
import java.security.Principal;
46
import java.security.Provider;
47
import java.security.cert.Certificate;
48
import java.security.cert.CertificateFactory;
49
import java.security.cert.CertStoreException;
50
import java.security.cert.CRL;
51
import java.security.cert.X509Certificate;
52
import java.security.cert.CertificateException;
53
import java.security.interfaces.ECKey;
54
import java.security.spec.AlgorithmParameterSpec;
55
import java.security.spec.ECParameterSpec;
56
import java.text.Collator;
57
import java.text.MessageFormat;
58
import java.util.*;
59
import java.util.jar.JarEntry;
60
import java.util.jar.JarFile;
61
import java.lang.reflect.Constructor;
62
import java.math.BigInteger;
63
import java.net.URI;
64
import java.net.URL;
65
import java.net.URLClassLoader;
66
import java.security.cert.CertStore;
67
68
import java.security.cert.X509CRL;
69
import java.security.cert.X509CRLEntry;
70
import java.security.cert.X509CRLSelector;
71
import javax.security.auth.x500.X500Principal;
72
import java.util.Base64;
73
74
import sun.security.util.DisabledAlgorithmConstraints;
75
import sun.security.util.KeyUtil;
76
import sun.security.util.NamedCurve;
77
import sun.security.util.ObjectIdentifier;
78
import sun.security.pkcs10.PKCS10;
79
import sun.security.pkcs10.PKCS10Attribute;
80
import sun.security.provider.X509Factory;
81
import sun.security.provider.certpath.CertStoreHelper;
82
import sun.security.util.Password;
83
import sun.security.util.SecurityProviderConstants;
84
import sun.security.util.SignatureUtil;
85
import javax.crypto.KeyGenerator;
86
import javax.crypto.SecretKey;
87
import javax.crypto.SecretKeyFactory;
88
import javax.crypto.spec.PBEKeySpec;
89
90
import sun.security.pkcs.PKCS9Attribute;
91
import sun.security.tools.KeyStoreUtil;
92
import sun.security.tools.PathList;
93
import sun.security.util.DerValue;
94
import sun.security.util.Pem;
95
import sun.security.x509.*;
96
97
import static java.security.KeyStore.*;
98
import static sun.security.tools.keytool.Main.Command.*;
99
import static sun.security.tools.keytool.Main.Option.*;
100
101
/**
102
* This tool manages keystores.
103
*
104
* @author Jan Luehe
105
*
106
*
107
* @see java.security.KeyStore
108
* @see sun.security.provider.KeyProtector
109
* @see sun.security.provider.JavaKeyStore
110
*
111
* @since 1.2
112
*/
113
public final class Main {
114
115
private static final byte[] CRLF = new byte[] {'\r', '\n'};
116
117
private boolean debug = false;
118
private Command command = null;
119
private String sigAlgName = null;
120
private String keyAlgName = null;
121
private boolean verbose = false;
122
private int keysize = -1;
123
private boolean rfc = false;
124
private long validity = (long)90;
125
private String alias = null;
126
private String dname = null;
127
private String dest = null;
128
private String filename = null;
129
private String infilename = null;
130
private String outfilename = null;
131
private String srcksfname = null;
132
133
// User-specified providers are added before any command is called.
134
// However, they are not removed before the end of the main() method.
135
// If you're calling KeyTool.main() directly in your own Java program,
136
// please programtically add any providers you need and do not specify
137
// them through the command line.
138
139
private Set<Pair <String, String>> providers = null;
140
private String storetype = null;
141
private String srcProviderName = null;
142
private String providerName = null;
143
private String pathlist = null;
144
private char[] storePass = null;
145
private char[] storePassNew = null;
146
private char[] keyPass = null;
147
private char[] keyPassNew = null;
148
private char[] newPass = null;
149
private char[] destKeyPass = null;
150
private char[] srckeyPass = null;
151
private String ksfname = null;
152
private File ksfile = null;
153
private InputStream ksStream = null; // keystore stream
154
private String sslserver = null;
155
private String jarfile = null;
156
private KeyStore keyStore = null;
157
private boolean token = false;
158
private boolean nullStream = false;
159
private boolean kssave = false;
160
private boolean noprompt = false;
161
private boolean trustcacerts = false;
162
private boolean nowarn = false;
163
private boolean protectedPath = false;
164
private boolean srcprotectedPath = false;
165
private CertificateFactory cf = null;
166
private KeyStore caks = null; // "cacerts" keystore
167
private char[] srcstorePass = null;
168
private String srcstoretype = null;
169
private Set<char[]> passwords = new HashSet<>();
170
private String startDate = null;
171
172
private List<String> ids = new ArrayList<>(); // used in GENCRL
173
private List<String> v3ext = new ArrayList<>();
174
175
// In-place importkeystore is special.
176
// A backup is needed, and no need to prompt for deststorepass.
177
private boolean inplaceImport = false;
178
private String inplaceBackupName = null;
179
180
// Warnings on weak algorithms etc
181
private List<String> weakWarnings = new ArrayList<>();
182
183
private static final DisabledAlgorithmConstraints DISABLED_CHECK =
184
new DisabledAlgorithmConstraints(
185
DisabledAlgorithmConstraints.PROPERTY_CERTPATH_DISABLED_ALGS);
186
187
private static final DisabledAlgorithmConstraints LEGACY_CHECK =
188
new DisabledAlgorithmConstraints(
189
DisabledAlgorithmConstraints.PROPERTY_SECURITY_LEGACY_ALGS);
190
191
private static final Set<CryptoPrimitive> SIG_PRIMITIVE_SET = Collections
192
.unmodifiableSet(EnumSet.of(CryptoPrimitive.SIGNATURE));
193
194
enum Command {
195
CERTREQ("Generates.a.certificate.request",
196
ALIAS, SIGALG, FILEOUT, KEYPASS, KEYSTORE, DNAME,
197
STOREPASS, STORETYPE, PROVIDERNAME, PROVIDERCLASS,
198
PROVIDERARG, PROVIDERPATH, V, PROTECTED),
199
CHANGEALIAS("Changes.an.entry.s.alias",
200
ALIAS, DESTALIAS, KEYPASS, KEYSTORE, STOREPASS,
201
STORETYPE, PROVIDERNAME, PROVIDERCLASS, PROVIDERARG,
202
PROVIDERPATH, V, PROTECTED),
203
DELETE("Deletes.an.entry",
204
ALIAS, KEYSTORE, STOREPASS, STORETYPE,
205
PROVIDERNAME, PROVIDERCLASS, PROVIDERARG,
206
PROVIDERPATH, V, PROTECTED),
207
EXPORTCERT("Exports.certificate",
208
RFC, ALIAS, FILEOUT, KEYSTORE, STOREPASS,
209
STORETYPE, PROVIDERNAME, PROVIDERCLASS, PROVIDERARG,
210
PROVIDERPATH, V, PROTECTED),
211
GENKEYPAIR("Generates.a.key.pair",
212
ALIAS, KEYALG, KEYSIZE, SIGALG, DESTALIAS, DNAME,
213
STARTDATE, EXT, VALIDITY, KEYPASS, KEYSTORE,
214
STOREPASS, STORETYPE, PROVIDERNAME, PROVIDERCLASS,
215
PROVIDERARG, PROVIDERPATH, V, PROTECTED),
216
GENSECKEY("Generates.a.secret.key",
217
ALIAS, KEYPASS, KEYALG, KEYSIZE, KEYSTORE,
218
STOREPASS, STORETYPE, PROVIDERNAME, PROVIDERCLASS,
219
PROVIDERARG, PROVIDERPATH, V, PROTECTED),
220
GENCERT("Generates.certificate.from.a.certificate.request",
221
RFC, INFILE, OUTFILE, ALIAS, SIGALG, DNAME,
222
STARTDATE, EXT, VALIDITY, KEYPASS, KEYSTORE,
223
STOREPASS, STORETYPE, PROVIDERNAME, PROVIDERCLASS,
224
PROVIDERARG, PROVIDERPATH, V, PROTECTED),
225
IMPORTCERT("Imports.a.certificate.or.a.certificate.chain",
226
NOPROMPT, TRUSTCACERTS, PROTECTED, ALIAS, FILEIN,
227
KEYPASS, KEYSTORE, STOREPASS, STORETYPE,
228
PROVIDERNAME, PROVIDERCLASS, PROVIDERARG,
229
PROVIDERPATH, V),
230
IMPORTPASS("Imports.a.password",
231
ALIAS, KEYPASS, KEYALG, KEYSIZE, KEYSTORE,
232
STOREPASS, STORETYPE, PROVIDERNAME, PROVIDERCLASS,
233
PROVIDERARG, PROVIDERPATH, V, PROTECTED),
234
IMPORTKEYSTORE("Imports.one.or.all.entries.from.another.keystore",
235
SRCKEYSTORE, DESTKEYSTORE, SRCSTORETYPE,
236
DESTSTORETYPE, SRCSTOREPASS, DESTSTOREPASS,
237
SRCPROTECTED, SRCPROVIDERNAME, DESTPROVIDERNAME,
238
SRCALIAS, DESTALIAS, SRCKEYPASS, DESTKEYPASS,
239
NOPROMPT, PROVIDERCLASS, PROVIDERARG, PROVIDERPATH,
240
V),
241
KEYPASSWD("Changes.the.key.password.of.an.entry",
242
ALIAS, KEYPASS, NEW, KEYSTORE, STOREPASS,
243
STORETYPE, PROVIDERNAME, PROVIDERCLASS, PROVIDERARG,
244
PROVIDERPATH, V),
245
LIST("Lists.entries.in.a.keystore",
246
RFC, ALIAS, KEYSTORE, STOREPASS, STORETYPE,
247
PROVIDERNAME, PROVIDERCLASS, PROVIDERARG,
248
PROVIDERPATH, V, PROTECTED),
249
PRINTCERT("Prints.the.content.of.a.certificate",
250
RFC, FILEIN, SSLSERVER, JARFILE, V),
251
PRINTCERTREQ("Prints.the.content.of.a.certificate.request",
252
FILEIN, V),
253
PRINTCRL("Prints.the.content.of.a.CRL.file",
254
FILEIN, V),
255
STOREPASSWD("Changes.the.store.password.of.a.keystore",
256
NEW, KEYSTORE, STOREPASS, STORETYPE, PROVIDERNAME,
257
PROVIDERCLASS, PROVIDERARG, PROVIDERPATH, V),
258
259
// Undocumented start here, KEYCLONE is used a marker in -help;
260
261
KEYCLONE("Clones.a.key.entry",
262
ALIAS, DESTALIAS, KEYPASS, NEW, STORETYPE,
263
KEYSTORE, STOREPASS, PROVIDERNAME, PROVIDERCLASS,
264
PROVIDERARG, PROVIDERPATH, V),
265
SELFCERT("Generates.a.self.signed.certificate",
266
ALIAS, SIGALG, DNAME, STARTDATE, VALIDITY, KEYPASS,
267
STORETYPE, KEYSTORE, STOREPASS, PROVIDERNAME,
268
PROVIDERCLASS, PROVIDERARG, PROVIDERPATH, V),
269
GENCRL("Generates.CRL",
270
RFC, FILEOUT, ID,
271
ALIAS, SIGALG, EXT, KEYPASS, KEYSTORE,
272
STOREPASS, STORETYPE, PROVIDERNAME, PROVIDERCLASS,
273
PROVIDERARG, PROVIDERPATH, V, PROTECTED),
274
IDENTITYDB("Imports.entries.from.a.JDK.1.1.x.style.identity.database",
275
FILEIN, STORETYPE, KEYSTORE, STOREPASS, PROVIDERNAME,
276
PROVIDERCLASS, PROVIDERARG, PROVIDERPATH, V);
277
278
final String description;
279
final Option[] options;
280
Command(String d, Option... o) {
281
description = d;
282
options = o;
283
}
284
@Override
285
public String toString() {
286
return "-" + name().toLowerCase(Locale.ENGLISH);
287
}
288
};
289
290
enum Option {
291
ALIAS("alias", "<alias>", "alias.name.of.the.entry.to.process"),
292
DESTALIAS("destalias", "<destalias>", "destination.alias"),
293
DESTKEYPASS("destkeypass", "<arg>", "destination.key.password"),
294
DESTKEYSTORE("destkeystore", "<destkeystore>", "destination.keystore.name"),
295
DESTPROTECTED("destprotected", null, "destination.keystore.password.protected"),
296
DESTPROVIDERNAME("destprovidername", "<destprovidername>", "destination.keystore.provider.name"),
297
DESTSTOREPASS("deststorepass", "<arg>", "destination.keystore.password"),
298
DESTSTORETYPE("deststoretype", "<deststoretype>", "destination.keystore.type"),
299
DNAME("dname", "<dname>", "distinguished.name"),
300
EXT("ext", "<value>", "X.509.extension"),
301
FILEOUT("file", "<filename>", "output.file.name"),
302
FILEIN("file", "<filename>", "input.file.name"),
303
ID("id", "<id:reason>", "Serial.ID.of.cert.to.revoke"),
304
INFILE("infile", "<filename>", "input.file.name"),
305
KEYALG("keyalg", "<keyalg>", "key.algorithm.name"),
306
KEYPASS("keypass", "<arg>", "key.password"),
307
KEYSIZE("keysize", "<keysize>", "key.bit.size"),
308
KEYSTORE("keystore", "<keystore>", "keystore.name"),
309
NEW("new", "<arg>", "new.password"),
310
NOPROMPT("noprompt", null, "do.not.prompt"),
311
OUTFILE("outfile", "<filename>", "output.file.name"),
312
PROTECTED("protected", null, "password.through.protected.mechanism"),
313
PROVIDERARG("providerarg", "<arg>", "provider.argument"),
314
PROVIDERCLASS("providerclass", "<providerclass>", "provider.class.name"),
315
PROVIDERNAME("providername", "<providername>", "provider.name"),
316
PROVIDERPATH("providerpath", "<pathlist>", "provider.classpath"),
317
RFC("rfc", null, "output.in.RFC.style"),
318
SIGALG("sigalg", "<sigalg>", "signature.algorithm.name"),
319
SRCALIAS("srcalias", "<srcalias>", "source.alias"),
320
SRCKEYPASS("srckeypass", "<arg>", "source.key.password"),
321
SRCKEYSTORE("srckeystore", "<srckeystore>", "source.keystore.name"),
322
SRCPROTECTED("srcprotected", null, "source.keystore.password.protected"),
323
SRCPROVIDERNAME("srcprovidername", "<srcprovidername>", "source.keystore.provider.name"),
324
SRCSTOREPASS("srcstorepass", "<arg>", "source.keystore.password"),
325
SRCSTORETYPE("srcstoretype", "<srcstoretype>", "source.keystore.type"),
326
SSLSERVER("sslserver", "<server[:port]>", "SSL.server.host.and.port"),
327
JARFILE("jarfile", "<filename>", "signed.jar.file"),
328
STARTDATE("startdate", "<startdate>", "certificate.validity.start.date.time"),
329
STOREPASS("storepass", "<arg>", "keystore.password"),
330
STORETYPE("storetype", "<storetype>", "keystore.type"),
331
TRUSTCACERTS("trustcacerts", null, "trust.certificates.from.cacerts"),
332
V("v", null, "verbose.output"),
333
VALIDITY("validity", "<valDays>", "validity.number.of.days");
334
335
final String name, arg, description;
336
Option(String name, String arg, String description) {
337
this.name = name;
338
this.arg = arg;
339
this.description = description;
340
}
341
@Override
342
public String toString() {
343
return "-" + name;
344
}
345
};
346
347
private static final Class<?>[] PARAM_STRING = { String.class };
348
349
private static final String NONE = "NONE";
350
private static final String P11KEYSTORE = "PKCS11";
351
private static final String P12KEYSTORE = "PKCS12";
352
private static final String keyAlias = "mykey";
353
354
// for i18n
355
private static final java.util.ResourceBundle rb =
356
java.util.ResourceBundle.getBundle(
357
"sun.security.tools.keytool.Resources");
358
private static final Collator collator = Collator.getInstance();
359
static {
360
// this is for case insensitive string comparisons
361
collator.setStrength(Collator.PRIMARY);
362
};
363
364
private Main() { }
365
366
public static void main(String[] args) throws Exception {
367
Main kt = new Main();
368
kt.run(args, System.out);
369
}
370
371
private void run(String[] args, PrintStream out) throws Exception {
372
try {
373
parseArgs(args);
374
if (command != null) {
375
doCommands(out);
376
}
377
} catch (Exception e) {
378
System.out.println(rb.getString("keytool.error.") + e);
379
if (verbose) {
380
e.printStackTrace(System.out);
381
}
382
if (!debug) {
383
System.exit(1);
384
} else {
385
throw e;
386
}
387
} finally {
388
printWeakWarnings(false);
389
for (char[] pass : passwords) {
390
if (pass != null) {
391
Arrays.fill(pass, ' ');
392
pass = null;
393
}
394
}
395
396
if (ksStream != null) {
397
ksStream.close();
398
}
399
}
400
}
401
402
/**
403
* Parse command line arguments.
404
*/
405
void parseArgs(String[] args) {
406
407
int i=0;
408
boolean help = args.length == 0;
409
410
for (i=0; (i < args.length) && args[i].startsWith("-"); i++) {
411
412
String flags = args[i];
413
414
// Check if the last option needs an arg
415
if (i == args.length - 1) {
416
for (Option option: Option.values()) {
417
// Only options with an arg need to be checked
418
if (collator.compare(flags, option.toString()) == 0) {
419
if (option.arg != null) errorNeedArgument(flags);
420
break;
421
}
422
}
423
}
424
425
/*
426
* Check modifiers
427
*/
428
String modifier = null;
429
int pos = flags.indexOf(':');
430
if (pos > 0) {
431
modifier = flags.substring(pos+1);
432
flags = flags.substring(0, pos);
433
}
434
/*
435
* command modes
436
*/
437
boolean isCommand = false;
438
for (Command c: Command.values()) {
439
if (collator.compare(flags, c.toString()) == 0) {
440
command = c;
441
isCommand = true;
442
break;
443
}
444
}
445
446
if (isCommand) {
447
// already recognized as a command
448
} else if (collator.compare(flags, "-export") == 0) {
449
command = EXPORTCERT;
450
} else if (collator.compare(flags, "-genkey") == 0) {
451
command = GENKEYPAIR;
452
} else if (collator.compare(flags, "-import") == 0) {
453
command = IMPORTCERT;
454
} else if (collator.compare(flags, "-importpassword") == 0) {
455
command = IMPORTPASS;
456
} else if (collator.compare(flags, "-help") == 0) {
457
help = true;
458
} else if (collator.compare(flags, "-nowarn") == 0) {
459
nowarn = true;
460
}
461
462
/*
463
* specifiers
464
*/
465
else if (collator.compare(flags, "-keystore") == 0 ||
466
collator.compare(flags, "-destkeystore") == 0) {
467
ksfname = args[++i];
468
} else if (collator.compare(flags, "-storepass") == 0 ||
469
collator.compare(flags, "-deststorepass") == 0) {
470
storePass = getPass(modifier, args[++i]);
471
passwords.add(storePass);
472
} else if (collator.compare(flags, "-storetype") == 0 ||
473
collator.compare(flags, "-deststoretype") == 0) {
474
storetype = KeyStoreUtil.niceStoreTypeName(args[++i]);
475
} else if (collator.compare(flags, "-srcstorepass") == 0) {
476
srcstorePass = getPass(modifier, args[++i]);
477
passwords.add(srcstorePass);
478
} else if (collator.compare(flags, "-srcstoretype") == 0) {
479
srcstoretype = KeyStoreUtil.niceStoreTypeName(args[++i]);
480
} else if (collator.compare(flags, "-srckeypass") == 0) {
481
srckeyPass = getPass(modifier, args[++i]);
482
passwords.add(srckeyPass);
483
} else if (collator.compare(flags, "-srcprovidername") == 0) {
484
srcProviderName = args[++i];
485
} else if (collator.compare(flags, "-providername") == 0 ||
486
collator.compare(flags, "-destprovidername") == 0) {
487
providerName = args[++i];
488
} else if (collator.compare(flags, "-providerpath") == 0) {
489
pathlist = args[++i];
490
} else if (collator.compare(flags, "-keypass") == 0) {
491
keyPass = getPass(modifier, args[++i]);
492
passwords.add(keyPass);
493
} else if (collator.compare(flags, "-new") == 0) {
494
newPass = getPass(modifier, args[++i]);
495
passwords.add(newPass);
496
} else if (collator.compare(flags, "-destkeypass") == 0) {
497
destKeyPass = getPass(modifier, args[++i]);
498
passwords.add(destKeyPass);
499
} else if (collator.compare(flags, "-alias") == 0 ||
500
collator.compare(flags, "-srcalias") == 0) {
501
alias = args[++i];
502
} else if (collator.compare(flags, "-dest") == 0 ||
503
collator.compare(flags, "-destalias") == 0) {
504
dest = args[++i];
505
} else if (collator.compare(flags, "-dname") == 0) {
506
dname = args[++i];
507
} else if (collator.compare(flags, "-keysize") == 0) {
508
keysize = Integer.parseInt(args[++i]);
509
} else if (collator.compare(flags, "-keyalg") == 0) {
510
keyAlgName = args[++i];
511
} else if (collator.compare(flags, "-sigalg") == 0) {
512
sigAlgName = args[++i];
513
} else if (collator.compare(flags, "-startdate") == 0) {
514
startDate = args[++i];
515
} else if (collator.compare(flags, "-validity") == 0) {
516
validity = Long.parseLong(args[++i]);
517
} else if (collator.compare(flags, "-ext") == 0) {
518
v3ext.add(args[++i]);
519
} else if (collator.compare(flags, "-id") == 0) {
520
ids.add(args[++i]);
521
} else if (collator.compare(flags, "-file") == 0) {
522
filename = args[++i];
523
} else if (collator.compare(flags, "-infile") == 0) {
524
infilename = args[++i];
525
} else if (collator.compare(flags, "-outfile") == 0) {
526
outfilename = args[++i];
527
} else if (collator.compare(flags, "-sslserver") == 0) {
528
sslserver = args[++i];
529
} else if (collator.compare(flags, "-jarfile") == 0) {
530
jarfile = args[++i];
531
} else if (collator.compare(flags, "-srckeystore") == 0) {
532
srcksfname = args[++i];
533
} else if ((collator.compare(flags, "-provider") == 0) ||
534
(collator.compare(flags, "-providerclass") == 0)) {
535
if (providers == null) {
536
providers = new HashSet<Pair <String, String>> (3);
537
}
538
String providerClass = args[++i];
539
String providerArg = null;
540
541
if (args.length > (i+1)) {
542
flags = args[i+1];
543
if (collator.compare(flags, "-providerarg") == 0) {
544
if (args.length == (i+2)) errorNeedArgument(flags);
545
providerArg = args[i+2];
546
i += 2;
547
}
548
}
549
providers.add(
550
Pair.of(providerClass, providerArg));
551
}
552
553
/*
554
* options
555
*/
556
else if (collator.compare(flags, "-v") == 0) {
557
verbose = true;
558
} else if (collator.compare(flags, "-debug") == 0) {
559
debug = true;
560
} else if (collator.compare(flags, "-rfc") == 0) {
561
rfc = true;
562
} else if (collator.compare(flags, "-noprompt") == 0) {
563
noprompt = true;
564
} else if (collator.compare(flags, "-trustcacerts") == 0) {
565
trustcacerts = true;
566
} else if (collator.compare(flags, "-protected") == 0 ||
567
collator.compare(flags, "-destprotected") == 0) {
568
protectedPath = true;
569
} else if (collator.compare(flags, "-srcprotected") == 0) {
570
srcprotectedPath = true;
571
} else {
572
System.err.println(rb.getString("Illegal.option.") + flags);
573
tinyHelp();
574
}
575
}
576
577
if (i<args.length) {
578
System.err.println(rb.getString("Illegal.option.") + args[i]);
579
tinyHelp();
580
}
581
582
if (command == null) {
583
if (help) {
584
usage();
585
} else {
586
System.err.println(rb.getString("Usage.error.no.command.provided"));
587
tinyHelp();
588
}
589
} else if (help) {
590
usage();
591
command = null;
592
}
593
}
594
595
boolean isKeyStoreRelated(Command cmd) {
596
return cmd != PRINTCERT && cmd != PRINTCERTREQ;
597
}
598
599
600
/**
601
* Execute the commands.
602
*/
603
void doCommands(PrintStream out) throws Exception {
604
if (P11KEYSTORE.equalsIgnoreCase(storetype) ||
605
KeyStoreUtil.isWindowsKeyStore(storetype)) {
606
token = true;
607
if (ksfname == null) {
608
ksfname = NONE;
609
}
610
}
611
if (NONE.equals(ksfname)) {
612
nullStream = true;
613
}
614
615
if (token && !nullStream) {
616
System.err.println(MessageFormat.format(rb.getString
617
(".keystore.must.be.NONE.if.storetype.is.{0}"), storetype));
618
System.err.println();
619
tinyHelp();
620
}
621
622
if (token &&
623
(command == KEYPASSWD || command == STOREPASSWD)) {
624
throw new UnsupportedOperationException(MessageFormat.format(rb.getString
625
(".storepasswd.and.keypasswd.commands.not.supported.if.storetype.is.{0}"), storetype));
626
}
627
628
if (token && (keyPass != null || newPass != null || destKeyPass != null)) {
629
throw new IllegalArgumentException(MessageFormat.format(rb.getString
630
(".keypass.and.new.can.not.be.specified.if.storetype.is.{0}"), storetype));
631
}
632
633
if (protectedPath) {
634
if (storePass != null || keyPass != null ||
635
newPass != null || destKeyPass != null) {
636
throw new IllegalArgumentException(rb.getString
637
("if.protected.is.specified.then.storepass.keypass.and.new.must.not.be.specified"));
638
}
639
}
640
641
if (srcprotectedPath) {
642
if (srcstorePass != null || srckeyPass != null) {
643
throw new IllegalArgumentException(rb.getString
644
("if.srcprotected.is.specified.then.srcstorepass.and.srckeypass.must.not.be.specified"));
645
}
646
}
647
648
if (KeyStoreUtil.isWindowsKeyStore(storetype)) {
649
if (storePass != null || keyPass != null ||
650
newPass != null || destKeyPass != null) {
651
throw new IllegalArgumentException(rb.getString
652
("if.keystore.is.not.password.protected.then.storepass.keypass.and.new.must.not.be.specified"));
653
}
654
}
655
656
if (KeyStoreUtil.isWindowsKeyStore(srcstoretype)) {
657
if (srcstorePass != null || srckeyPass != null) {
658
throw new IllegalArgumentException(rb.getString
659
("if.source.keystore.is.not.password.protected.then.srcstorepass.and.srckeypass.must.not.be.specified"));
660
}
661
}
662
663
if (validity <= (long)0) {
664
throw new Exception
665
(rb.getString("Validity.must.be.greater.than.zero"));
666
}
667
668
// Try to load and install specified provider
669
if (providers != null) {
670
ClassLoader cl = null;
671
if (pathlist != null) {
672
String path = null;
673
path = PathList.appendPath(
674
path, System.getProperty("java.class.path"));
675
path = PathList.appendPath(
676
path, System.getProperty("env.class.path"));
677
path = PathList.appendPath(path, pathlist);
678
679
URL[] urls = PathList.pathToURLs(path);
680
cl = new URLClassLoader(urls);
681
} else {
682
cl = ClassLoader.getSystemClassLoader();
683
}
684
685
for (Pair <String, String> provider: providers) {
686
String provName = provider.fst;
687
Class<?> provClass;
688
if (cl != null) {
689
provClass = cl.loadClass(provName);
690
} else {
691
provClass = Class.forName(provName);
692
}
693
694
String provArg = provider.snd;
695
Object obj;
696
if (provArg == null) {
697
obj = provClass.newInstance();
698
} else {
699
Constructor<?> c = provClass.getConstructor(PARAM_STRING);
700
obj = c.newInstance(provArg);
701
}
702
if (!(obj instanceof Provider)) {
703
MessageFormat form = new MessageFormat
704
(rb.getString("provName.not.a.provider"));
705
Object[] source = {provName};
706
throw new Exception(form.format(source));
707
}
708
Security.addProvider((Provider)obj);
709
}
710
}
711
712
if (command == LIST && verbose && rfc) {
713
System.err.println(rb.getString
714
("Must.not.specify.both.v.and.rfc.with.list.command"));
715
tinyHelp();
716
}
717
718
// Make sure provided passwords are at least 6 characters long
719
if (command == GENKEYPAIR && keyPass!=null && keyPass.length < 6) {
720
throw new Exception(rb.getString
721
("Key.password.must.be.at.least.6.characters"));
722
}
723
if (newPass != null && newPass.length < 6) {
724
throw new Exception(rb.getString
725
("New.password.must.be.at.least.6.characters"));
726
}
727
if (destKeyPass != null && destKeyPass.length < 6) {
728
throw new Exception(rb.getString
729
("New.password.must.be.at.least.6.characters"));
730
}
731
732
// Set this before inplaceImport check so we can compare name.
733
if (ksfname == null) {
734
ksfname = System.getProperty("user.home") + File.separator
735
+ ".keystore";
736
}
737
738
KeyStore srcKeyStore = null;
739
if (command == IMPORTKEYSTORE) {
740
inplaceImport = inplaceImportCheck();
741
if (inplaceImport) {
742
// We load srckeystore first so we have srcstorePass that
743
// can be assigned to storePass
744
srcKeyStore = loadSourceKeyStore();
745
if (storePass == null) {
746
storePass = srcstorePass;
747
}
748
}
749
}
750
751
// Check if keystore exists.
752
// If no keystore has been specified at the command line, try to use
753
// the default, which is located in $HOME/.keystore.
754
// If the command is "genkey", "identitydb", "import", or "printcert",
755
// it is OK not to have a keystore.
756
757
// DO NOT open the existing keystore if this is an in-place import.
758
// The keystore should be created as brand new.
759
if (isKeyStoreRelated(command) && !nullStream && !inplaceImport) {
760
try {
761
ksfile = new File(ksfname);
762
// Check if keystore file is empty
763
if (ksfile.exists() && ksfile.length() == 0) {
764
throw new Exception(rb.getString
765
("Keystore.file.exists.but.is.empty.") + ksfname);
766
}
767
ksStream = new FileInputStream(ksfile);
768
} catch (FileNotFoundException e) {
769
if (command != GENKEYPAIR &&
770
command != GENSECKEY &&
771
command != IDENTITYDB &&
772
command != IMPORTCERT &&
773
command != IMPORTPASS &&
774
command != IMPORTKEYSTORE &&
775
command != PRINTCRL) {
776
throw new Exception(rb.getString
777
("Keystore.file.does.not.exist.") + ksfname);
778
}
779
}
780
}
781
782
if ((command == KEYCLONE || command == CHANGEALIAS)
783
&& dest == null) {
784
dest = getAlias("destination");
785
if ("".equals(dest)) {
786
throw new Exception(rb.getString
787
("Must.specify.destination.alias"));
788
}
789
}
790
791
if (command == DELETE && alias == null) {
792
alias = getAlias(null);
793
if ("".equals(alias)) {
794
throw new Exception(rb.getString("Must.specify.alias"));
795
}
796
}
797
798
// Create new keystore
799
if (storetype == null) {
800
storetype = KeyStore.getDefaultType();
801
}
802
if (providerName == null) {
803
keyStore = KeyStore.getInstance(storetype);
804
} else {
805
keyStore = KeyStore.getInstance(storetype, providerName);
806
}
807
808
/*
809
* Load the keystore data.
810
*
811
* At this point, it's OK if no keystore password has been provided.
812
* We want to make sure that we can load the keystore data, i.e.,
813
* the keystore data has the right format. If we cannot load the
814
* keystore, why bother asking the user for his or her password?
815
* Only if we were able to load the keystore, and no keystore
816
* password has been provided, will we prompt the user for the
817
* keystore password to verify the keystore integrity.
818
* This means that the keystore is loaded twice: first load operation
819
* checks the keystore format, second load operation verifies the
820
* keystore integrity.
821
*
822
* If the keystore password has already been provided (at the
823
* command line), however, the keystore is loaded only once, and the
824
* keystore format and integrity are checked "at the same time".
825
*
826
* Null stream keystores are loaded later.
827
*/
828
if (!nullStream) {
829
if (inplaceImport) {
830
keyStore.load(null, storePass);
831
} else {
832
keyStore.load(ksStream, storePass);
833
}
834
if (ksStream != null) {
835
ksStream.close();
836
}
837
}
838
839
if (P12KEYSTORE.equalsIgnoreCase(storetype) && command == KEYPASSWD) {
840
throw new UnsupportedOperationException(rb.getString
841
(".keypasswd.commands.not.supported.if.storetype.is.PKCS12"));
842
}
843
844
// All commands that create or modify the keystore require a keystore
845
// password.
846
847
if (nullStream && storePass != null) {
848
keyStore.load(null, storePass);
849
} else if (!nullStream && storePass != null) {
850
// If we are creating a new non nullStream-based keystore,
851
// insist that the password be at least 6 characters
852
if (ksStream == null && storePass.length < 6) {
853
throw new Exception(rb.getString
854
("Keystore.password.must.be.at.least.6.characters"));
855
}
856
} else if (storePass == null) {
857
858
// only prompt if (protectedPath == false)
859
860
if (!protectedPath && !KeyStoreUtil.isWindowsKeyStore(storetype) &&
861
(command == CERTREQ ||
862
command == DELETE ||
863
command == GENKEYPAIR ||
864
command == GENSECKEY ||
865
command == IMPORTCERT ||
866
command == IMPORTPASS ||
867
command == IMPORTKEYSTORE ||
868
command == KEYCLONE ||
869
command == CHANGEALIAS ||
870
command == SELFCERT ||
871
command == STOREPASSWD ||
872
command == KEYPASSWD ||
873
command == IDENTITYDB)) {
874
int count = 0;
875
do {
876
if (command == IMPORTKEYSTORE) {
877
System.err.print
878
(rb.getString("Enter.destination.keystore.password."));
879
} else {
880
System.err.print
881
(rb.getString("Enter.keystore.password."));
882
}
883
System.err.flush();
884
storePass = Password.readPassword(System.in);
885
passwords.add(storePass);
886
887
// If we are creating a new non nullStream-based keystore,
888
// insist that the password be at least 6 characters
889
if (!nullStream && (storePass == null || storePass.length < 6)) {
890
System.err.println(rb.getString
891
("Keystore.password.is.too.short.must.be.at.least.6.characters"));
892
storePass = null;
893
}
894
895
// If the keystore file does not exist and needs to be
896
// created, the storepass should be prompted twice.
897
if (storePass != null && !nullStream && ksStream == null) {
898
System.err.print(rb.getString("Re.enter.new.password."));
899
char[] storePassAgain = Password.readPassword(System.in);
900
passwords.add(storePassAgain);
901
if (!Arrays.equals(storePass, storePassAgain)) {
902
System.err.println
903
(rb.getString("They.don.t.match.Try.again"));
904
storePass = null;
905
}
906
}
907
908
count++;
909
} while ((storePass == null) && count < 3);
910
911
912
if (storePass == null) {
913
System.err.println
914
(rb.getString("Too.many.failures.try.later"));
915
return;
916
}
917
} else if (!protectedPath
918
&& !KeyStoreUtil.isWindowsKeyStore(storetype)
919
&& isKeyStoreRelated(command)) {
920
// here we have EXPORTCERT and LIST (info valid until STOREPASSWD)
921
if (command != PRINTCRL) {
922
System.err.print(rb.getString("Enter.keystore.password."));
923
System.err.flush();
924
storePass = Password.readPassword(System.in);
925
passwords.add(storePass);
926
}
927
}
928
929
// Now load a nullStream-based keystore,
930
// or verify the integrity of an input stream-based keystore
931
if (nullStream) {
932
keyStore.load(null, storePass);
933
} else if (ksStream != null) {
934
ksStream = new FileInputStream(ksfile);
935
keyStore.load(ksStream, storePass);
936
ksStream.close();
937
}
938
}
939
940
if (storePass != null && P12KEYSTORE.equalsIgnoreCase(storetype)) {
941
MessageFormat form = new MessageFormat(rb.getString(
942
"Warning.Different.store.and.key.passwords.not.supported.for.PKCS12.KeyStores.Ignoring.user.specified.command.value."));
943
if (keyPass != null && !Arrays.equals(storePass, keyPass)) {
944
Object[] source = {"-keypass"};
945
System.err.println(form.format(source));
946
keyPass = storePass;
947
}
948
if (newPass != null && !Arrays.equals(storePass, newPass)) {
949
Object[] source = {"-new"};
950
System.err.println(form.format(source));
951
newPass = storePass;
952
}
953
if (destKeyPass != null && !Arrays.equals(storePass, destKeyPass)) {
954
Object[] source = {"-destkeypass"};
955
System.err.println(form.format(source));
956
destKeyPass = storePass;
957
}
958
}
959
960
// Create a certificate factory
961
if (command == PRINTCERT || command == IMPORTCERT
962
|| command == IDENTITYDB || command == PRINTCRL) {
963
cf = CertificateFactory.getInstance("X509");
964
}
965
966
// -trustcacerts can only be specified on -importcert.
967
// Reset it so that warnings on CA cert will remain for
968
// -printcert, etc.
969
if (command != IMPORTCERT) {
970
trustcacerts = false;
971
}
972
973
if (trustcacerts) {
974
caks = KeyStoreUtil.getCacertsKeyStore();
975
}
976
977
// Perform the specified command
978
if (command == CERTREQ) {
979
if (filename != null) {
980
try (PrintStream ps = new PrintStream(new FileOutputStream
981
(filename))) {
982
doCertReq(alias, sigAlgName, ps);
983
}
984
} else {
985
doCertReq(alias, sigAlgName, out);
986
}
987
if (verbose && filename != null) {
988
MessageFormat form = new MessageFormat(rb.getString
989
("Certification.request.stored.in.file.filename."));
990
Object[] source = {filename};
991
System.err.println(form.format(source));
992
System.err.println(rb.getString("Submit.this.to.your.CA"));
993
}
994
} else if (command == DELETE) {
995
doDeleteEntry(alias);
996
kssave = true;
997
} else if (command == EXPORTCERT) {
998
if (filename != null) {
999
try (PrintStream ps = new PrintStream(new FileOutputStream
1000
(filename))) {
1001
doExportCert(alias, ps);
1002
}
1003
} else {
1004
doExportCert(alias, out);
1005
}
1006
if (filename != null) {
1007
MessageFormat form = new MessageFormat(rb.getString
1008
("Certificate.stored.in.file.filename."));
1009
Object[] source = {filename};
1010
System.err.println(form.format(source));
1011
}
1012
} else if (command == GENKEYPAIR) {
1013
if (keyAlgName == null) {
1014
keyAlgName = "DSA";
1015
}
1016
doGenKeyPair(alias, dname, keyAlgName, keysize, sigAlgName);
1017
kssave = true;
1018
} else if (command == GENSECKEY) {
1019
if (keyAlgName == null) {
1020
keyAlgName = "DES";
1021
}
1022
doGenSecretKey(alias, keyAlgName, keysize);
1023
kssave = true;
1024
} else if (command == IMPORTPASS) {
1025
if (keyAlgName == null) {
1026
keyAlgName = "PBE";
1027
}
1028
// password is stored as a secret key
1029
doGenSecretKey(alias, keyAlgName, keysize);
1030
kssave = true;
1031
} else if (command == IDENTITYDB) {
1032
if (filename != null) {
1033
try (InputStream inStream = new FileInputStream(filename)) {
1034
doImportIdentityDatabase(inStream);
1035
}
1036
} else {
1037
doImportIdentityDatabase(System.in);
1038
}
1039
} else if (command == IMPORTCERT) {
1040
InputStream inStream = System.in;
1041
if (filename != null) {
1042
inStream = new FileInputStream(filename);
1043
}
1044
String importAlias = (alias!=null)?alias:keyAlias;
1045
try {
1046
if (keyStore.entryInstanceOf(
1047
importAlias, KeyStore.PrivateKeyEntry.class)) {
1048
kssave = installReply(importAlias, inStream);
1049
if (kssave) {
1050
System.err.println(rb.getString
1051
("Certificate.reply.was.installed.in.keystore"));
1052
} else {
1053
System.err.println(rb.getString
1054
("Certificate.reply.was.not.installed.in.keystore"));
1055
}
1056
} else if (!keyStore.containsAlias(importAlias) ||
1057
keyStore.entryInstanceOf(importAlias,
1058
KeyStore.TrustedCertificateEntry.class)) {
1059
kssave = addTrustedCert(importAlias, inStream);
1060
if (kssave) {
1061
System.err.println(rb.getString
1062
("Certificate.was.added.to.keystore"));
1063
} else {
1064
System.err.println(rb.getString
1065
("Certificate.was.not.added.to.keystore"));
1066
}
1067
}
1068
} finally {
1069
if (inStream != System.in) {
1070
inStream.close();
1071
}
1072
}
1073
} else if (command == IMPORTKEYSTORE) {
1074
// When not in-place import, srcKeyStore is not loaded yet.
1075
if (srcKeyStore == null) {
1076
srcKeyStore = loadSourceKeyStore();
1077
}
1078
doImportKeyStore(srcKeyStore);
1079
kssave = true;
1080
} else if (command == KEYCLONE) {
1081
keyPassNew = newPass;
1082
1083
// added to make sure only key can go thru
1084
if (alias == null) {
1085
alias = keyAlias;
1086
}
1087
if (keyStore.containsAlias(alias) == false) {
1088
MessageFormat form = new MessageFormat
1089
(rb.getString("Alias.alias.does.not.exist"));
1090
Object[] source = {alias};
1091
throw new Exception(form.format(source));
1092
}
1093
if (!keyStore.entryInstanceOf(alias, KeyStore.PrivateKeyEntry.class)) {
1094
MessageFormat form = new MessageFormat(rb.getString(
1095
"Alias.alias.references.an.entry.type.that.is.not.a.private.key.entry.The.keyclone.command.only.supports.cloning.of.private.key"));
1096
Object[] source = {alias};
1097
throw new Exception(form.format(source));
1098
}
1099
1100
doCloneEntry(alias, dest, true); // Now everything can be cloned
1101
kssave = true;
1102
} else if (command == CHANGEALIAS) {
1103
if (alias == null) {
1104
alias = keyAlias;
1105
}
1106
doCloneEntry(alias, dest, false);
1107
// in PKCS11, clone a PrivateKeyEntry will delete the old one
1108
if (keyStore.containsAlias(alias)) {
1109
doDeleteEntry(alias);
1110
}
1111
kssave = true;
1112
} else if (command == KEYPASSWD) {
1113
keyPassNew = newPass;
1114
doChangeKeyPasswd(alias);
1115
kssave = true;
1116
} else if (command == LIST) {
1117
if (storePass == null
1118
&& !KeyStoreUtil.isWindowsKeyStore(storetype)) {
1119
printNoIntegrityWarning();
1120
}
1121
1122
if (alias != null) {
1123
doPrintEntry(rb.getString("the.certificate"), alias, out);
1124
} else {
1125
doPrintEntries(out);
1126
}
1127
} else if (command == PRINTCERT) {
1128
doPrintCert(out);
1129
} else if (command == SELFCERT) {
1130
doSelfCert(alias, dname, sigAlgName);
1131
kssave = true;
1132
} else if (command == STOREPASSWD) {
1133
storePassNew = newPass;
1134
if (storePassNew == null) {
1135
storePassNew = getNewPasswd("keystore password", storePass);
1136
}
1137
kssave = true;
1138
} else if (command == GENCERT) {
1139
if (alias == null) {
1140
alias = keyAlias;
1141
}
1142
InputStream inStream = System.in;
1143
if (infilename != null) {
1144
inStream = new FileInputStream(infilename);
1145
}
1146
PrintStream ps = null;
1147
if (outfilename != null) {
1148
ps = new PrintStream(new FileOutputStream(outfilename));
1149
out = ps;
1150
}
1151
try {
1152
doGenCert(alias, sigAlgName, inStream, out);
1153
} finally {
1154
if (inStream != System.in) {
1155
inStream.close();
1156
}
1157
if (ps != null) {
1158
ps.close();
1159
}
1160
}
1161
} else if (command == GENCRL) {
1162
if (alias == null) {
1163
alias = keyAlias;
1164
}
1165
if (filename != null) {
1166
try (PrintStream ps =
1167
new PrintStream(new FileOutputStream(filename))) {
1168
doGenCRL(ps);
1169
}
1170
} else {
1171
doGenCRL(out);
1172
}
1173
} else if (command == PRINTCERTREQ) {
1174
if (filename != null) {
1175
try (InputStream inStream = new FileInputStream(filename)) {
1176
doPrintCertReq(inStream, out);
1177
}
1178
} else {
1179
doPrintCertReq(System.in, out);
1180
}
1181
} else if (command == PRINTCRL) {
1182
doPrintCRL(filename, out);
1183
}
1184
1185
// If we need to save the keystore, do so.
1186
if (kssave) {
1187
if (verbose) {
1188
MessageFormat form = new MessageFormat
1189
(rb.getString(".Storing.ksfname."));
1190
Object[] source = {nullStream ? "keystore" : ksfname};
1191
System.err.println(form.format(source));
1192
}
1193
1194
if (token) {
1195
keyStore.store(null, null);
1196
} else {
1197
char[] pass = (storePassNew!=null) ? storePassNew : storePass;
1198
if (nullStream) {
1199
keyStore.store(null, pass);
1200
} else {
1201
ByteArrayOutputStream bout = new ByteArrayOutputStream();
1202
keyStore.store(bout, pass);
1203
try (FileOutputStream fout = new FileOutputStream(ksfname)) {
1204
fout.write(bout.toByteArray());
1205
}
1206
}
1207
}
1208
}
1209
1210
if (isKeyStoreRelated(command)
1211
&& !token && !nullStream && ksfname != null) {
1212
1213
// JKS storetype warning on the final result keystore
1214
File f = new File(ksfname);
1215
if (f.exists()) {
1216
// Read the first 4 bytes to determine
1217
// if we're dealing with JKS/JCEKS type store
1218
String realType = keyStoreType(f);
1219
if (realType.equalsIgnoreCase("JKS")
1220
|| realType.equalsIgnoreCase("JCEKS")) {
1221
boolean allCerts = true;
1222
for (String a : Collections.list(keyStore.aliases())) {
1223
if (!keyStore.entryInstanceOf(
1224
a, TrustedCertificateEntry.class)) {
1225
allCerts = false;
1226
break;
1227
}
1228
}
1229
// Don't warn for "cacerts" style keystore.
1230
if (!allCerts) {
1231
weakWarnings.add(String.format(
1232
rb.getString("jks.storetype.warning"),
1233
realType, ksfname));
1234
}
1235
}
1236
if (inplaceImport) {
1237
String realSourceStoreType =
1238
keyStoreType(new File(inplaceBackupName));
1239
String format =
1240
realType.equalsIgnoreCase(realSourceStoreType) ?
1241
rb.getString("backup.keystore.warning") :
1242
rb.getString("migrate.keystore.warning");
1243
weakWarnings.add(
1244
String.format(format,
1245
srcksfname,
1246
realSourceStoreType,
1247
inplaceBackupName,
1248
realType));
1249
}
1250
}
1251
}
1252
}
1253
1254
private String keyStoreType(File f) throws IOException {
1255
int MAGIC = 0xfeedfeed;
1256
int JCEKS_MAGIC = 0xcececece;
1257
try (DataInputStream dis = new DataInputStream(
1258
new FileInputStream(f))) {
1259
int xMagic = dis.readInt();
1260
if (xMagic == MAGIC) {
1261
return "JKS";
1262
} else if (xMagic == JCEKS_MAGIC) {
1263
return "JCEKS";
1264
} else {
1265
return "Non JKS/JCEKS";
1266
}
1267
}
1268
}
1269
1270
/**
1271
* Generate a certificate: Read PKCS10 request from in, and print
1272
* certificate to out. Use alias as CA, sigAlgName as the signature
1273
* type.
1274
*/
1275
private void doGenCert(String alias, String sigAlgName, InputStream in, PrintStream out)
1276
throws Exception {
1277
1278
1279
if (keyStore.containsAlias(alias) == false) {
1280
MessageFormat form = new MessageFormat
1281
(rb.getString("Alias.alias.does.not.exist"));
1282
Object[] source = {alias};
1283
throw new Exception(form.format(source));
1284
}
1285
Certificate signerCert = keyStore.getCertificate(alias);
1286
byte[] encoded = signerCert.getEncoded();
1287
X509CertImpl signerCertImpl = new X509CertImpl(encoded);
1288
X509CertInfo signerCertInfo = (X509CertInfo)signerCertImpl.get(
1289
X509CertImpl.NAME + "." + X509CertImpl.INFO);
1290
X500Name issuer = (X500Name)signerCertInfo.get(X509CertInfo.SUBJECT + "." +
1291
X509CertInfo.DN_NAME);
1292
1293
Date firstDate = getStartDate(startDate);
1294
Date lastDate = new Date();
1295
lastDate.setTime(firstDate.getTime() + validity*1000L*24L*60L*60L);
1296
CertificateValidity interval = new CertificateValidity(firstDate,
1297
lastDate);
1298
1299
PrivateKey privateKey =
1300
(PrivateKey)recoverKey(alias, storePass, keyPass).fst;
1301
if (sigAlgName == null) {
1302
sigAlgName = getCompatibleSigAlgName(privateKey.getAlgorithm());
1303
}
1304
Signature signature = Signature.getInstance(sigAlgName);
1305
AlgorithmParameterSpec params = AlgorithmId
1306
.getDefaultAlgorithmParameterSpec(sigAlgName, privateKey);
1307
1308
SignatureUtil.initSignWithParam(signature, privateKey, params, null);
1309
1310
X509CertInfo info = new X509CertInfo();
1311
AlgorithmId algID = AlgorithmId.getWithParameterSpec(sigAlgName, params);
1312
info.set(X509CertInfo.VALIDITY, interval);
1313
info.set(X509CertInfo.SERIAL_NUMBER, new CertificateSerialNumber(
1314
new java.util.Random().nextInt() & 0x7fffffff));
1315
info.set(X509CertInfo.VERSION,
1316
new CertificateVersion(CertificateVersion.V3));
1317
info.set(X509CertInfo.ALGORITHM_ID,
1318
new CertificateAlgorithmId(algID));
1319
info.set(X509CertInfo.ISSUER, issuer);
1320
1321
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
1322
boolean canRead = false;
1323
StringBuffer sb = new StringBuffer();
1324
while (true) {
1325
String s = reader.readLine();
1326
if (s == null) break;
1327
// OpenSSL does not use NEW
1328
//if (s.startsWith("-----BEGIN NEW CERTIFICATE REQUEST-----")) {
1329
if (s.startsWith("-----BEGIN") && s.indexOf("REQUEST") >= 0) {
1330
canRead = true;
1331
//} else if (s.startsWith("-----END NEW CERTIFICATE REQUEST-----")) {
1332
} else if (s.startsWith("-----END") && s.indexOf("REQUEST") >= 0) {
1333
break;
1334
} else if (canRead) {
1335
sb.append(s);
1336
}
1337
}
1338
byte[] rawReq = Pem.decode(new String(sb));
1339
PKCS10 req = new PKCS10(rawReq);
1340
1341
checkWeak(rb.getString("the.certificate.request"), req);
1342
1343
info.set(X509CertInfo.KEY, new CertificateX509Key(req.getSubjectPublicKeyInfo()));
1344
info.set(X509CertInfo.SUBJECT,
1345
dname==null?req.getSubjectName():new X500Name(dname));
1346
CertificateExtensions reqex = null;
1347
Iterator<PKCS10Attribute> attrs = req.getAttributes().getAttributes().iterator();
1348
while (attrs.hasNext()) {
1349
PKCS10Attribute attr = attrs.next();
1350
if (attr.getAttributeId().equals((Object)PKCS9Attribute.EXTENSION_REQUEST_OID)) {
1351
reqex = (CertificateExtensions)attr.getAttributeValue();
1352
}
1353
}
1354
CertificateExtensions ext = createV3Extensions(
1355
reqex,
1356
null,
1357
v3ext,
1358
req.getSubjectPublicKeyInfo(),
1359
signerCert.getPublicKey());
1360
info.set(X509CertInfo.EXTENSIONS, ext);
1361
X509CertImpl cert = new X509CertImpl(info);
1362
cert.sign(privateKey, params, sigAlgName, null);
1363
dumpCert(cert, out);
1364
for (Certificate ca: keyStore.getCertificateChain(alias)) {
1365
if (ca instanceof X509Certificate) {
1366
X509Certificate xca = (X509Certificate)ca;
1367
if (!KeyStoreUtil.isSelfSigned(xca)) {
1368
dumpCert(xca, out);
1369
}
1370
}
1371
}
1372
1373
checkWeak(rb.getString("the.issuer"), keyStore.getCertificateChain(alias));
1374
checkWeak(rb.getString("the.generated.certificate"), cert);
1375
}
1376
1377
private void doGenCRL(PrintStream out)
1378
throws Exception {
1379
if (ids == null) {
1380
throw new Exception("Must provide -id when -gencrl");
1381
}
1382
Certificate signerCert = keyStore.getCertificate(alias);
1383
byte[] encoded = signerCert.getEncoded();
1384
X509CertImpl signerCertImpl = new X509CertImpl(encoded);
1385
X509CertInfo signerCertInfo = (X509CertInfo)signerCertImpl.get(
1386
X509CertImpl.NAME + "." + X509CertImpl.INFO);
1387
X500Name owner = (X500Name)signerCertInfo.get(X509CertInfo.SUBJECT + "." +
1388
X509CertInfo.DN_NAME);
1389
1390
Date firstDate = getStartDate(startDate);
1391
Date lastDate = (Date) firstDate.clone();
1392
lastDate.setTime(lastDate.getTime() + validity*1000*24*60*60);
1393
CertificateValidity interval = new CertificateValidity(firstDate,
1394
lastDate);
1395
1396
1397
PrivateKey privateKey =
1398
(PrivateKey)recoverKey(alias, storePass, keyPass).fst;
1399
if (sigAlgName == null) {
1400
sigAlgName = getCompatibleSigAlgName(privateKey.getAlgorithm());
1401
}
1402
1403
X509CRLEntry[] badCerts = new X509CRLEntry[ids.size()];
1404
for (int i=0; i<ids.size(); i++) {
1405
String id = ids.get(i);
1406
int d = id.indexOf(':');
1407
if (d >= 0) {
1408
CRLExtensions ext = new CRLExtensions();
1409
ext.set("Reason", new CRLReasonCodeExtension(Integer.parseInt(id.substring(d+1))));
1410
badCerts[i] = new X509CRLEntryImpl(new BigInteger(id.substring(0, d)),
1411
firstDate, ext);
1412
} else {
1413
badCerts[i] = new X509CRLEntryImpl(new BigInteger(ids.get(i)), firstDate);
1414
}
1415
}
1416
X509CRLImpl crl = new X509CRLImpl(owner, firstDate, lastDate, badCerts);
1417
crl.sign(privateKey, sigAlgName);
1418
if (rfc) {
1419
out.println("-----BEGIN X509 CRL-----");
1420
out.println(Base64.getMimeEncoder(64, CRLF).encodeToString(crl.getEncodedInternal()));
1421
out.println("-----END X509 CRL-----");
1422
} else {
1423
out.write(crl.getEncodedInternal());
1424
}
1425
checkWeak(rb.getString("the.generated.crl"), crl, privateKey);
1426
}
1427
1428
/**
1429
* Creates a PKCS#10 cert signing request, corresponding to the
1430
* keys (and name) associated with a given alias.
1431
*/
1432
private void doCertReq(String alias, String sigAlgName, PrintStream out)
1433
throws Exception
1434
{
1435
if (alias == null) {
1436
alias = keyAlias;
1437
}
1438
1439
Pair<Key,char[]> objs = recoverKey(alias, storePass, keyPass);
1440
PrivateKey privKey = (PrivateKey)objs.fst;
1441
if (keyPass == null) {
1442
keyPass = objs.snd;
1443
}
1444
1445
Certificate cert = keyStore.getCertificate(alias);
1446
if (cert == null) {
1447
MessageFormat form = new MessageFormat
1448
(rb.getString("alias.has.no.public.key.certificate."));
1449
Object[] source = {alias};
1450
throw new Exception(form.format(source));
1451
}
1452
PKCS10 request = new PKCS10(cert.getPublicKey());
1453
CertificateExtensions ext = createV3Extensions(null, null, v3ext, cert.getPublicKey(), null);
1454
// Attribute name is not significant
1455
request.getAttributes().setAttribute(X509CertInfo.EXTENSIONS,
1456
new PKCS10Attribute(PKCS9Attribute.EXTENSION_REQUEST_OID, ext));
1457
1458
// Construct a Signature object, so that we can sign the request
1459
if (sigAlgName == null) {
1460
sigAlgName = getCompatibleSigAlgName(privKey.getAlgorithm());
1461
}
1462
1463
Signature signature = Signature.getInstance(sigAlgName);
1464
AlgorithmParameterSpec params = AlgorithmId
1465
.getDefaultAlgorithmParameterSpec(sigAlgName, privKey);
1466
SignatureUtil.initSignWithParam(signature, privKey, params, null);
1467
1468
X500Name subject = dname == null?
1469
new X500Name(((X509Certificate)cert).getSubjectDN().toString()):
1470
new X500Name(dname);
1471
1472
// Sign the request and base-64 encode it
1473
request.encodeAndSign(subject, signature);
1474
request.print(out);
1475
1476
checkWeak(rb.getString("the.generated.certificate.request"), request);
1477
}
1478
1479
/**
1480
* Deletes an entry from the keystore.
1481
*/
1482
private void doDeleteEntry(String alias) throws Exception {
1483
if (keyStore.containsAlias(alias) == false) {
1484
MessageFormat form = new MessageFormat
1485
(rb.getString("Alias.alias.does.not.exist"));
1486
Object[] source = {alias};
1487
throw new Exception(form.format(source));
1488
}
1489
keyStore.deleteEntry(alias);
1490
}
1491
1492
/**
1493
* Exports a certificate from the keystore.
1494
*/
1495
private void doExportCert(String alias, PrintStream out)
1496
throws Exception
1497
{
1498
if (storePass == null
1499
&& !KeyStoreUtil.isWindowsKeyStore(storetype)) {
1500
printNoIntegrityWarning();
1501
}
1502
if (alias == null) {
1503
alias = keyAlias;
1504
}
1505
if (keyStore.containsAlias(alias) == false) {
1506
MessageFormat form = new MessageFormat
1507
(rb.getString("Alias.alias.does.not.exist"));
1508
Object[] source = {alias};
1509
throw new Exception(form.format(source));
1510
}
1511
1512
X509Certificate cert = (X509Certificate)keyStore.getCertificate(alias);
1513
if (cert == null) {
1514
MessageFormat form = new MessageFormat
1515
(rb.getString("Alias.alias.has.no.certificate"));
1516
Object[] source = {alias};
1517
throw new Exception(form.format(source));
1518
}
1519
dumpCert(cert, out);
1520
checkWeak(rb.getString("the.certificate"), cert);
1521
}
1522
1523
/**
1524
* Prompt the user for a keypass when generating a key entry.
1525
* @param alias the entry we will set password for
1526
* @param orig the original entry of doing a dup, null if generate new
1527
* @param origPass the password to copy from if user press ENTER
1528
*/
1529
private char[] promptForKeyPass(String alias, String orig, char[] origPass) throws Exception{
1530
if (P12KEYSTORE.equalsIgnoreCase(storetype)) {
1531
return origPass;
1532
} else if (!token && !protectedPath) {
1533
// Prompt for key password
1534
int count;
1535
for (count = 0; count < 3; count++) {
1536
MessageFormat form = new MessageFormat(rb.getString
1537
("Enter.key.password.for.alias."));
1538
Object[] source = {alias};
1539
System.err.println(form.format(source));
1540
if (orig == null) {
1541
System.err.print(rb.getString
1542
(".RETURN.if.same.as.keystore.password."));
1543
} else {
1544
form = new MessageFormat(rb.getString
1545
(".RETURN.if.same.as.for.otherAlias."));
1546
Object[] src = {orig};
1547
System.err.print(form.format(src));
1548
}
1549
System.err.flush();
1550
char[] entered = Password.readPassword(System.in);
1551
passwords.add(entered);
1552
if (entered == null) {
1553
return origPass;
1554
} else if (entered.length >= 6) {
1555
System.err.print(rb.getString("Re.enter.new.password."));
1556
char[] passAgain = Password.readPassword(System.in);
1557
passwords.add(passAgain);
1558
if (!Arrays.equals(entered, passAgain)) {
1559
System.err.println
1560
(rb.getString("They.don.t.match.Try.again"));
1561
continue;
1562
}
1563
return entered;
1564
} else {
1565
System.err.println(rb.getString
1566
("Key.password.is.too.short.must.be.at.least.6.characters"));
1567
}
1568
}
1569
if (count == 3) {
1570
if (command == KEYCLONE) {
1571
throw new Exception(rb.getString
1572
("Too.many.failures.Key.entry.not.cloned"));
1573
} else {
1574
throw new Exception(rb.getString
1575
("Too.many.failures.key.not.added.to.keystore"));
1576
}
1577
}
1578
}
1579
return null; // PKCS11, MSCAPI, or -protected
1580
}
1581
1582
/*
1583
* Prompt the user for the password credential to be stored.
1584
*/
1585
private char[] promptForCredential() throws Exception {
1586
// Handle password supplied via stdin
1587
if (System.console() == null) {
1588
char[] importPass = Password.readPassword(System.in);
1589
passwords.add(importPass);
1590
return importPass;
1591
}
1592
1593
int count;
1594
for (count = 0; count < 3; count++) {
1595
System.err.print(
1596
rb.getString("Enter.the.password.to.be.stored."));
1597
System.err.flush();
1598
char[] entered = Password.readPassword(System.in);
1599
passwords.add(entered);
1600
System.err.print(rb.getString("Re.enter.password."));
1601
char[] passAgain = Password.readPassword(System.in);
1602
passwords.add(passAgain);
1603
if (!Arrays.equals(entered, passAgain)) {
1604
System.err.println(rb.getString("They.don.t.match.Try.again"));
1605
continue;
1606
}
1607
return entered;
1608
}
1609
1610
if (count == 3) {
1611
throw new Exception(rb.getString
1612
("Too.many.failures.key.not.added.to.keystore"));
1613
}
1614
1615
return null;
1616
}
1617
1618
/**
1619
* Creates a new secret key.
1620
*/
1621
private void doGenSecretKey(String alias, String keyAlgName,
1622
int keysize)
1623
throws Exception
1624
{
1625
if (alias == null) {
1626
alias = keyAlias;
1627
}
1628
if (keyStore.containsAlias(alias)) {
1629
MessageFormat form = new MessageFormat(rb.getString
1630
("Secret.key.not.generated.alias.alias.already.exists"));
1631
Object[] source = {alias};
1632
throw new Exception(form.format(source));
1633
}
1634
1635
// Use the keystore's default PBE algorithm for entry protection
1636
boolean useDefaultPBEAlgorithm = true;
1637
SecretKey secKey = null;
1638
1639
if (keyAlgName.toUpperCase(Locale.ENGLISH).startsWith("PBE")) {
1640
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBE");
1641
1642
// User is prompted for PBE credential
1643
secKey =
1644
factory.generateSecret(new PBEKeySpec(promptForCredential()));
1645
1646
// Check whether a specific PBE algorithm was specified
1647
if (!"PBE".equalsIgnoreCase(keyAlgName)) {
1648
useDefaultPBEAlgorithm = false;
1649
}
1650
1651
if (verbose) {
1652
MessageFormat form = new MessageFormat(rb.getString(
1653
"Generated.keyAlgName.secret.key"));
1654
Object[] source =
1655
{useDefaultPBEAlgorithm ? "PBE" : secKey.getAlgorithm()};
1656
System.err.println(form.format(source));
1657
}
1658
} else {
1659
KeyGenerator keygen = KeyGenerator.getInstance(keyAlgName);
1660
if (keysize == -1) {
1661
if ("DES".equalsIgnoreCase(keyAlgName)) {
1662
keysize = 56;
1663
} else if ("DESede".equalsIgnoreCase(keyAlgName)) {
1664
keysize = 168;
1665
} else {
1666
throw new Exception(rb.getString
1667
("Please.provide.keysize.for.secret.key.generation"));
1668
}
1669
}
1670
keygen.init(keysize);
1671
secKey = keygen.generateKey();
1672
1673
if (verbose) {
1674
MessageFormat form = new MessageFormat(rb.getString
1675
("Generated.keysize.bit.keyAlgName.secret.key"));
1676
Object[] source = {new Integer(keysize),
1677
secKey.getAlgorithm()};
1678
System.err.println(form.format(source));
1679
}
1680
}
1681
1682
if (keyPass == null) {
1683
keyPass = promptForKeyPass(alias, null, storePass);
1684
}
1685
1686
if (useDefaultPBEAlgorithm) {
1687
keyStore.setKeyEntry(alias, secKey, keyPass, null);
1688
} else {
1689
keyStore.setEntry(alias, new KeyStore.SecretKeyEntry(secKey),
1690
new KeyStore.PasswordProtection(keyPass, keyAlgName, null));
1691
}
1692
}
1693
1694
/**
1695
* If no signature algorithm was specified at the command line,
1696
* we choose one that is compatible with the selected private key
1697
*/
1698
private static String getCompatibleSigAlgName(String keyAlgName)
1699
throws Exception {
1700
if ("DSA".equalsIgnoreCase(keyAlgName)) {
1701
return "SHA256WithDSA";
1702
} else if ("RSA".equalsIgnoreCase(keyAlgName)) {
1703
return "SHA256WithRSA";
1704
} else if ("EC".equalsIgnoreCase(keyAlgName)) {
1705
return "SHA256withECDSA";
1706
} else {
1707
throw new Exception(rb.getString
1708
("Cannot.derive.signature.algorithm"));
1709
}
1710
}
1711
/**
1712
* Creates a new key pair and self-signed certificate.
1713
*/
1714
private void doGenKeyPair(String alias, String dname, String keyAlgName,
1715
int keysize, String sigAlgName)
1716
throws Exception
1717
{
1718
if (keysize == -1) {
1719
if ("EC".equalsIgnoreCase(keyAlgName)) {
1720
keysize = SecurityProviderConstants.DEF_EC_KEY_SIZE;
1721
} else if ("RSA".equalsIgnoreCase(keyAlgName)) {
1722
keysize = SecurityProviderConstants.DEF_RSA_KEY_SIZE;
1723
} else if ("RSASSA-PSS".equalsIgnoreCase(keyAlgName)) {
1724
keysize = SecurityProviderConstants.DEF_RSASSA_PSS_KEY_SIZE;
1725
} else if ("DSA".equalsIgnoreCase(keyAlgName)) {
1726
keysize = SecurityProviderConstants.DEF_DSA_KEY_SIZE;
1727
}
1728
}
1729
1730
if (alias == null) {
1731
alias = keyAlias;
1732
}
1733
1734
if (keyStore.containsAlias(alias)) {
1735
MessageFormat form = new MessageFormat(rb.getString
1736
("Key.pair.not.generated.alias.alias.already.exists"));
1737
Object[] source = {alias};
1738
throw new Exception(form.format(source));
1739
}
1740
1741
if (sigAlgName == null) {
1742
sigAlgName = getCompatibleSigAlgName(keyAlgName);
1743
}
1744
CertAndKeyGen keypair =
1745
new CertAndKeyGen(keyAlgName, sigAlgName, providerName);
1746
1747
1748
// If DN is provided, parse it. Otherwise, prompt the user for it.
1749
X500Name x500Name;
1750
if (dname == null) {
1751
x500Name = getX500Name();
1752
} else {
1753
x500Name = new X500Name(dname);
1754
}
1755
1756
keypair.generate(keysize);
1757
PrivateKey privKey = keypair.getPrivateKey();
1758
1759
CertificateExtensions ext = createV3Extensions(
1760
null,
1761
null,
1762
v3ext,
1763
keypair.getPublicKeyAnyway(),
1764
null);
1765
1766
X509Certificate[] chain = new X509Certificate[1];
1767
chain[0] = keypair.getSelfCertificate(
1768
x500Name, getStartDate(startDate), validity*24L*60L*60L, ext);
1769
1770
if (verbose) {
1771
MessageFormat form = new MessageFormat(rb.getString
1772
("Generating.keysize.bit.keyAlgName.key.pair.and.self.signed.certificate.sigAlgName.with.a.validity.of.validality.days.for"));
1773
Object[] source = {new Integer(keysize),
1774
privKey.getAlgorithm(),
1775
chain[0].getSigAlgName(),
1776
new Long(validity),
1777
x500Name};
1778
System.err.println(form.format(source));
1779
}
1780
1781
if (keyPass == null) {
1782
keyPass = promptForKeyPass(alias, null, storePass);
1783
}
1784
checkWeak(rb.getString("the.generated.certificate"), chain[0]);
1785
keyStore.setKeyEntry(alias, privKey, keyPass, chain);
1786
}
1787
1788
/**
1789
* Clones an entry
1790
* @param orig original alias
1791
* @param dest destination alias
1792
* @changePassword if the password can be changed
1793
*/
1794
private void doCloneEntry(String orig, String dest, boolean changePassword)
1795
throws Exception
1796
{
1797
if (orig == null) {
1798
orig = keyAlias;
1799
}
1800
1801
if (keyStore.containsAlias(dest)) {
1802
MessageFormat form = new MessageFormat
1803
(rb.getString("Destination.alias.dest.already.exists"));
1804
Object[] source = {dest};
1805
throw new Exception(form.format(source));
1806
}
1807
1808
Pair<Entry,char[]> objs = recoverEntry(keyStore, orig, storePass, keyPass);
1809
Entry entry = objs.fst;
1810
keyPass = objs.snd;
1811
1812
PasswordProtection pp = null;
1813
1814
if (keyPass != null) { // protected
1815
if (!changePassword || P12KEYSTORE.equalsIgnoreCase(storetype)) {
1816
keyPassNew = keyPass;
1817
} else {
1818
if (keyPassNew == null) {
1819
keyPassNew = promptForKeyPass(dest, orig, keyPass);
1820
}
1821
}
1822
pp = new PasswordProtection(keyPassNew);
1823
}
1824
keyStore.setEntry(dest, entry, pp);
1825
}
1826
1827
/**
1828
* Changes a key password.
1829
*/
1830
private void doChangeKeyPasswd(String alias) throws Exception
1831
{
1832
1833
if (alias == null) {
1834
alias = keyAlias;
1835
}
1836
Pair<Key,char[]> objs = recoverKey(alias, storePass, keyPass);
1837
Key privKey = objs.fst;
1838
if (keyPass == null) {
1839
keyPass = objs.snd;
1840
}
1841
1842
if (keyPassNew == null) {
1843
MessageFormat form = new MessageFormat
1844
(rb.getString("key.password.for.alias."));
1845
Object[] source = {alias};
1846
keyPassNew = getNewPasswd(form.format(source), keyPass);
1847
}
1848
keyStore.setKeyEntry(alias, privKey, keyPassNew,
1849
keyStore.getCertificateChain(alias));
1850
}
1851
1852
/**
1853
* Imports a JDK 1.1-style identity database. We can only store one
1854
* certificate per identity, because we use the identity's name as the
1855
* alias (which references a keystore entry), and aliases must be unique.
1856
*/
1857
private void doImportIdentityDatabase(InputStream in)
1858
throws Exception
1859
{
1860
System.err.println(rb.getString
1861
("No.entries.from.identity.database.added"));
1862
}
1863
1864
/**
1865
* Prints a single keystore entry.
1866
*/
1867
private void doPrintEntry(String label, String alias, PrintStream out)
1868
throws Exception
1869
{
1870
if (keyStore.containsAlias(alias) == false) {
1871
MessageFormat form = new MessageFormat
1872
(rb.getString("Alias.alias.does.not.exist"));
1873
Object[] source = {alias};
1874
throw new Exception(form.format(source));
1875
}
1876
1877
if (verbose || rfc || debug) {
1878
MessageFormat form = new MessageFormat
1879
(rb.getString("Alias.name.alias"));
1880
Object[] source = {alias};
1881
out.println(form.format(source));
1882
1883
if (!token) {
1884
form = new MessageFormat(rb.getString
1885
("Creation.date.keyStore.getCreationDate.alias."));
1886
Object[] src = {keyStore.getCreationDate(alias)};
1887
out.println(form.format(src));
1888
}
1889
} else {
1890
if (!token) {
1891
MessageFormat form = new MessageFormat
1892
(rb.getString("alias.keyStore.getCreationDate.alias."));
1893
Object[] source = {alias, keyStore.getCreationDate(alias)};
1894
out.print(form.format(source));
1895
} else {
1896
MessageFormat form = new MessageFormat
1897
(rb.getString("alias."));
1898
Object[] source = {alias};
1899
out.print(form.format(source));
1900
}
1901
}
1902
1903
if (keyStore.entryInstanceOf(alias, KeyStore.SecretKeyEntry.class)) {
1904
if (verbose || rfc || debug) {
1905
Object[] source = {"SecretKeyEntry"};
1906
out.println(new MessageFormat(
1907
rb.getString("Entry.type.type.")).format(source));
1908
} else {
1909
out.println("SecretKeyEntry, ");
1910
}
1911
} else if (keyStore.entryInstanceOf(alias, KeyStore.PrivateKeyEntry.class)) {
1912
if (verbose || rfc || debug) {
1913
Object[] source = {"PrivateKeyEntry"};
1914
out.println(new MessageFormat(
1915
rb.getString("Entry.type.type.")).format(source));
1916
} else {
1917
out.println("PrivateKeyEntry, ");
1918
}
1919
1920
// Get the chain
1921
Certificate[] chain = keyStore.getCertificateChain(alias);
1922
if (chain != null) {
1923
if (verbose || rfc || debug) {
1924
out.println(rb.getString
1925
("Certificate.chain.length.") + chain.length);
1926
for (int i = 0; i < chain.length; i ++) {
1927
MessageFormat form = new MessageFormat
1928
(rb.getString("Certificate.i.1."));
1929
Object[] source = {new Integer((i + 1))};
1930
out.println(form.format(source));
1931
if (verbose && (chain[i] instanceof X509Certificate)) {
1932
printX509Cert((X509Certificate)(chain[i]), out);
1933
} else if (debug) {
1934
out.println(chain[i].toString());
1935
} else {
1936
dumpCert(chain[i], out);
1937
}
1938
checkWeak(label, chain[i]);
1939
}
1940
} else {
1941
// Print the digest of the user cert only
1942
out.println
1943
(rb.getString("Certificate.fingerprint.SHA.256.") +
1944
getCertFingerPrint("SHA-256", chain[0]));
1945
checkWeak(label, chain[0]);
1946
}
1947
}
1948
} else if (keyStore.entryInstanceOf(alias,
1949
KeyStore.TrustedCertificateEntry.class)) {
1950
// We have a trusted certificate entry
1951
Certificate cert = keyStore.getCertificate(alias);
1952
Object[] source = {"trustedCertEntry"};
1953
String mf = new MessageFormat(
1954
rb.getString("Entry.type.type.")).format(source) + "\n";
1955
if (verbose && (cert instanceof X509Certificate)) {
1956
out.println(mf);
1957
printX509Cert((X509Certificate)cert, out);
1958
} else if (rfc) {
1959
out.println(mf);
1960
dumpCert(cert, out);
1961
} else if (debug) {
1962
out.println(cert.toString());
1963
} else {
1964
out.println("trustedCertEntry, ");
1965
out.println(rb.getString("Certificate.fingerprint.SHA.256.")
1966
+ getCertFingerPrint("SHA-256", cert));
1967
}
1968
checkWeak(label, cert);
1969
} else {
1970
out.println(rb.getString("Unknown.Entry.Type"));
1971
}
1972
}
1973
1974
boolean inplaceImportCheck() throws Exception {
1975
if (P11KEYSTORE.equalsIgnoreCase(srcstoretype) ||
1976
KeyStoreUtil.isWindowsKeyStore(srcstoretype)) {
1977
return false;
1978
}
1979
1980
if (srcksfname != null) {
1981
File srcksfile = new File(srcksfname);
1982
if (srcksfile.exists() && srcksfile.length() == 0) {
1983
throw new Exception(rb.getString
1984
("Source.keystore.file.exists.but.is.empty.") +
1985
srcksfname);
1986
}
1987
if (srcksfile.getCanonicalFile()
1988
.equals(new File(ksfname).getCanonicalFile())) {
1989
return true;
1990
} else {
1991
// Informational, especially if destkeystore is not
1992
// provided, which default to ~/.keystore.
1993
System.err.println(String.format(rb.getString(
1994
"importing.keystore.status"), srcksfname, ksfname));
1995
return false;
1996
}
1997
} else {
1998
throw new Exception(rb.getString
1999
("Please.specify.srckeystore"));
2000
}
2001
}
2002
2003
/**
2004
* Load the srckeystore from a stream, used in -importkeystore
2005
* @returns the src KeyStore
2006
*/
2007
KeyStore loadSourceKeyStore() throws Exception {
2008
2009
InputStream is = null;
2010
File srcksfile = null;
2011
2012
if (P11KEYSTORE.equalsIgnoreCase(srcstoretype) ||
2013
KeyStoreUtil.isWindowsKeyStore(srcstoretype)) {
2014
if (!NONE.equals(srcksfname)) {
2015
System.err.println(MessageFormat.format(rb.getString
2016
(".keystore.must.be.NONE.if.storetype.is.{0}"), srcstoretype));
2017
System.err.println();
2018
tinyHelp();
2019
}
2020
} else {
2021
srcksfile = new File(srcksfname);
2022
is = new FileInputStream(srcksfile);
2023
}
2024
2025
KeyStore store;
2026
try {
2027
if (srcstoretype == null) {
2028
srcstoretype = KeyStore.getDefaultType();
2029
}
2030
if (srcProviderName == null) {
2031
store = KeyStore.getInstance(srcstoretype);
2032
} else {
2033
store = KeyStore.getInstance(srcstoretype, srcProviderName);
2034
}
2035
2036
if (srcstorePass == null
2037
&& !srcprotectedPath
2038
&& !KeyStoreUtil.isWindowsKeyStore(srcstoretype)) {
2039
System.err.print(rb.getString("Enter.source.keystore.password."));
2040
System.err.flush();
2041
srcstorePass = Password.readPassword(System.in);
2042
passwords.add(srcstorePass);
2043
}
2044
2045
// always let keypass be storepass when using pkcs12
2046
if (P12KEYSTORE.equalsIgnoreCase(srcstoretype)) {
2047
if (srckeyPass != null && srcstorePass != null &&
2048
!Arrays.equals(srcstorePass, srckeyPass)) {
2049
MessageFormat form = new MessageFormat(rb.getString(
2050
"Warning.Different.store.and.key.passwords.not.supported.for.PKCS12.KeyStores.Ignoring.user.specified.command.value."));
2051
Object[] source = {"-srckeypass"};
2052
System.err.println(form.format(source));
2053
srckeyPass = srcstorePass;
2054
}
2055
}
2056
2057
store.load(is, srcstorePass); // "is" already null in PKCS11
2058
} finally {
2059
if (is != null) {
2060
is.close();
2061
}
2062
}
2063
2064
if (srcstorePass == null
2065
&& !KeyStoreUtil.isWindowsKeyStore(srcstoretype)) {
2066
// anti refactoring, copied from printNoIntegrityWarning(),
2067
// but change 2 lines
2068
System.err.println();
2069
System.err.println(rb.getString
2070
(".WARNING.WARNING.WARNING."));
2071
System.err.println(rb.getString
2072
(".The.integrity.of.the.information.stored.in.the.srckeystore."));
2073
System.err.println(rb.getString
2074
(".WARNING.WARNING.WARNING."));
2075
System.err.println();
2076
}
2077
2078
return store;
2079
}
2080
2081
/**
2082
* import all keys and certs from importkeystore.
2083
* keep alias unchanged if no name conflict, otherwise, prompt.
2084
* keep keypass unchanged for keys
2085
*/
2086
private void doImportKeyStore(KeyStore srcKS) throws Exception {
2087
2088
if (alias != null) {
2089
doImportKeyStoreSingle(srcKS, alias);
2090
} else {
2091
if (dest != null || srckeyPass != null) {
2092
throw new Exception(rb.getString(
2093
"if.alias.not.specified.destalias.and.srckeypass.must.not.be.specified"));
2094
}
2095
doImportKeyStoreAll(srcKS);
2096
}
2097
2098
if (inplaceImport) {
2099
// Backup to file.old or file.old2...
2100
// The keystore is not rewritten yet now.
2101
for (int n = 1; /* forever */; n++) {
2102
inplaceBackupName = srcksfname + ".old" + (n == 1 ? "" : n);
2103
File bkFile = new File(inplaceBackupName);
2104
if (!bkFile.exists()) {
2105
Files.copy(Paths.get(srcksfname), bkFile.toPath());
2106
break;
2107
}
2108
}
2109
2110
}
2111
2112
/*
2113
* Information display rule of -importkeystore
2114
* 1. inside single, shows failure
2115
* 2. inside all, shows sucess
2116
* 3. inside all where there is a failure, prompt for continue
2117
* 4. at the final of all, shows summary
2118
*/
2119
}
2120
2121
/**
2122
* Import a single entry named alias from srckeystore
2123
* @returns 1 if the import action succeed
2124
* 0 if user choose to ignore an alias-dumplicated entry
2125
* 2 if setEntry throws Exception
2126
*/
2127
private int doImportKeyStoreSingle(KeyStore srckeystore, String alias)
2128
throws Exception {
2129
2130
String newAlias = (dest==null) ? alias : dest;
2131
2132
if (keyStore.containsAlias(newAlias)) {
2133
Object[] source = {alias};
2134
if (noprompt) {
2135
System.err.println(new MessageFormat(rb.getString(
2136
"Warning.Overwriting.existing.alias.alias.in.destination.keystore")).format(source));
2137
} else {
2138
String reply = getYesNoReply(new MessageFormat(rb.getString(
2139
"Existing.entry.alias.alias.exists.overwrite.no.")).format(source));
2140
if ("NO".equals(reply)) {
2141
newAlias = inputStringFromStdin(rb.getString
2142
("Enter.new.alias.name.RETURN.to.cancel.import.for.this.entry."));
2143
if ("".equals(newAlias)) {
2144
System.err.println(new MessageFormat(rb.getString(
2145
"Entry.for.alias.alias.not.imported.")).format(
2146
source));
2147
return 0;
2148
}
2149
}
2150
}
2151
}
2152
2153
Pair<Entry,char[]> objs = recoverEntry(srckeystore, alias, srcstorePass, srckeyPass);
2154
Entry entry = objs.fst;
2155
2156
PasswordProtection pp = null;
2157
2158
// According to keytool.html, "The destination entry will be protected
2159
// using destkeypass. If destkeypass is not provided, the destination
2160
// entry will be protected with the source entry password."
2161
// so always try to protect with destKeyPass.
2162
char[] newPass = null;
2163
if (destKeyPass != null) {
2164
newPass = destKeyPass;
2165
pp = new PasswordProtection(destKeyPass);
2166
} else if (objs.snd != null) {
2167
newPass = objs.snd;
2168
pp = new PasswordProtection(objs.snd);
2169
}
2170
2171
try {
2172
Certificate c = srckeystore.getCertificate(alias);
2173
if (c != null) {
2174
checkWeak("<" + newAlias + ">", c);
2175
}
2176
keyStore.setEntry(newAlias, entry, pp);
2177
// Place the check so that only successful imports are blocked.
2178
// For example, we don't block a failed SecretEntry import.
2179
if (P12KEYSTORE.equalsIgnoreCase(storetype)) {
2180
if (newPass != null && !Arrays.equals(newPass, storePass)) {
2181
throw new Exception(rb.getString(
2182
"The.destination.pkcs12.keystore.has.different.storepass.and.keypass.Please.retry.with.destkeypass.specified."));
2183
}
2184
}
2185
return 1;
2186
} catch (KeyStoreException kse) {
2187
Object[] source2 = {alias, kse.toString()};
2188
MessageFormat form = new MessageFormat(rb.getString(
2189
"Problem.importing.entry.for.alias.alias.exception.Entry.for.alias.alias.not.imported."));
2190
System.err.println(form.format(source2));
2191
return 2;
2192
}
2193
}
2194
2195
private void doImportKeyStoreAll(KeyStore srckeystore) throws Exception {
2196
2197
int ok = 0;
2198
int count = srckeystore.size();
2199
for (Enumeration<String> e = srckeystore.aliases();
2200
e.hasMoreElements(); ) {
2201
String alias = e.nextElement();
2202
int result = doImportKeyStoreSingle(srckeystore, alias);
2203
if (result == 1) {
2204
ok++;
2205
Object[] source = {alias};
2206
MessageFormat form = new MessageFormat(rb.getString("Entry.for.alias.alias.successfully.imported."));
2207
System.err.println(form.format(source));
2208
} else if (result == 2) {
2209
if (!noprompt) {
2210
String reply = getYesNoReply("Do you want to quit the import process? [no]: ");
2211
if ("YES".equals(reply)) {
2212
break;
2213
}
2214
}
2215
}
2216
}
2217
Object[] source = {ok, count-ok};
2218
MessageFormat form = new MessageFormat(rb.getString(
2219
"Import.command.completed.ok.entries.successfully.imported.fail.entries.failed.or.cancelled"));
2220
System.err.println(form.format(source));
2221
}
2222
2223
/**
2224
* Prints all keystore entries.
2225
*/
2226
private void doPrintEntries(PrintStream out)
2227
throws Exception
2228
{
2229
// Adjust displayed keystore type if needed.
2230
String keystoreTypeToPrint = keyStore.getType();
2231
if ("JKS".equalsIgnoreCase(keystoreTypeToPrint)) {
2232
if (ksfile != null && ksfile.exists()) {
2233
String realType = keyStoreType(ksfile);
2234
// If the magic number does not conform to JKS
2235
// then it must be PKCS12
2236
if (!"JKS".equalsIgnoreCase(realType)) {
2237
keystoreTypeToPrint = P12KEYSTORE;
2238
}
2239
}
2240
}
2241
out.println(rb.getString("Keystore.type.") + keystoreTypeToPrint);
2242
out.println(rb.getString("Keystore.provider.") +
2243
keyStore.getProvider().getName());
2244
out.println();
2245
2246
MessageFormat form;
2247
form = (keyStore.size() == 1) ?
2248
new MessageFormat(rb.getString
2249
("Your.keystore.contains.keyStore.size.entry")) :
2250
new MessageFormat(rb.getString
2251
("Your.keystore.contains.keyStore.size.entries"));
2252
Object[] source = {new Integer(keyStore.size())};
2253
out.println(form.format(source));
2254
out.println();
2255
2256
List<String> aliases = Collections.list(keyStore.aliases());
2257
aliases.sort(String::compareTo);
2258
for (String alias : aliases) {
2259
doPrintEntry("<" + alias + ">", alias, out);
2260
if (verbose || rfc) {
2261
out.println(rb.getString("NEWLINE"));
2262
out.println(rb.getString
2263
("STAR"));
2264
out.println(rb.getString
2265
("STARNN"));
2266
}
2267
}
2268
}
2269
2270
private static <T> Iterable<T> e2i(final Enumeration<T> e) {
2271
return new Iterable<T>() {
2272
@Override
2273
public Iterator<T> iterator() {
2274
return new Iterator<T>() {
2275
@Override
2276
public boolean hasNext() {
2277
return e.hasMoreElements();
2278
}
2279
@Override
2280
public T next() {
2281
return e.nextElement();
2282
}
2283
public void remove() {
2284
throw new UnsupportedOperationException("Not supported yet.");
2285
}
2286
};
2287
}
2288
};
2289
}
2290
2291
/**
2292
* Loads CRLs from a source. This method is also called in JarSigner.
2293
* @param src the source, which means System.in if null, or a URI,
2294
* or a bare file path name
2295
*/
2296
public static Collection<? extends CRL> loadCRLs(String src) throws Exception {
2297
InputStream in = null;
2298
URI uri = null;
2299
if (src == null) {
2300
in = System.in;
2301
} else {
2302
try {
2303
uri = new URI(src);
2304
if (uri.getScheme().equals("ldap")) {
2305
// No input stream for LDAP
2306
} else {
2307
in = uri.toURL().openStream();
2308
}
2309
} catch (Exception e) {
2310
try {
2311
in = new FileInputStream(src);
2312
} catch (Exception e2) {
2313
if (uri == null || uri.getScheme() == null) {
2314
throw e2; // More likely a bare file path
2315
} else {
2316
throw e; // More likely a protocol or network problem
2317
}
2318
}
2319
}
2320
}
2321
if (in != null) {
2322
try {
2323
// Read the full stream before feeding to X509Factory,
2324
// otherwise, keytool -gencrl | keytool -printcrl
2325
// might not work properly, since -gencrl is slow
2326
// and there's no data in the pipe at the beginning.
2327
ByteArrayOutputStream bout = new ByteArrayOutputStream();
2328
byte[] b = new byte[4096];
2329
while (true) {
2330
int len = in.read(b);
2331
if (len < 0) break;
2332
bout.write(b, 0, len);
2333
}
2334
return CertificateFactory.getInstance("X509").generateCRLs(
2335
new ByteArrayInputStream(bout.toByteArray()));
2336
} finally {
2337
if (in != System.in) {
2338
in.close();
2339
}
2340
}
2341
} else { // must be LDAP, and uri is not null
2342
// Lazily load LDAPCertStoreHelper if present
2343
CertStoreHelper helper = CertStoreHelper.getInstance("LDAP");
2344
String path = uri.getPath();
2345
if (path.charAt(0) == '/') path = path.substring(1);
2346
CertStore s = helper.getCertStore(uri);
2347
X509CRLSelector sel =
2348
helper.wrap(new X509CRLSelector(), null, path);
2349
return s.getCRLs(sel);
2350
}
2351
}
2352
2353
/**
2354
* Returns CRLs described in a X509Certificate's CRLDistributionPoints
2355
* Extension. Only those containing a general name of type URI are read.
2356
*/
2357
public static List<CRL> readCRLsFromCert(X509Certificate cert)
2358
throws Exception {
2359
List<CRL> crls = new ArrayList<>();
2360
CRLDistributionPointsExtension ext =
2361
X509CertImpl.toImpl(cert).getCRLDistributionPointsExtension();
2362
if (ext == null) return crls;
2363
List<DistributionPoint> distPoints =
2364
ext.get(CRLDistributionPointsExtension.POINTS);
2365
for (DistributionPoint o: distPoints) {
2366
GeneralNames names = o.getFullName();
2367
if (names != null) {
2368
for (GeneralName name: names.names()) {
2369
if (name.getType() == GeneralNameInterface.NAME_URI) {
2370
URIName uriName = (URIName)name.getName();
2371
for (CRL crl: loadCRLs(uriName.getName())) {
2372
if (crl instanceof X509CRL) {
2373
crls.add((X509CRL)crl);
2374
}
2375
}
2376
break; // Different name should point to same CRL
2377
}
2378
}
2379
}
2380
}
2381
return crls;
2382
}
2383
2384
private static String verifyCRL(KeyStore ks, CRL crl)
2385
throws Exception {
2386
X509CRLImpl xcrl = (X509CRLImpl)crl;
2387
X500Principal issuer = xcrl.getIssuerX500Principal();
2388
for (String s: e2i(ks.aliases())) {
2389
Certificate cert = ks.getCertificate(s);
2390
if (cert instanceof X509Certificate) {
2391
X509Certificate xcert = (X509Certificate)cert;
2392
if (xcert.getSubjectX500Principal().equals(issuer)) {
2393
try {
2394
((X509CRLImpl)crl).verify(cert.getPublicKey());
2395
return s;
2396
} catch (Exception e) {
2397
}
2398
}
2399
}
2400
}
2401
return null;
2402
}
2403
2404
private void doPrintCRL(String src, PrintStream out)
2405
throws Exception {
2406
for (CRL crl: loadCRLs(src)) {
2407
printCRL(crl, out);
2408
String issuer = null;
2409
Certificate signer = null;
2410
if (caks != null) {
2411
issuer = verifyCRL(caks, crl);
2412
if (issuer != null) {
2413
signer = caks.getCertificate(issuer);
2414
out.printf(rb.getString(
2415
"verified.by.s.in.s.weak"),
2416
issuer,
2417
"cacerts",
2418
withWeak(signer.getPublicKey()));
2419
out.println();
2420
}
2421
}
2422
if (issuer == null && keyStore != null) {
2423
issuer = verifyCRL(keyStore, crl);
2424
if (issuer != null) {
2425
signer = keyStore.getCertificate(issuer);
2426
out.printf(rb.getString(
2427
"verified.by.s.in.s.weak"),
2428
issuer,
2429
"keystore",
2430
withWeak(signer.getPublicKey()));
2431
out.println();
2432
}
2433
}
2434
if (issuer == null) {
2435
out.println(rb.getString
2436
("STAR"));
2437
out.println(rb.getString
2438
("warning.not.verified.make.sure.keystore.is.correct"));
2439
out.println(rb.getString
2440
("STARNN"));
2441
}
2442
checkWeak(rb.getString("the.crl"), crl, signer == null ? null : signer.getPublicKey());
2443
}
2444
}
2445
2446
private void printCRL(CRL crl, PrintStream out)
2447
throws Exception {
2448
X509CRL xcrl = (X509CRL)crl;
2449
if (rfc) {
2450
out.println("-----BEGIN X509 CRL-----");
2451
out.println(Base64.getMimeEncoder(64, CRLF).encodeToString(xcrl.getEncoded()));
2452
out.println("-----END X509 CRL-----");
2453
} else {
2454
String s;
2455
if (crl instanceof X509CRLImpl) {
2456
X509CRLImpl x509crl = (X509CRLImpl) crl;
2457
s = x509crl.toStringWithAlgName(withWeak("" + x509crl.getSigAlgId()));
2458
} else {
2459
s = crl.toString();
2460
}
2461
out.println(s);
2462
}
2463
}
2464
2465
private void doPrintCertReq(InputStream in, PrintStream out)
2466
throws Exception {
2467
2468
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
2469
StringBuffer sb = new StringBuffer();
2470
boolean started = false;
2471
while (true) {
2472
String s = reader.readLine();
2473
if (s == null) break;
2474
if (!started) {
2475
if (s.startsWith("-----")) {
2476
started = true;
2477
}
2478
} else {
2479
if (s.startsWith("-----")) {
2480
break;
2481
}
2482
sb.append(s);
2483
}
2484
}
2485
PKCS10 req = new PKCS10(Pem.decode(new String(sb)));
2486
2487
PublicKey pkey = req.getSubjectPublicKeyInfo();
2488
out.printf(rb.getString("PKCS.10.with.weak"),
2489
req.getSubjectName(),
2490
pkey.getFormat(),
2491
withWeak(pkey),
2492
withWeak(req.getSigAlg()));
2493
for (PKCS10Attribute attr: req.getAttributes().getAttributes()) {
2494
ObjectIdentifier oid = attr.getAttributeId();
2495
if (oid.equals((Object)PKCS9Attribute.EXTENSION_REQUEST_OID)) {
2496
CertificateExtensions exts = (CertificateExtensions)attr.getAttributeValue();
2497
if (exts != null) {
2498
printExtensions(rb.getString("Extension.Request."), exts, out);
2499
}
2500
} else {
2501
out.println("Attribute: " + attr.getAttributeId());
2502
PKCS9Attribute pkcs9Attr =
2503
new PKCS9Attribute(attr.getAttributeId(),
2504
attr.getAttributeValue());
2505
out.print(pkcs9Attr.getName() + ": ");
2506
Object attrVal = attr.getAttributeValue();
2507
out.println(attrVal instanceof String[] ?
2508
Arrays.toString((String[]) attrVal) :
2509
attrVal);
2510
}
2511
}
2512
if (debug) {
2513
out.println(req); // Just to see more, say, public key length...
2514
}
2515
checkWeak(rb.getString("the.certificate.request"), req);
2516
}
2517
2518
/**
2519
* Reads a certificate (or certificate chain) and prints its contents in
2520
* a human readable format.
2521
*/
2522
private void printCertFromStream(InputStream in, PrintStream out)
2523
throws Exception
2524
{
2525
Collection<? extends Certificate> c = null;
2526
try {
2527
c = cf.generateCertificates(in);
2528
} catch (CertificateException ce) {
2529
throw new Exception(rb.getString("Failed.to.parse.input"), ce);
2530
}
2531
if (c.isEmpty()) {
2532
throw new Exception(rb.getString("Empty.input"));
2533
}
2534
Certificate[] certs = c.toArray(new Certificate[c.size()]);
2535
for (int i=0; i<certs.length; i++) {
2536
X509Certificate x509Cert = null;
2537
try {
2538
x509Cert = (X509Certificate)certs[i];
2539
} catch (ClassCastException cce) {
2540
throw new Exception(rb.getString("Not.X.509.certificate"));
2541
}
2542
if (certs.length > 1) {
2543
MessageFormat form = new MessageFormat
2544
(rb.getString("Certificate.i.1."));
2545
Object[] source = {new Integer(i + 1)};
2546
out.println(form.format(source));
2547
}
2548
if (rfc)
2549
dumpCert(x509Cert, out);
2550
else
2551
printX509Cert(x509Cert, out);
2552
if (i < (certs.length-1)) {
2553
out.println();
2554
}
2555
checkWeak(oneInMany(rb.getString("the.certificate"), i, certs.length), x509Cert);
2556
}
2557
}
2558
2559
private static String oneInMany(String label, int i, int num) {
2560
if (num == 1) {
2561
return label;
2562
} else {
2563
return String.format(rb.getString("one.in.many"), label, i+1, num);
2564
}
2565
}
2566
2567
private void doPrintCert(final PrintStream out) throws Exception {
2568
if (jarfile != null) {
2569
JarFile jf = new JarFile(jarfile, true);
2570
Enumeration<JarEntry> entries = jf.entries();
2571
Set<CodeSigner> ss = new HashSet<>();
2572
byte[] buffer = new byte[8192];
2573
int pos = 0;
2574
while (entries.hasMoreElements()) {
2575
JarEntry je = entries.nextElement();
2576
try (InputStream is = jf.getInputStream(je)) {
2577
while (is.read(buffer) != -1) {
2578
// we just read. this will throw a SecurityException
2579
// if a signature/digest check fails. This also
2580
// populate the signers
2581
}
2582
}
2583
CodeSigner[] signers = je.getCodeSigners();
2584
if (signers != null) {
2585
for (CodeSigner signer: signers) {
2586
if (!ss.contains(signer)) {
2587
ss.add(signer);
2588
out.printf(rb.getString("Signer.d."), ++pos);
2589
out.println();
2590
out.println();
2591
out.println(rb.getString("Signature."));
2592
out.println();
2593
2594
List<? extends Certificate> certs
2595
= signer.getSignerCertPath().getCertificates();
2596
int cc = 0;
2597
for (Certificate cert: certs) {
2598
X509Certificate x = (X509Certificate)cert;
2599
if (rfc) {
2600
out.println(rb.getString("Certificate.owner.") + x.getSubjectDN() + "\n");
2601
dumpCert(x, out);
2602
} else {
2603
printX509Cert(x, out);
2604
}
2605
out.println();
2606
checkWeak(oneInMany(rb.getString("the.certificate"), cc++, certs.size()), x);
2607
}
2608
Timestamp ts = signer.getTimestamp();
2609
if (ts != null) {
2610
out.println(rb.getString("Timestamp."));
2611
out.println();
2612
certs = ts.getSignerCertPath().getCertificates();
2613
cc = 0;
2614
for (Certificate cert: certs) {
2615
X509Certificate x = (X509Certificate)cert;
2616
if (rfc) {
2617
out.println(rb.getString("Certificate.owner.") + x.getSubjectDN() + "\n");
2618
dumpCert(x, out);
2619
} else {
2620
printX509Cert(x, out);
2621
}
2622
out.println();
2623
checkWeak(oneInMany(rb.getString("the.tsa.certificate"), cc++, certs.size()), x);
2624
}
2625
}
2626
}
2627
}
2628
}
2629
}
2630
jf.close();
2631
if (ss.isEmpty()) {
2632
out.println(rb.getString("Not.a.signed.jar.file"));
2633
}
2634
} else if (sslserver != null) {
2635
// Lazily load SSLCertStoreHelper if present
2636
CertStoreHelper helper = CertStoreHelper.getInstance("SSLServer");
2637
CertStore cs = helper.getCertStore(new URI("https://" + sslserver));
2638
Collection<? extends Certificate> chain;
2639
try {
2640
chain = cs.getCertificates(null);
2641
if (chain.isEmpty()) {
2642
// If the certs are not retrieved, we consider it an error
2643
// even if the URL connection is successful.
2644
throw new Exception(rb.getString(
2645
"No.certificate.from.the.SSL.server"));
2646
}
2647
} catch (CertStoreException cse) {
2648
if (cse.getCause() instanceof IOException) {
2649
throw new Exception(rb.getString(
2650
"No.certificate.from.the.SSL.server"),
2651
cse.getCause());
2652
} else {
2653
throw cse;
2654
}
2655
}
2656
2657
int i = 0;
2658
for (Certificate cert : chain) {
2659
try {
2660
if (rfc) {
2661
dumpCert(cert, out);
2662
} else {
2663
out.println("Certificate #" + i);
2664
out.println("====================================");
2665
printX509Cert((X509Certificate)cert, out);
2666
out.println();
2667
}
2668
checkWeak(oneInMany(rb.getString("the.certificate"), i++, chain.size()), cert);
2669
} catch (Exception e) {
2670
if (debug) {
2671
e.printStackTrace();
2672
}
2673
}
2674
}
2675
} else {
2676
if (filename != null) {
2677
try (FileInputStream inStream = new FileInputStream(filename)) {
2678
printCertFromStream(inStream, out);
2679
}
2680
} else {
2681
printCertFromStream(System.in, out);
2682
}
2683
}
2684
}
2685
/**
2686
* Creates a self-signed certificate, and stores it as a single-element
2687
* certificate chain.
2688
*/
2689
private void doSelfCert(String alias, String dname, String sigAlgName)
2690
throws Exception
2691
{
2692
if (alias == null) {
2693
alias = keyAlias;
2694
}
2695
2696
Pair<Key,char[]> objs = recoverKey(alias, storePass, keyPass);
2697
PrivateKey privKey = (PrivateKey)objs.fst;
2698
if (keyPass == null)
2699
keyPass = objs.snd;
2700
2701
// Determine the signature algorithm
2702
if (sigAlgName == null) {
2703
sigAlgName = getCompatibleSigAlgName(privKey.getAlgorithm());
2704
}
2705
2706
// Get the old certificate
2707
Certificate oldCert = keyStore.getCertificate(alias);
2708
if (oldCert == null) {
2709
MessageFormat form = new MessageFormat
2710
(rb.getString("alias.has.no.public.key"));
2711
Object[] source = {alias};
2712
throw new Exception(form.format(source));
2713
}
2714
if (!(oldCert instanceof X509Certificate)) {
2715
MessageFormat form = new MessageFormat
2716
(rb.getString("alias.has.no.X.509.certificate"));
2717
Object[] source = {alias};
2718
throw new Exception(form.format(source));
2719
}
2720
2721
// convert to X509CertImpl, so that we can modify selected fields
2722
// (no public APIs available yet)
2723
byte[] encoded = oldCert.getEncoded();
2724
X509CertImpl certImpl = new X509CertImpl(encoded);
2725
X509CertInfo certInfo = (X509CertInfo)certImpl.get(X509CertImpl.NAME
2726
+ "." +
2727
X509CertImpl.INFO);
2728
2729
// Extend its validity
2730
Date firstDate = getStartDate(startDate);
2731
Date lastDate = new Date();
2732
lastDate.setTime(firstDate.getTime() + validity*1000L*24L*60L*60L);
2733
CertificateValidity interval = new CertificateValidity(firstDate,
2734
lastDate);
2735
certInfo.set(X509CertInfo.VALIDITY, interval);
2736
2737
// Make new serial number
2738
certInfo.set(X509CertInfo.SERIAL_NUMBER, new CertificateSerialNumber(
2739
new java.util.Random().nextInt() & 0x7fffffff));
2740
2741
// Set owner and issuer fields
2742
X500Name owner;
2743
if (dname == null) {
2744
// Get the owner name from the certificate
2745
owner = (X500Name)certInfo.get(X509CertInfo.SUBJECT + "." +
2746
X509CertInfo.DN_NAME);
2747
} else {
2748
// Use the owner name specified at the command line
2749
owner = new X500Name(dname);
2750
certInfo.set(X509CertInfo.SUBJECT + "." +
2751
X509CertInfo.DN_NAME, owner);
2752
}
2753
// Make issuer same as owner (self-signed!)
2754
certInfo.set(X509CertInfo.ISSUER + "." +
2755
X509CertInfo.DN_NAME, owner);
2756
2757
// The inner and outer signature algorithms have to match.
2758
// The way we achieve that is really ugly, but there seems to be no
2759
// other solution: We first sign the cert, then retrieve the
2760
// outer sigalg and use it to set the inner sigalg
2761
X509CertImpl newCert = new X509CertImpl(certInfo);
2762
AlgorithmParameterSpec params = AlgorithmId
2763
.getDefaultAlgorithmParameterSpec(sigAlgName, privKey);
2764
newCert.sign(privKey, params, sigAlgName, null);
2765
AlgorithmId sigAlgid = (AlgorithmId)newCert.get(X509CertImpl.SIG_ALG);
2766
certInfo.set(CertificateAlgorithmId.NAME + "." +
2767
CertificateAlgorithmId.ALGORITHM, sigAlgid);
2768
2769
certInfo.set(X509CertInfo.VERSION,
2770
new CertificateVersion(CertificateVersion.V3));
2771
2772
CertificateExtensions ext = createV3Extensions(
2773
null,
2774
(CertificateExtensions)certInfo.get(X509CertInfo.EXTENSIONS),
2775
v3ext,
2776
oldCert.getPublicKey(),
2777
null);
2778
certInfo.set(X509CertInfo.EXTENSIONS, ext);
2779
// Sign the new certificate
2780
newCert = new X509CertImpl(certInfo);
2781
newCert.sign(privKey, params, sigAlgName, null);
2782
2783
// Store the new certificate as a single-element certificate chain
2784
keyStore.setKeyEntry(alias, privKey,
2785
(keyPass != null) ? keyPass : storePass,
2786
new Certificate[] { newCert } );
2787
2788
if (verbose) {
2789
System.err.println(rb.getString("New.certificate.self.signed."));
2790
System.err.print(newCert.toString());
2791
System.err.println();
2792
}
2793
}
2794
2795
/**
2796
* Processes a certificate reply from a certificate authority.
2797
*
2798
* <p>Builds a certificate chain on top of the certificate reply,
2799
* using trusted certificates from the keystore. The chain is complete
2800
* after a self-signed certificate has been encountered. The self-signed
2801
* certificate is considered a root certificate authority, and is stored
2802
* at the end of the chain.
2803
*
2804
* <p>The newly generated chain replaces the old chain associated with the
2805
* key entry.
2806
*
2807
* @return true if the certificate reply was installed, otherwise false.
2808
*/
2809
private boolean installReply(String alias, InputStream in)
2810
throws Exception
2811
{
2812
if (alias == null) {
2813
alias = keyAlias;
2814
}
2815
2816
Pair<Key,char[]> objs = recoverKey(alias, storePass, keyPass);
2817
PrivateKey privKey = (PrivateKey)objs.fst;
2818
if (keyPass == null) {
2819
keyPass = objs.snd;
2820
}
2821
2822
Certificate userCert = keyStore.getCertificate(alias);
2823
if (userCert == null) {
2824
MessageFormat form = new MessageFormat
2825
(rb.getString("alias.has.no.public.key.certificate."));
2826
Object[] source = {alias};
2827
throw new Exception(form.format(source));
2828
}
2829
2830
// Read the certificates in the reply
2831
Collection<? extends Certificate> c = cf.generateCertificates(in);
2832
if (c.isEmpty()) {
2833
throw new Exception(rb.getString("Reply.has.no.certificates"));
2834
}
2835
Certificate[] replyCerts = c.toArray(new Certificate[c.size()]);
2836
Certificate[] newChain;
2837
if (replyCerts.length == 1) {
2838
// single-cert reply
2839
newChain = establishCertChain(userCert, replyCerts[0]);
2840
} else {
2841
// cert-chain reply (e.g., PKCS#7)
2842
newChain = validateReply(alias, userCert, replyCerts);
2843
}
2844
2845
// Now store the newly established chain in the keystore. The new
2846
// chain replaces the old one. The chain can be null if user chooses no.
2847
if (newChain != null) {
2848
keyStore.setKeyEntry(alias, privKey,
2849
(keyPass != null) ? keyPass : storePass,
2850
newChain);
2851
return true;
2852
} else {
2853
return false;
2854
}
2855
}
2856
2857
/**
2858
* Imports a certificate and adds it to the list of trusted certificates.
2859
*
2860
* @return true if the certificate was added, otherwise false.
2861
*/
2862
private boolean addTrustedCert(String alias, InputStream in)
2863
throws Exception
2864
{
2865
if (alias == null) {
2866
throw new Exception(rb.getString("Must.specify.alias"));
2867
}
2868
if (keyStore.containsAlias(alias)) {
2869
MessageFormat form = new MessageFormat(rb.getString
2870
("Certificate.not.imported.alias.alias.already.exists"));
2871
Object[] source = {alias};
2872
throw new Exception(form.format(source));
2873
}
2874
2875
// Read the certificate
2876
X509Certificate cert = null;
2877
try {
2878
cert = (X509Certificate)cf.generateCertificate(in);
2879
} catch (ClassCastException | CertificateException ce) {
2880
throw new Exception(rb.getString("Input.not.an.X.509.certificate"));
2881
}
2882
2883
if (noprompt) {
2884
checkWeak(rb.getString("the.input"), cert);
2885
keyStore.setCertificateEntry(alias, cert);
2886
return true;
2887
}
2888
2889
// if certificate is self-signed, make sure it verifies
2890
boolean selfSigned = false;
2891
if (KeyStoreUtil.isSelfSigned(cert)) {
2892
cert.verify(cert.getPublicKey());
2893
selfSigned = true;
2894
}
2895
2896
// check if cert already exists in keystore
2897
String reply = null;
2898
String trustalias = keyStore.getCertificateAlias(cert);
2899
if (trustalias != null) {
2900
MessageFormat form = new MessageFormat(rb.getString
2901
("Certificate.already.exists.in.keystore.under.alias.trustalias."));
2902
Object[] source = {trustalias};
2903
System.err.println(form.format(source));
2904
checkWeak(rb.getString("the.input"), cert);
2905
printWeakWarnings(true);
2906
reply = getYesNoReply
2907
(rb.getString("Do.you.still.want.to.add.it.no."));
2908
} else if (selfSigned) {
2909
if (trustcacerts && (caks != null) &&
2910
((trustalias=caks.getCertificateAlias(cert)) != null)) {
2911
MessageFormat form = new MessageFormat(rb.getString
2912
("Certificate.already.exists.in.system.wide.CA.keystore.under.alias.trustalias."));
2913
Object[] source = {trustalias};
2914
System.err.println(form.format(source));
2915
checkWeak(rb.getString("the.input"), cert);
2916
printWeakWarnings(true);
2917
reply = getYesNoReply
2918
(rb.getString("Do.you.still.want.to.add.it.to.your.own.keystore.no."));
2919
}
2920
if (trustalias == null) {
2921
// Print the cert and ask user if they really want to add
2922
// it to their keystore
2923
printX509Cert(cert, System.out);
2924
checkWeak(rb.getString("the.input"), cert);
2925
printWeakWarnings(true);
2926
reply = getYesNoReply
2927
(rb.getString("Trust.this.certificate.no."));
2928
}
2929
}
2930
if (reply != null) {
2931
if ("YES".equals(reply)) {
2932
keyStore.setCertificateEntry(alias, cert);
2933
return true;
2934
} else {
2935
return false;
2936
}
2937
}
2938
2939
// Not found in this keystore and not self-signed
2940
// Try to establish trust chain
2941
try {
2942
Certificate[] chain = establishCertChain(null, cert);
2943
if (chain != null) {
2944
keyStore.setCertificateEntry(alias, cert);
2945
return true;
2946
}
2947
} catch (Exception e) {
2948
// Print the cert and ask user if they really want to add it to
2949
// their keystore
2950
printX509Cert(cert, System.out);
2951
checkWeak(rb.getString("the.input"), cert);
2952
printWeakWarnings(true);
2953
reply = getYesNoReply
2954
(rb.getString("Trust.this.certificate.no."));
2955
if ("YES".equals(reply)) {
2956
keyStore.setCertificateEntry(alias, cert);
2957
return true;
2958
} else {
2959
return false;
2960
}
2961
}
2962
2963
return false;
2964
}
2965
2966
/**
2967
* Prompts user for new password. New password must be different from
2968
* old one.
2969
*
2970
* @param prompt the message that gets prompted on the screen
2971
* @param oldPasswd the current (i.e., old) password
2972
*/
2973
private char[] getNewPasswd(String prompt, char[] oldPasswd)
2974
throws Exception
2975
{
2976
char[] entered = null;
2977
char[] reentered = null;
2978
2979
for (int count = 0; count < 3; count++) {
2980
MessageFormat form = new MessageFormat
2981
(rb.getString("New.prompt."));
2982
Object[] source = {prompt};
2983
System.err.print(form.format(source));
2984
entered = Password.readPassword(System.in);
2985
passwords.add(entered);
2986
if (entered == null || entered.length < 6) {
2987
System.err.println(rb.getString
2988
("Password.is.too.short.must.be.at.least.6.characters"));
2989
} else if (Arrays.equals(entered, oldPasswd)) {
2990
System.err.println(rb.getString("Passwords.must.differ"));
2991
} else {
2992
form = new MessageFormat
2993
(rb.getString("Re.enter.new.prompt."));
2994
Object[] src = {prompt};
2995
System.err.print(form.format(src));
2996
reentered = Password.readPassword(System.in);
2997
passwords.add(reentered);
2998
if (!Arrays.equals(entered, reentered)) {
2999
System.err.println
3000
(rb.getString("They.don.t.match.Try.again"));
3001
} else {
3002
Arrays.fill(reentered, ' ');
3003
return entered;
3004
}
3005
}
3006
if (entered != null) {
3007
Arrays.fill(entered, ' ');
3008
entered = null;
3009
}
3010
if (reentered != null) {
3011
Arrays.fill(reentered, ' ');
3012
reentered = null;
3013
}
3014
}
3015
throw new Exception(rb.getString("Too.many.failures.try.later"));
3016
}
3017
3018
/**
3019
* Prompts user for alias name.
3020
* @param prompt the {0} of "Enter {0} alias name: " in prompt line
3021
* @returns the string entered by the user, without the \n at the end
3022
*/
3023
private String getAlias(String prompt) throws Exception {
3024
if (prompt != null) {
3025
MessageFormat form = new MessageFormat
3026
(rb.getString("Enter.prompt.alias.name."));
3027
Object[] source = {prompt};
3028
System.err.print(form.format(source));
3029
} else {
3030
System.err.print(rb.getString("Enter.alias.name."));
3031
}
3032
return (new BufferedReader(new InputStreamReader(
3033
System.in))).readLine();
3034
}
3035
3036
/**
3037
* Prompts user for an input string from the command line (System.in)
3038
* @prompt the prompt string printed
3039
* @returns the string entered by the user, without the \n at the end
3040
*/
3041
private String inputStringFromStdin(String prompt) throws Exception {
3042
System.err.print(prompt);
3043
return (new BufferedReader(new InputStreamReader(
3044
System.in))).readLine();
3045
}
3046
3047
/**
3048
* Prompts user for key password. User may select to choose the same
3049
* password (<code>otherKeyPass</code>) as for <code>otherAlias</code>.
3050
*/
3051
private char[] getKeyPasswd(String alias, String otherAlias,
3052
char[] otherKeyPass)
3053
throws Exception
3054
{
3055
int count = 0;
3056
char[] keyPass = null;
3057
3058
do {
3059
if (otherKeyPass != null) {
3060
MessageFormat form = new MessageFormat(rb.getString
3061
("Enter.key.password.for.alias."));
3062
Object[] source = {alias};
3063
System.err.println(form.format(source));
3064
3065
form = new MessageFormat(rb.getString
3066
(".RETURN.if.same.as.for.otherAlias."));
3067
Object[] src = {otherAlias};
3068
System.err.print(form.format(src));
3069
} else {
3070
MessageFormat form = new MessageFormat(rb.getString
3071
("Enter.key.password.for.alias."));
3072
Object[] source = {alias};
3073
System.err.print(form.format(source));
3074
}
3075
System.err.flush();
3076
keyPass = Password.readPassword(System.in);
3077
passwords.add(keyPass);
3078
if (keyPass == null) {
3079
keyPass = otherKeyPass;
3080
}
3081
count++;
3082
} while ((keyPass == null) && count < 3);
3083
3084
if (keyPass == null) {
3085
throw new Exception(rb.getString("Too.many.failures.try.later"));
3086
}
3087
3088
return keyPass;
3089
}
3090
3091
private String withWeak(String alg) {
3092
if (DISABLED_CHECK.permits(SIG_PRIMITIVE_SET, alg, null)) {
3093
if (LEGACY_CHECK.permits(SIG_PRIMITIVE_SET, alg, null)) {
3094
return alg;
3095
} else {
3096
return String.format(rb.getString("with.weak"), alg);
3097
}
3098
} else {
3099
return String.format(rb.getString("with.disabled"), alg);
3100
}
3101
}
3102
3103
private String fullDisplayAlgName(Key key) {
3104
String result = key.getAlgorithm();
3105
if (key instanceof ECKey) {
3106
ECParameterSpec paramSpec = ((ECKey) key).getParams();
3107
if (paramSpec instanceof NamedCurve) {
3108
result += " (" + paramSpec.toString().split(" ")[0] + ")";
3109
}
3110
}
3111
return result;
3112
}
3113
3114
private String withWeak(PublicKey key) {
3115
int kLen = KeyUtil.getKeySize(key);
3116
String displayAlg = fullDisplayAlgName(key);
3117
if (DISABLED_CHECK.permits(SIG_PRIMITIVE_SET, key)) {
3118
if (LEGACY_CHECK.permits(SIG_PRIMITIVE_SET, key)) {
3119
if (kLen >= 0) {
3120
return String.format(rb.getString("key.bit"), kLen, displayAlg);
3121
} else {
3122
return String.format(rb.getString("unknown.size.1"), displayAlg);
3123
}
3124
} else {
3125
return String.format(rb.getString("key.bit.weak"), kLen, displayAlg);
3126
}
3127
} else {
3128
return String.format(rb.getString("key.bit.disabled"), kLen, displayAlg);
3129
}
3130
}
3131
3132
/**
3133
* Prints a certificate in a human readable format.
3134
*/
3135
private void printX509Cert(X509Certificate cert, PrintStream out)
3136
throws Exception
3137
{
3138
3139
MessageFormat form = new MessageFormat
3140
(rb.getString(".PATTERN.printX509Cert.with.weak"));
3141
PublicKey pkey = cert.getPublicKey();
3142
String sigName = cert.getSigAlgName();
3143
// No need to warn about sigalg of a trust anchor
3144
if (!isTrustedCert(cert)) {
3145
sigName = withWeak(sigName);
3146
}
3147
Object[] source = {cert.getSubjectDN().toString(),
3148
cert.getIssuerDN().toString(),
3149
cert.getSerialNumber().toString(16),
3150
cert.getNotBefore().toString(),
3151
cert.getNotAfter().toString(),
3152
getCertFingerPrint("SHA-1", cert),
3153
getCertFingerPrint("SHA-256", cert),
3154
sigName,
3155
withWeak(pkey),
3156
cert.getVersion()
3157
};
3158
out.println(form.format(source));
3159
3160
if (cert instanceof X509CertImpl) {
3161
X509CertImpl impl = (X509CertImpl)cert;
3162
X509CertInfo certInfo = (X509CertInfo)impl.get(X509CertImpl.NAME
3163
+ "." +
3164
X509CertImpl.INFO);
3165
CertificateExtensions exts = (CertificateExtensions)
3166
certInfo.get(X509CertInfo.EXTENSIONS);
3167
if (exts != null) {
3168
printExtensions(rb.getString("Extensions."), exts, out);
3169
}
3170
}
3171
}
3172
3173
private static void printExtensions(String title, CertificateExtensions exts, PrintStream out)
3174
throws Exception {
3175
int extnum = 0;
3176
Iterator<Extension> i1 = exts.getAllExtensions().iterator();
3177
Iterator<Extension> i2 = exts.getUnparseableExtensions().values().iterator();
3178
while (i1.hasNext() || i2.hasNext()) {
3179
Extension ext = i1.hasNext()?i1.next():i2.next();
3180
if (extnum == 0) {
3181
out.println();
3182
out.println(title);
3183
out.println();
3184
}
3185
out.print("#"+(++extnum)+": "+ ext);
3186
if (ext.getClass() == Extension.class) {
3187
byte[] v = ext.getExtensionValue();
3188
if (v.length == 0) {
3189
out.println(rb.getString(".Empty.value."));
3190
} else {
3191
new sun.misc.HexDumpEncoder().encodeBuffer(ext.getExtensionValue(), out);
3192
out.println();
3193
}
3194
}
3195
out.println();
3196
}
3197
}
3198
3199
/**
3200
* Locates a signer for a given certificate from a given keystore and
3201
* returns the signer's certificate.
3202
* @param cert the certificate whose signer is searched, not null
3203
* @param ks the keystore to search with, not null
3204
* @return <code>cert</code> itself if it's already inside <code>ks</code>,
3205
* or a certificate inside <code>ks</code> who signs <code>cert</code>,
3206
* or null otherwise. A label is added.
3207
*/
3208
private static Pair<String,Certificate>
3209
getSigner(Certificate cert, KeyStore ks) throws Exception {
3210
if (ks.getCertificateAlias(cert) != null) {
3211
return new Pair<>("", cert);
3212
}
3213
for (Enumeration<String> aliases = ks.aliases();
3214
aliases.hasMoreElements(); ) {
3215
String name = aliases.nextElement();
3216
Certificate trustedCert = ks.getCertificate(name);
3217
if (trustedCert != null) {
3218
try {
3219
cert.verify(trustedCert.getPublicKey());
3220
return new Pair<>(name, trustedCert);
3221
} catch (Exception e) {
3222
// Not verified, skip to the next one
3223
}
3224
}
3225
}
3226
return null;
3227
}
3228
3229
/**
3230
* Gets an X.500 name suitable for inclusion in a certification request.
3231
*/
3232
private X500Name getX500Name() throws IOException {
3233
BufferedReader in;
3234
in = new BufferedReader(new InputStreamReader(System.in));
3235
String commonName = "Unknown";
3236
String organizationalUnit = "Unknown";
3237
String organization = "Unknown";
3238
String city = "Unknown";
3239
String state = "Unknown";
3240
String country = "Unknown";
3241
X500Name name;
3242
String userInput = null;
3243
3244
int maxRetry = 20;
3245
do {
3246
if (maxRetry-- < 0) {
3247
throw new RuntimeException(rb.getString(
3248
"Too.many.retries.program.terminated"));
3249
}
3250
commonName = inputString(in,
3251
rb.getString("What.is.your.first.and.last.name."),
3252
commonName);
3253
organizationalUnit = inputString(in,
3254
rb.getString
3255
("What.is.the.name.of.your.organizational.unit."),
3256
organizationalUnit);
3257
organization = inputString(in,
3258
rb.getString("What.is.the.name.of.your.organization."),
3259
organization);
3260
city = inputString(in,
3261
rb.getString("What.is.the.name.of.your.City.or.Locality."),
3262
city);
3263
state = inputString(in,
3264
rb.getString("What.is.the.name.of.your.State.or.Province."),
3265
state);
3266
country = inputString(in,
3267
rb.getString
3268
("What.is.the.two.letter.country.code.for.this.unit."),
3269
country);
3270
name = new X500Name(commonName, organizationalUnit, organization,
3271
city, state, country);
3272
MessageFormat form = new MessageFormat
3273
(rb.getString("Is.name.correct."));
3274
Object[] source = {name};
3275
userInput = inputString
3276
(in, form.format(source), rb.getString("no"));
3277
} while (collator.compare(userInput, rb.getString("yes")) != 0 &&
3278
collator.compare(userInput, rb.getString("y")) != 0);
3279
3280
System.err.println();
3281
return name;
3282
}
3283
3284
private String inputString(BufferedReader in, String prompt,
3285
String defaultValue)
3286
throws IOException
3287
{
3288
System.err.println(prompt);
3289
MessageFormat form = new MessageFormat
3290
(rb.getString(".defaultValue."));
3291
Object[] source = {defaultValue};
3292
System.err.print(form.format(source));
3293
System.err.flush();
3294
3295
String value = in.readLine();
3296
if (value == null || collator.compare(value, "") == 0) {
3297
value = defaultValue;
3298
}
3299
return value;
3300
}
3301
3302
/**
3303
* Writes an X.509 certificate in base64 or binary encoding to an output
3304
* stream.
3305
*/
3306
private void dumpCert(Certificate cert, PrintStream out)
3307
throws IOException, CertificateException
3308
{
3309
if (rfc) {
3310
out.println(X509Factory.BEGIN_CERT);
3311
out.println(Base64.getMimeEncoder(64, CRLF).encodeToString(cert.getEncoded()));
3312
out.println(X509Factory.END_CERT);
3313
} else {
3314
out.write(cert.getEncoded()); // binary
3315
}
3316
}
3317
3318
/**
3319
* Converts a byte to hex digit and writes to the supplied buffer
3320
*/
3321
private void byte2hex(byte b, StringBuffer buf) {
3322
char[] hexChars = { '0', '1', '2', '3', '4', '5', '6', '7', '8',
3323
'9', 'A', 'B', 'C', 'D', 'E', 'F' };
3324
int high = ((b & 0xf0) >> 4);
3325
int low = (b & 0x0f);
3326
buf.append(hexChars[high]);
3327
buf.append(hexChars[low]);
3328
}
3329
3330
/**
3331
* Converts a byte array to hex string
3332
*/
3333
private String toHexString(byte[] block) {
3334
StringBuffer buf = new StringBuffer();
3335
int len = block.length;
3336
for (int i = 0; i < len; i++) {
3337
byte2hex(block[i], buf);
3338
if (i < len-1) {
3339
buf.append(":");
3340
}
3341
}
3342
return buf.toString();
3343
}
3344
3345
/**
3346
* Recovers (private) key associated with given alias.
3347
*
3348
* @return an array of objects, where the 1st element in the array is the
3349
* recovered private key, and the 2nd element is the password used to
3350
* recover it.
3351
*/
3352
private Pair<Key,char[]> recoverKey(String alias, char[] storePass,
3353
char[] keyPass)
3354
throws Exception
3355
{
3356
Key key = null;
3357
3358
if (KeyStoreUtil.isWindowsKeyStore(storetype)) {
3359
key = keyStore.getKey(alias, null);
3360
return Pair.of(key, null);
3361
}
3362
3363
if (keyStore.containsAlias(alias) == false) {
3364
MessageFormat form = new MessageFormat
3365
(rb.getString("Alias.alias.does.not.exist"));
3366
Object[] source = {alias};
3367
throw new Exception(form.format(source));
3368
}
3369
if (!keyStore.entryInstanceOf(alias, KeyStore.PrivateKeyEntry.class) &&
3370
!keyStore.entryInstanceOf(alias, KeyStore.SecretKeyEntry.class)) {
3371
MessageFormat form = new MessageFormat
3372
(rb.getString("Alias.alias.has.no.key"));
3373
Object[] source = {alias};
3374
throw new Exception(form.format(source));
3375
}
3376
3377
if (keyPass == null) {
3378
// Try to recover the key using the keystore password
3379
try {
3380
key = keyStore.getKey(alias, storePass);
3381
3382
keyPass = storePass;
3383
passwords.add(keyPass);
3384
} catch (UnrecoverableKeyException e) {
3385
// Did not work out, so prompt user for key password
3386
if (!token) {
3387
keyPass = getKeyPasswd(alias, null, null);
3388
key = keyStore.getKey(alias, keyPass);
3389
} else {
3390
throw e;
3391
}
3392
}
3393
} else {
3394
key = keyStore.getKey(alias, keyPass);
3395
}
3396
3397
return Pair.of(key, keyPass);
3398
}
3399
3400
/**
3401
* Recovers entry associated with given alias.
3402
*
3403
* @return an array of objects, where the 1st element in the array is the
3404
* recovered entry, and the 2nd element is the password used to
3405
* recover it (null if no password).
3406
*/
3407
private Pair<Entry,char[]> recoverEntry(KeyStore ks,
3408
String alias,
3409
char[] pstore,
3410
char[] pkey) throws Exception {
3411
3412
if (ks.containsAlias(alias) == false) {
3413
MessageFormat form = new MessageFormat
3414
(rb.getString("Alias.alias.does.not.exist"));
3415
Object[] source = {alias};
3416
throw new Exception(form.format(source));
3417
}
3418
3419
PasswordProtection pp = null;
3420
Entry entry;
3421
3422
try {
3423
// First attempt to access entry without key password
3424
// (PKCS11 entry or trusted certificate entry, for example)
3425
3426
entry = ks.getEntry(alias, pp);
3427
pkey = null;
3428
} catch (UnrecoverableEntryException une) {
3429
3430
if(P11KEYSTORE.equalsIgnoreCase(ks.getType()) ||
3431
KeyStoreUtil.isWindowsKeyStore(ks.getType())) {
3432
// should not happen, but a possibility
3433
throw une;
3434
}
3435
3436
// entry is protected
3437
3438
if (pkey != null) {
3439
3440
// try provided key password
3441
3442
pp = new PasswordProtection(pkey);
3443
entry = ks.getEntry(alias, pp);
3444
3445
} else {
3446
3447
// try store pass
3448
3449
try {
3450
pp = new PasswordProtection(pstore);
3451
entry = ks.getEntry(alias, pp);
3452
pkey = pstore;
3453
} catch (UnrecoverableEntryException une2) {
3454
if (P12KEYSTORE.equalsIgnoreCase(ks.getType())) {
3455
3456
// P12 keystore currently does not support separate
3457
// store and entry passwords
3458
3459
throw une2;
3460
} else {
3461
3462
// prompt for entry password
3463
3464
pkey = getKeyPasswd(alias, null, null);
3465
pp = new PasswordProtection(pkey);
3466
entry = ks.getEntry(alias, pp);
3467
}
3468
}
3469
}
3470
}
3471
3472
return Pair.of(entry, pkey);
3473
}
3474
/**
3475
* Gets the requested finger print of the certificate.
3476
*/
3477
private String getCertFingerPrint(String mdAlg, Certificate cert)
3478
throws Exception
3479
{
3480
byte[] encCertInfo = cert.getEncoded();
3481
MessageDigest md = MessageDigest.getInstance(mdAlg);
3482
byte[] digest = md.digest(encCertInfo);
3483
return toHexString(digest);
3484
}
3485
3486
/**
3487
* Prints warning about missing integrity check.
3488
*/
3489
private void printNoIntegrityWarning() {
3490
System.err.println();
3491
System.err.println(rb.getString
3492
(".WARNING.WARNING.WARNING."));
3493
System.err.println(rb.getString
3494
(".The.integrity.of.the.information.stored.in.your.keystore."));
3495
System.err.println(rb.getString
3496
(".WARNING.WARNING.WARNING."));
3497
System.err.println();
3498
}
3499
3500
/**
3501
* Validates chain in certification reply, and returns the ordered
3502
* elements of the chain (with user certificate first, and root
3503
* certificate last in the array).
3504
*
3505
* @param alias the alias name
3506
* @param userCert the user certificate of the alias
3507
* @param replyCerts the chain provided in the reply
3508
*/
3509
private Certificate[] validateReply(String alias,
3510
Certificate userCert,
3511
Certificate[] replyCerts)
3512
throws Exception
3513
{
3514
3515
checkWeak(rb.getString("reply"), replyCerts);
3516
3517
// order the certs in the reply (bottom-up).
3518
// we know that all certs in the reply are of type X.509, because
3519
// we parsed them using an X.509 certificate factory
3520
int i;
3521
PublicKey userPubKey = userCert.getPublicKey();
3522
for (i=0; i<replyCerts.length; i++) {
3523
if (userPubKey.equals(replyCerts[i].getPublicKey())) {
3524
break;
3525
}
3526
}
3527
if (i == replyCerts.length) {
3528
MessageFormat form = new MessageFormat(rb.getString
3529
("Certificate.reply.does.not.contain.public.key.for.alias."));
3530
Object[] source = {alias};
3531
throw new Exception(form.format(source));
3532
}
3533
3534
Certificate tmpCert = replyCerts[0];
3535
replyCerts[0] = replyCerts[i];
3536
replyCerts[i] = tmpCert;
3537
3538
X509Certificate thisCert = (X509Certificate)replyCerts[0];
3539
3540
for (i=1; i < replyCerts.length-1; i++) {
3541
// find a cert in the reply who signs thisCert
3542
int j;
3543
for (j=i; j<replyCerts.length; j++) {
3544
if (KeyStoreUtil.signedBy(thisCert, (X509Certificate)replyCerts[j])) {
3545
tmpCert = replyCerts[i];
3546
replyCerts[i] = replyCerts[j];
3547
replyCerts[j] = tmpCert;
3548
thisCert = (X509Certificate)replyCerts[i];
3549
break;
3550
}
3551
}
3552
if (j == replyCerts.length) {
3553
throw new Exception
3554
(rb.getString("Incomplete.certificate.chain.in.reply"));
3555
}
3556
}
3557
3558
if (noprompt) {
3559
return replyCerts;
3560
}
3561
3562
// do we trust the cert at the top?
3563
Certificate topCert = replyCerts[replyCerts.length-1];
3564
boolean fromKeyStore = true;
3565
Pair<String,Certificate> root = getSigner(topCert, keyStore);
3566
if (root == null && trustcacerts && caks != null) {
3567
root = getSigner(topCert, caks);
3568
fromKeyStore = false;
3569
}
3570
if (root == null) {
3571
System.err.println();
3572
System.err.println
3573
(rb.getString("Top.level.certificate.in.reply."));
3574
printX509Cert((X509Certificate)topCert, System.out);
3575
System.err.println();
3576
System.err.print(rb.getString(".is.not.trusted."));
3577
printWeakWarnings(true);
3578
String reply = getYesNoReply
3579
(rb.getString("Install.reply.anyway.no."));
3580
if ("NO".equals(reply)) {
3581
return null;
3582
}
3583
} else {
3584
if (root.snd != topCert) {
3585
// append the root CA cert to the chain
3586
Certificate[] tmpCerts =
3587
new Certificate[replyCerts.length+1];
3588
System.arraycopy(replyCerts, 0, tmpCerts, 0,
3589
replyCerts.length);
3590
tmpCerts[tmpCerts.length-1] = root.snd;
3591
replyCerts = tmpCerts;
3592
checkWeak(String.format(rb.getString(fromKeyStore ?
3593
"alias.in.keystore" :
3594
"alias.in.cacerts"),
3595
root.fst),
3596
root.snd);
3597
}
3598
}
3599
return replyCerts;
3600
}
3601
3602
/**
3603
* Establishes a certificate chain (using trusted certificates in the
3604
* keystore and cacerts), starting with the reply (certToVerify)
3605
* and ending at a self-signed certificate found in the keystore.
3606
*
3607
* @param userCert optional existing certificate, mostly likely be the
3608
* original self-signed cert created by -genkeypair.
3609
* It must have the same public key as certToVerify
3610
* but cannot be the same cert.
3611
* @param certToVerify the starting certificate to build the chain
3612
* @returns the established chain, might be null if user decides not
3613
*/
3614
private Certificate[] establishCertChain(Certificate userCert,
3615
Certificate certToVerify)
3616
throws Exception
3617
{
3618
if (userCert != null) {
3619
// Make sure that the public key of the certificate reply matches
3620
// the original public key in the keystore
3621
PublicKey origPubKey = userCert.getPublicKey();
3622
PublicKey replyPubKey = certToVerify.getPublicKey();
3623
if (!origPubKey.equals(replyPubKey)) {
3624
throw new Exception(rb.getString
3625
("Public.keys.in.reply.and.keystore.don.t.match"));
3626
}
3627
3628
// If the two certs are identical, we're done: no need to import
3629
// anything
3630
if (certToVerify.equals(userCert)) {
3631
throw new Exception(rb.getString
3632
("Certificate.reply.and.certificate.in.keystore.are.identical"));
3633
}
3634
}
3635
3636
// Build a hash table of all certificates in the keystore.
3637
// Use the subject distinguished name as the key into the hash table.
3638
// All certificates associated with the same subject distinguished
3639
// name are stored in the same hash table entry as a vector.
3640
Hashtable<Principal, Vector<Pair<String,X509Certificate>>> certs = null;
3641
if (keyStore.size() > 0) {
3642
certs = new Hashtable<>(11);
3643
keystorecerts2Hashtable(keyStore, certs);
3644
}
3645
if (trustcacerts) {
3646
if (caks!=null && caks.size()>0) {
3647
if (certs == null) {
3648
certs = new Hashtable<>(11);
3649
}
3650
keystorecerts2Hashtable(caks, certs);
3651
}
3652
}
3653
3654
// start building chain
3655
Vector<Pair<String,X509Certificate>> chain = new Vector<>(2);
3656
if (buildChain(
3657
new Pair<>(rb.getString("the.input"),
3658
(X509Certificate) certToVerify),
3659
chain, certs)) {
3660
for (Pair<String,X509Certificate> p : chain) {
3661
checkWeak(p.fst, p.snd);
3662
}
3663
Certificate[] newChain =
3664
new Certificate[chain.size()];
3665
// buildChain() returns chain with self-signed root-cert first and
3666
// user-cert last, so we need to invert the chain before we store
3667
// it
3668
int j=0;
3669
for (int i=chain.size()-1; i>=0; i--) {
3670
newChain[j] = chain.elementAt(i).snd;
3671
j++;
3672
}
3673
return newChain;
3674
} else {
3675
throw new Exception
3676
(rb.getString("Failed.to.establish.chain.from.reply"));
3677
}
3678
}
3679
3680
/**
3681
* Recursively tries to establish chain from pool of certs starting from
3682
* certToVerify until a self-signed cert is found, and fill the certs found
3683
* into chain. Each cert in the chain signs the next one.
3684
*
3685
* This method is able to recover from an error, say, if certToVerify
3686
* is signed by certA but certA has no issuer in certs and itself is not
3687
* self-signed, the method can try another certB that also signs
3688
* certToVerify and look for signer of certB, etc, etc.
3689
*
3690
* Each cert in chain comes with a label showing its origin. The label is
3691
* used in the warning message when the cert is considered a risk.
3692
*
3693
* @param certToVerify the cert that needs to be verified.
3694
* @param chain the chain that's being built.
3695
* @param certs the pool of trusted certs
3696
*
3697
* @return true if successful, false otherwise.
3698
*/
3699
private boolean buildChain(Pair<String,X509Certificate> certToVerify,
3700
Vector<Pair<String,X509Certificate>> chain,
3701
Hashtable<Principal, Vector<Pair<String,X509Certificate>>> certs) {
3702
if (KeyStoreUtil.isSelfSigned(certToVerify.snd)) {
3703
// reached self-signed root cert;
3704
// no verification needed because it's trusted.
3705
chain.addElement(certToVerify);
3706
return true;
3707
}
3708
3709
Principal issuer = certToVerify.snd.getIssuerDN();
3710
3711
// Get the issuer's certificate(s)
3712
Vector<Pair<String,X509Certificate>> vec = certs.get(issuer);
3713
if (vec == null) {
3714
return false;
3715
}
3716
3717
// Try out each certificate in the vector, until we find one
3718
// whose public key verifies the signature of the certificate
3719
// in question.
3720
for (Enumeration<Pair<String,X509Certificate>> issuerCerts = vec.elements();
3721
issuerCerts.hasMoreElements(); ) {
3722
Pair<String,X509Certificate> issuerCert = issuerCerts.nextElement();
3723
PublicKey issuerPubKey = issuerCert.snd.getPublicKey();
3724
try {
3725
certToVerify.snd.verify(issuerPubKey);
3726
} catch (Exception e) {
3727
continue;
3728
}
3729
if (buildChain(issuerCert, chain, certs)) {
3730
chain.addElement(certToVerify);
3731
return true;
3732
}
3733
}
3734
return false;
3735
}
3736
3737
/**
3738
* Prompts user for yes/no decision.
3739
*
3740
* @return the user's decision, can only be "YES" or "NO"
3741
*/
3742
private String getYesNoReply(String prompt)
3743
throws IOException
3744
{
3745
String reply = null;
3746
int maxRetry = 20;
3747
do {
3748
if (maxRetry-- < 0) {
3749
throw new RuntimeException(rb.getString(
3750
"Too.many.retries.program.terminated"));
3751
}
3752
System.err.print(prompt);
3753
System.err.flush();
3754
reply = (new BufferedReader(new InputStreamReader
3755
(System.in))).readLine();
3756
if (collator.compare(reply, "") == 0 ||
3757
collator.compare(reply, rb.getString("n")) == 0 ||
3758
collator.compare(reply, rb.getString("no")) == 0) {
3759
reply = "NO";
3760
} else if (collator.compare(reply, rb.getString("y")) == 0 ||
3761
collator.compare(reply, rb.getString("yes")) == 0) {
3762
reply = "YES";
3763
} else {
3764
System.err.println(rb.getString("Wrong.answer.try.again"));
3765
reply = null;
3766
}
3767
} while (reply == null);
3768
return reply;
3769
}
3770
3771
/**
3772
* Stores the (leaf) certificates of a keystore in a hashtable.
3773
* All certs belonging to the same CA are stored in a vector that
3774
* in turn is stored in the hashtable, keyed by the CA's subject DN.
3775
* Each cert comes with a string label that shows its origin and alias.
3776
*/
3777
private void keystorecerts2Hashtable(KeyStore ks,
3778
Hashtable<Principal, Vector<Pair<String,X509Certificate>>> hash)
3779
throws Exception {
3780
3781
for (Enumeration<String> aliases = ks.aliases();
3782
aliases.hasMoreElements(); ) {
3783
String alias = aliases.nextElement();
3784
Certificate cert = ks.getCertificate(alias);
3785
if (cert != null) {
3786
Principal subjectDN = ((X509Certificate)cert).getSubjectDN();
3787
Pair<String,X509Certificate> pair = new Pair<>(
3788
String.format(
3789
rb.getString(ks == caks ?
3790
"alias.in.cacerts" :
3791
"alias.in.keystore"),
3792
alias),
3793
(X509Certificate)cert);
3794
Vector<Pair<String,X509Certificate>> vec = hash.get(subjectDN);
3795
if (vec == null) {
3796
vec = new Vector<>();
3797
vec.addElement(pair);
3798
} else {
3799
if (!vec.contains(pair)) {
3800
vec.addElement(pair);
3801
}
3802
}
3803
hash.put(subjectDN, vec);
3804
}
3805
}
3806
}
3807
3808
/**
3809
* Returns the issue time that's specified the -startdate option
3810
* @param s the value of -startdate option
3811
*/
3812
private static Date getStartDate(String s) throws IOException {
3813
Calendar c = new GregorianCalendar();
3814
if (s != null) {
3815
IOException ioe = new IOException(
3816
rb.getString("Illegal.startdate.value"));
3817
int len = s.length();
3818
if (len == 0) {
3819
throw ioe;
3820
}
3821
if (s.charAt(0) == '-' || s.charAt(0) == '+') {
3822
// Form 1: ([+-]nnn[ymdHMS])+
3823
int start = 0;
3824
while (start < len) {
3825
int sign = 0;
3826
switch (s.charAt(start)) {
3827
case '+': sign = 1; break;
3828
case '-': sign = -1; break;
3829
default: throw ioe;
3830
}
3831
int i = start+1;
3832
for (; i<len; i++) {
3833
char ch = s.charAt(i);
3834
if (ch < '0' || ch > '9') break;
3835
}
3836
if (i == start+1) throw ioe;
3837
int number = Integer.parseInt(s.substring(start+1, i));
3838
if (i >= len) throw ioe;
3839
int unit = 0;
3840
switch (s.charAt(i)) {
3841
case 'y': unit = Calendar.YEAR; break;
3842
case 'm': unit = Calendar.MONTH; break;
3843
case 'd': unit = Calendar.DATE; break;
3844
case 'H': unit = Calendar.HOUR; break;
3845
case 'M': unit = Calendar.MINUTE; break;
3846
case 'S': unit = Calendar.SECOND; break;
3847
default: throw ioe;
3848
}
3849
c.add(unit, sign * number);
3850
start = i + 1;
3851
}
3852
} else {
3853
// Form 2: [yyyy/mm/dd] [HH:MM:SS]
3854
String date = null, time = null;
3855
if (len == 19) {
3856
date = s.substring(0, 10);
3857
time = s.substring(11);
3858
if (s.charAt(10) != ' ')
3859
throw ioe;
3860
} else if (len == 10) {
3861
date = s;
3862
} else if (len == 8) {
3863
time = s;
3864
} else {
3865
throw ioe;
3866
}
3867
if (date != null) {
3868
if (date.matches("\\d\\d\\d\\d\\/\\d\\d\\/\\d\\d")) {
3869
c.set(Integer.valueOf(date.substring(0, 4)),
3870
Integer.valueOf(date.substring(5, 7))-1,
3871
Integer.valueOf(date.substring(8, 10)));
3872
} else {
3873
throw ioe;
3874
}
3875
}
3876
if (time != null) {
3877
if (time.matches("\\d\\d:\\d\\d:\\d\\d")) {
3878
c.set(Calendar.HOUR_OF_DAY, Integer.valueOf(time.substring(0, 2)));
3879
c.set(Calendar.MINUTE, Integer.valueOf(time.substring(0, 2)));
3880
c.set(Calendar.SECOND, Integer.valueOf(time.substring(0, 2)));
3881
c.set(Calendar.MILLISECOND, 0);
3882
} else {
3883
throw ioe;
3884
}
3885
}
3886
}
3887
}
3888
return c.getTime();
3889
}
3890
3891
/**
3892
* Match a command (may be abbreviated) with a command set.
3893
* @param s the command provided
3894
* @param list the legal command set. If there is a null, commands after it
3895
* are regarded experimental, which means they are supported but their
3896
* existence should not be revealed to user.
3897
* @return the position of a single match, or -1 if none matched
3898
* @throws Exception if s is ambiguous
3899
*/
3900
private static int oneOf(String s, String... list) throws Exception {
3901
int[] match = new int[list.length];
3902
int nmatch = 0;
3903
int experiment = Integer.MAX_VALUE;
3904
for (int i = 0; i<list.length; i++) {
3905
String one = list[i];
3906
if (one == null) {
3907
experiment = i;
3908
continue;
3909
}
3910
if (one.toLowerCase(Locale.ENGLISH)
3911
.startsWith(s.toLowerCase(Locale.ENGLISH))) {
3912
match[nmatch++] = i;
3913
} else {
3914
StringBuffer sb = new StringBuffer();
3915
boolean first = true;
3916
for (char c: one.toCharArray()) {
3917
if (first) {
3918
sb.append(c);
3919
first = false;
3920
} else {
3921
if (!Character.isLowerCase(c)) {
3922
sb.append(c);
3923
}
3924
}
3925
}
3926
if (sb.toString().equalsIgnoreCase(s)) {
3927
match[nmatch++] = i;
3928
}
3929
}
3930
}
3931
if (nmatch == 0) {
3932
return -1;
3933
} else if (nmatch == 1) {
3934
return match[0];
3935
} else {
3936
// If multiple matches is in experimental commands, ignore them
3937
if (match[1] > experiment) {
3938
return match[0];
3939
}
3940
StringBuffer sb = new StringBuffer();
3941
MessageFormat form = new MessageFormat(rb.getString
3942
("command.{0}.is.ambiguous."));
3943
Object[] source = {s};
3944
sb.append(form.format(source));
3945
sb.append("\n ");
3946
for (int i=0; i<nmatch && match[i]<experiment; i++) {
3947
sb.append(' ');
3948
sb.append(list[match[i]]);
3949
}
3950
throw new Exception(sb.toString());
3951
}
3952
}
3953
3954
/**
3955
* Create a GeneralName object from known types
3956
* @param t one of 5 known types
3957
* @param v value
3958
* @return which one
3959
*/
3960
private GeneralName createGeneralName(String t, String v)
3961
throws Exception {
3962
GeneralNameInterface gn;
3963
int p = oneOf(t, "EMAIL", "URI", "DNS", "IP", "OID");
3964
if (p < 0) {
3965
throw new Exception(rb.getString(
3966
"Unrecognized.GeneralName.type.") + t);
3967
}
3968
switch (p) {
3969
case 0: gn = new RFC822Name(v); break;
3970
case 1: gn = new URIName(v); break;
3971
case 2: gn = new DNSName(v); break;
3972
case 3: gn = new IPAddressName(v); break;
3973
default: gn = new OIDName(v); break; //4
3974
}
3975
return new GeneralName(gn);
3976
}
3977
3978
private static final String[] extSupported = {
3979
"BasicConstraints",
3980
"KeyUsage",
3981
"ExtendedKeyUsage",
3982
"SubjectAlternativeName",
3983
"IssuerAlternativeName",
3984
"SubjectInfoAccess",
3985
"AuthorityInfoAccess",
3986
null,
3987
"CRLDistributionPoints",
3988
};
3989
3990
private ObjectIdentifier findOidForExtName(String type)
3991
throws Exception {
3992
switch (oneOf(type, extSupported)) {
3993
case 0: return PKIXExtensions.BasicConstraints_Id;
3994
case 1: return PKIXExtensions.KeyUsage_Id;
3995
case 2: return PKIXExtensions.ExtendedKeyUsage_Id;
3996
case 3: return PKIXExtensions.SubjectAlternativeName_Id;
3997
case 4: return PKIXExtensions.IssuerAlternativeName_Id;
3998
case 5: return PKIXExtensions.SubjectInfoAccess_Id;
3999
case 6: return PKIXExtensions.AuthInfoAccess_Id;
4000
case 8: return PKIXExtensions.CRLDistributionPoints_Id;
4001
default: return new ObjectIdentifier(type);
4002
}
4003
}
4004
4005
/**
4006
* Create X509v3 extensions from a string representation. Note that the
4007
* SubjectKeyIdentifierExtension will always be created non-critical besides
4008
* the extension requested in the <code>extstr</code> argument.
4009
*
4010
* @param reqex the requested extensions, can be null, used for -gencert
4011
* @param ext the original extensions, can be null, used for -selfcert
4012
* @param extstrs -ext values, Read keytool doc
4013
* @param pkey the public key for the certificate
4014
* @param akey the public key for the authority (issuer)
4015
* @return the created CertificateExtensions
4016
*/
4017
private CertificateExtensions createV3Extensions(
4018
CertificateExtensions reqex,
4019
CertificateExtensions ext,
4020
List <String> extstrs,
4021
PublicKey pkey,
4022
PublicKey akey) throws Exception {
4023
4024
if (ext != null && reqex != null) {
4025
// This should not happen
4026
throw new Exception("One of request and original should be null.");
4027
}
4028
if (ext == null) ext = new CertificateExtensions();
4029
try {
4030
// name{:critical}{=value}
4031
// Honoring requested extensions
4032
if (reqex != null) {
4033
for(String extstr: extstrs) {
4034
if (extstr.toLowerCase(Locale.ENGLISH).startsWith("honored=")) {
4035
List<String> list = Arrays.asList(
4036
extstr.toLowerCase(Locale.ENGLISH).substring(8).split(","));
4037
// First check existence of "all"
4038
if (list.contains("all")) {
4039
ext = reqex; // we know ext was null
4040
}
4041
// one by one for others
4042
for (String item: list) {
4043
if (item.equals("all")) continue;
4044
4045
// add or remove
4046
boolean add = true;
4047
// -1, unchanged, 0 crtical, 1 non-critical
4048
int action = -1;
4049
String type = null;
4050
if (item.startsWith("-")) {
4051
add = false;
4052
type = item.substring(1);
4053
} else {
4054
int colonpos = item.indexOf(':');
4055
if (colonpos >= 0) {
4056
type = item.substring(0, colonpos);
4057
action = oneOf(item.substring(colonpos+1),
4058
"critical", "non-critical");
4059
if (action == -1) {
4060
throw new Exception(rb.getString
4061
("Illegal.value.") + item);
4062
}
4063
}
4064
}
4065
String n = reqex.getNameByOid(findOidForExtName(type));
4066
if (add) {
4067
Extension e = reqex.get(n);
4068
if (!e.isCritical() && action == 0
4069
|| e.isCritical() && action == 1) {
4070
e = Extension.newExtension(
4071
e.getExtensionId(),
4072
!e.isCritical(),
4073
e.getExtensionValue());
4074
ext.set(n, e);
4075
}
4076
} else {
4077
ext.delete(n);
4078
}
4079
}
4080
break;
4081
}
4082
}
4083
}
4084
for(String extstr: extstrs) {
4085
String name, value;
4086
boolean isCritical = false;
4087
4088
int eqpos = extstr.indexOf('=');
4089
if (eqpos >= 0) {
4090
name = extstr.substring(0, eqpos);
4091
value = extstr.substring(eqpos+1);
4092
} else {
4093
name = extstr;
4094
value = null;
4095
}
4096
4097
int colonpos = name.indexOf(':');
4098
if (colonpos >= 0) {
4099
if (oneOf(name.substring(colonpos+1), "critical") == 0) {
4100
isCritical = true;
4101
}
4102
name = name.substring(0, colonpos);
4103
}
4104
4105
if (name.equalsIgnoreCase("honored")) {
4106
continue;
4107
}
4108
int exttype = oneOf(name, extSupported);
4109
switch (exttype) {
4110
case 0: // BC
4111
int pathLen = -1;
4112
boolean isCA = false;
4113
if (value == null) {
4114
isCA = true;
4115
} else {
4116
try { // the abbr format
4117
pathLen = Integer.parseInt(value);
4118
isCA = true;
4119
} catch (NumberFormatException ufe) {
4120
// ca:true,pathlen:1
4121
for (String part: value.split(",")) {
4122
String[] nv = part.split(":");
4123
if (nv.length != 2) {
4124
throw new Exception(rb.getString
4125
("Illegal.value.") + extstr);
4126
} else {
4127
if (nv[0].equalsIgnoreCase("ca")) {
4128
isCA = Boolean.parseBoolean(nv[1]);
4129
} else if (nv[0].equalsIgnoreCase("pathlen")) {
4130
pathLen = Integer.parseInt(nv[1]);
4131
} else {
4132
throw new Exception(rb.getString
4133
("Illegal.value.") + extstr);
4134
}
4135
}
4136
}
4137
}
4138
}
4139
ext.set(BasicConstraintsExtension.NAME,
4140
new BasicConstraintsExtension(isCritical, isCA,
4141
pathLen));
4142
break;
4143
case 1: // KU
4144
if(value != null) {
4145
boolean[] ok = new boolean[9];
4146
for (String s: value.split(",")) {
4147
int p = oneOf(s,
4148
"digitalSignature", // (0),
4149
"nonRepudiation", // (1)
4150
"keyEncipherment", // (2),
4151
"dataEncipherment", // (3),
4152
"keyAgreement", // (4),
4153
"keyCertSign", // (5),
4154
"cRLSign", // (6),
4155
"encipherOnly", // (7),
4156
"decipherOnly", // (8)
4157
"contentCommitment" // also (1)
4158
);
4159
if (p < 0) {
4160
throw new Exception(rb.getString("Unknown.keyUsage.type.") + s);
4161
}
4162
if (p == 9) p = 1;
4163
ok[p] = true;
4164
}
4165
KeyUsageExtension kue = new KeyUsageExtension(ok);
4166
// The above KeyUsageExtension constructor does not
4167
// allow isCritical value, so...
4168
ext.set(KeyUsageExtension.NAME, Extension.newExtension(
4169
kue.getExtensionId(),
4170
isCritical,
4171
kue.getExtensionValue()));
4172
} else {
4173
throw new Exception(rb.getString
4174
("Illegal.value.") + extstr);
4175
}
4176
break;
4177
case 2: // EKU
4178
if(value != null) {
4179
Vector<ObjectIdentifier> v = new Vector<>();
4180
for (String s: value.split(",")) {
4181
int p = oneOf(s,
4182
"anyExtendedKeyUsage",
4183
"serverAuth", //1
4184
"clientAuth", //2
4185
"codeSigning", //3
4186
"emailProtection", //4
4187
"", //5
4188
"", //6
4189
"", //7
4190
"timeStamping", //8
4191
"OCSPSigning" //9
4192
);
4193
if (p < 0) {
4194
try {
4195
v.add(new ObjectIdentifier(s));
4196
} catch (Exception e) {
4197
throw new Exception(rb.getString(
4198
"Unknown.extendedkeyUsage.type.") + s);
4199
}
4200
} else if (p == 0) {
4201
v.add(new ObjectIdentifier("2.5.29.37.0"));
4202
} else {
4203
v.add(new ObjectIdentifier("1.3.6.1.5.5.7.3." + p));
4204
}
4205
}
4206
ext.set(ExtendedKeyUsageExtension.NAME,
4207
new ExtendedKeyUsageExtension(isCritical, v));
4208
} else {
4209
throw new Exception(rb.getString
4210
("Illegal.value.") + extstr);
4211
}
4212
break;
4213
case 3: // SAN
4214
case 4: // IAN
4215
if(value != null) {
4216
String[] ps = value.split(",");
4217
GeneralNames gnames = new GeneralNames();
4218
for(String item: ps) {
4219
colonpos = item.indexOf(':');
4220
if (colonpos < 0) {
4221
throw new Exception("Illegal item " + item + " in " + extstr);
4222
}
4223
String t = item.substring(0, colonpos);
4224
String v = item.substring(colonpos+1);
4225
gnames.add(createGeneralName(t, v));
4226
}
4227
if (exttype == 3) {
4228
ext.set(SubjectAlternativeNameExtension.NAME,
4229
new SubjectAlternativeNameExtension(
4230
isCritical, gnames));
4231
} else {
4232
ext.set(IssuerAlternativeNameExtension.NAME,
4233
new IssuerAlternativeNameExtension(
4234
isCritical, gnames));
4235
}
4236
} else {
4237
throw new Exception(rb.getString
4238
("Illegal.value.") + extstr);
4239
}
4240
break;
4241
case 5: // SIA, always non-critical
4242
case 6: // AIA, always non-critical
4243
if (isCritical) {
4244
throw new Exception(rb.getString(
4245
"This.extension.cannot.be.marked.as.critical.") + extstr);
4246
}
4247
if(value != null) {
4248
List<AccessDescription> accessDescriptions =
4249
new ArrayList<>();
4250
String[] ps = value.split(",");
4251
for(String item: ps) {
4252
colonpos = item.indexOf(':');
4253
int colonpos2 = item.indexOf(':', colonpos+1);
4254
if (colonpos < 0 || colonpos2 < 0) {
4255
throw new Exception(rb.getString
4256
("Illegal.value.") + extstr);
4257
}
4258
String m = item.substring(0, colonpos);
4259
String t = item.substring(colonpos+1, colonpos2);
4260
String v = item.substring(colonpos2+1);
4261
int p = oneOf(m,
4262
"",
4263
"ocsp", //1
4264
"caIssuers", //2
4265
"timeStamping", //3
4266
"",
4267
"caRepository" //5
4268
);
4269
ObjectIdentifier oid;
4270
if (p < 0) {
4271
try {
4272
oid = new ObjectIdentifier(m);
4273
} catch (Exception e) {
4274
throw new Exception(rb.getString(
4275
"Unknown.AccessDescription.type.") + m);
4276
}
4277
} else {
4278
oid = new ObjectIdentifier("1.3.6.1.5.5.7.48." + p);
4279
}
4280
accessDescriptions.add(new AccessDescription(
4281
oid, createGeneralName(t, v)));
4282
}
4283
if (exttype == 5) {
4284
ext.set(SubjectInfoAccessExtension.NAME,
4285
new SubjectInfoAccessExtension(accessDescriptions));
4286
} else {
4287
ext.set(AuthorityInfoAccessExtension.NAME,
4288
new AuthorityInfoAccessExtension(accessDescriptions));
4289
}
4290
} else {
4291
throw new Exception(rb.getString
4292
("Illegal.value.") + extstr);
4293
}
4294
break;
4295
case 8: // CRL, experimental, only support 1 distributionpoint
4296
if(value != null) {
4297
String[] ps = value.split(",");
4298
GeneralNames gnames = new GeneralNames();
4299
for(String item: ps) {
4300
colonpos = item.indexOf(':');
4301
if (colonpos < 0) {
4302
throw new Exception("Illegal item " + item + " in " + extstr);
4303
}
4304
String t = item.substring(0, colonpos);
4305
String v = item.substring(colonpos+1);
4306
gnames.add(createGeneralName(t, v));
4307
}
4308
ext.set(CRLDistributionPointsExtension.NAME,
4309
new CRLDistributionPointsExtension(
4310
isCritical, Collections.singletonList(
4311
new DistributionPoint(gnames, null, null))));
4312
} else {
4313
throw new Exception(rb.getString
4314
("Illegal.value.") + extstr);
4315
}
4316
break;
4317
case -1:
4318
ObjectIdentifier oid = new ObjectIdentifier(name);
4319
byte[] data = null;
4320
if (value != null) {
4321
data = new byte[value.length() / 2 + 1];
4322
int pos = 0;
4323
for (char c: value.toCharArray()) {
4324
int hex;
4325
if (c >= '0' && c <= '9') {
4326
hex = c - '0' ;
4327
} else if (c >= 'A' && c <= 'F') {
4328
hex = c - 'A' + 10;
4329
} else if (c >= 'a' && c <= 'f') {
4330
hex = c - 'a' + 10;
4331
} else {
4332
continue;
4333
}
4334
if (pos % 2 == 0) {
4335
data[pos/2] = (byte)(hex << 4);
4336
} else {
4337
data[pos/2] += hex;
4338
}
4339
pos++;
4340
}
4341
if (pos % 2 != 0) {
4342
throw new Exception(rb.getString(
4343
"Odd.number.of.hex.digits.found.") + extstr);
4344
}
4345
data = Arrays.copyOf(data, pos/2);
4346
} else {
4347
data = new byte[0];
4348
}
4349
ext.set(oid.toString(), new Extension(oid, isCritical,
4350
new DerValue(DerValue.tag_OctetString, data)
4351
.toByteArray()));
4352
break;
4353
default:
4354
throw new Exception(rb.getString(
4355
"Unknown.extension.type.") + extstr);
4356
}
4357
}
4358
// always non-critical
4359
ext.set(SubjectKeyIdentifierExtension.NAME,
4360
new SubjectKeyIdentifierExtension(
4361
new KeyIdentifier(pkey).getIdentifier()));
4362
if (akey != null && !pkey.equals(akey)) {
4363
ext.set(AuthorityKeyIdentifierExtension.NAME,
4364
new AuthorityKeyIdentifierExtension(
4365
new KeyIdentifier(akey), null, null));
4366
}
4367
} catch(IOException e) {
4368
throw new RuntimeException(e);
4369
}
4370
return ext;
4371
}
4372
4373
private boolean isTrustedCert(Certificate cert) throws KeyStoreException {
4374
if (caks != null && caks.getCertificateAlias(cert) != null) {
4375
return true;
4376
} else {
4377
String inKS = keyStore.getCertificateAlias(cert);
4378
return inKS != null && keyStore.isCertificateEntry(inKS);
4379
}
4380
}
4381
4382
private void checkWeak(String label, String sigAlg, Key key) {
4383
if (sigAlg != null) {
4384
if (!DISABLED_CHECK.permits(SIG_PRIMITIVE_SET, sigAlg, null)) {
4385
weakWarnings.add(String.format(
4386
rb.getString("whose.sigalg.disabled"), label, sigAlg));
4387
} else if (!LEGACY_CHECK.permits(SIG_PRIMITIVE_SET, sigAlg, null)) {
4388
weakWarnings.add(String.format(
4389
rb.getString("whose.sigalg.weak"), label, sigAlg));
4390
}
4391
}
4392
4393
if (key != null) {
4394
if (!DISABLED_CHECK.permits(SIG_PRIMITIVE_SET, key)) {
4395
weakWarnings.add(String.format(
4396
rb.getString("whose.key.disabled"), label,
4397
String.format(rb.getString("key.bit"),
4398
KeyUtil.getKeySize(key), fullDisplayAlgName(key))));
4399
} else if (!LEGACY_CHECK.permits(SIG_PRIMITIVE_SET, key)) {
4400
weakWarnings.add(String.format(
4401
rb.getString("whose.key.weak"), label,
4402
String.format(rb.getString("key.bit"),
4403
KeyUtil.getKeySize(key), fullDisplayAlgName(key))));
4404
}
4405
}
4406
}
4407
4408
private void checkWeak(String label, Certificate[] certs)
4409
throws KeyStoreException {
4410
for (int i = 0; i < certs.length; i++) {
4411
Certificate cert = certs[i];
4412
if (cert instanceof X509Certificate) {
4413
X509Certificate xc = (X509Certificate)cert;
4414
String fullLabel = label;
4415
if (certs.length > 1) {
4416
fullLabel = oneInMany(label, i, certs.length);
4417
}
4418
checkWeak(fullLabel, xc);
4419
}
4420
}
4421
}
4422
4423
private void checkWeak(String label, Certificate cert)
4424
throws KeyStoreException {
4425
if (cert instanceof X509Certificate) {
4426
X509Certificate xc = (X509Certificate)cert;
4427
// No need to check the sigalg of a trust anchor
4428
String sigAlg = isTrustedCert(cert) ? null : xc.getSigAlgName();
4429
checkWeak(label, sigAlg, xc.getPublicKey());
4430
}
4431
}
4432
4433
private void checkWeak(String label, PKCS10 p10) {
4434
checkWeak(label, p10.getSigAlg(), p10.getSubjectPublicKeyInfo());
4435
}
4436
4437
private void checkWeak(String label, CRL crl, Key key) {
4438
if (crl instanceof X509CRLImpl) {
4439
X509CRLImpl impl = (X509CRLImpl)crl;
4440
checkWeak(label, impl.getSigAlgName(), key);
4441
}
4442
}
4443
4444
private void printWeakWarnings(boolean newLine) {
4445
if (!weakWarnings.isEmpty() && !nowarn) {
4446
System.err.println("\nWarning:");
4447
for (String warning : weakWarnings) {
4448
System.err.println(warning);
4449
}
4450
if (newLine) {
4451
// When calling before a yes/no prompt, add a new line
4452
System.err.println();
4453
}
4454
}
4455
weakWarnings.clear();
4456
}
4457
4458
/**
4459
* Prints the usage of this tool.
4460
*/
4461
private void usage() {
4462
if (command != null) {
4463
System.err.println("keytool " + command +
4464
rb.getString(".OPTION."));
4465
System.err.println();
4466
System.err.println(rb.getString(command.description));
4467
System.err.println();
4468
System.err.println(rb.getString("Options."));
4469
System.err.println();
4470
4471
// Left and right sides of the options list
4472
String[] left = new String[command.options.length];
4473
String[] right = new String[command.options.length];
4474
4475
// Check if there's an unknown option
4476
boolean found = false;
4477
4478
// Length of left side of options list
4479
int lenLeft = 0;
4480
for (int j=0; j<left.length; j++) {
4481
Option opt = command.options[j];
4482
left[j] = opt.toString();
4483
if (opt.arg != null) left[j] += " " + opt.arg;
4484
if (left[j].length() > lenLeft) {
4485
lenLeft = left[j].length();
4486
}
4487
right[j] = rb.getString(opt.description);
4488
}
4489
for (int j=0; j<left.length; j++) {
4490
System.err.printf(" %-" + lenLeft + "s %s\n",
4491
left[j], right[j]);
4492
}
4493
System.err.println();
4494
System.err.println(rb.getString(
4495
"Use.keytool.help.for.all.available.commands"));
4496
} else {
4497
System.err.println(rb.getString(
4498
"Key.and.Certificate.Management.Tool"));
4499
System.err.println();
4500
System.err.println(rb.getString("Commands."));
4501
System.err.println();
4502
for (Command c: Command.values()) {
4503
if (c == KEYCLONE) break;
4504
System.err.printf(" %-20s%s\n", c, rb.getString(c.description));
4505
}
4506
System.err.println();
4507
System.err.println(rb.getString(
4508
"Use.keytool.command.name.help.for.usage.of.command.name"));
4509
}
4510
}
4511
4512
private void tinyHelp() {
4513
usage();
4514
if (debug) {
4515
throw new RuntimeException("NO BIG ERROR, SORRY");
4516
} else {
4517
System.exit(1);
4518
}
4519
}
4520
4521
private void errorNeedArgument(String flag) {
4522
Object[] source = {flag};
4523
System.err.println(new MessageFormat(
4524
rb.getString("Command.option.flag.needs.an.argument.")).format(source));
4525
tinyHelp();
4526
}
4527
4528
private char[] getPass(String modifier, String arg) {
4529
char[] output = KeyStoreUtil.getPassWithModifier(modifier, arg, rb);
4530
if (output != null) return output;
4531
tinyHelp();
4532
return null; // Useless, tinyHelp() already exits.
4533
}
4534
}
4535
4536
// This class is exactly the same as com.sun.tools.javac.util.Pair,
4537
// it's copied here since the original one is not included in JRE.
4538
class Pair<A, B> {
4539
4540
public final A fst;
4541
public final B snd;
4542
4543
public Pair(A fst, B snd) {
4544
this.fst = fst;
4545
this.snd = snd;
4546
}
4547
4548
public String toString() {
4549
return "Pair[" + fst + "," + snd + "]";
4550
}
4551
4552
public boolean equals(Object other) {
4553
return
4554
other instanceof Pair &&
4555
Objects.equals(fst, ((Pair)other).fst) &&
4556
Objects.equals(snd, ((Pair)other).snd);
4557
}
4558
4559
public int hashCode() {
4560
if (fst == null) return (snd == null) ? 0 : snd.hashCode() + 1;
4561
else if (snd == null) return fst.hashCode() + 2;
4562
else return fst.hashCode() * 17 + snd.hashCode();
4563
}
4564
4565
public static <A,B> Pair<A,B> of(A a, B b) {
4566
return new Pair<>(a,b);
4567
}
4568
}
4569
4570
4571