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