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