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