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