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             int kLen = KeyUtil.getKeySize(key);
3095             if (kLen >= 0) {
3096                 return String.format(rb.getString("key.bit"),
3097                         kLen, key.getAlgorithm());
3098             } else {
3099                 return String.format(
3100                         rb.getString("unknown.size.1"), key.getAlgorithm());
3101             }
3102         } else {
3103             return String.format(rb.getString("key.bit.weak"),
3104                     KeyUtil.getKeySize(key), key.getAlgorithm());
3105         }
3106     }
3107 
3108     /**
3109      * Prints a certificate in a human readable format.
3110      */
3111     private void printX509Cert(X509Certificate cert, PrintStream out)
3112         throws Exception
3113     {
3114 
3115         MessageFormat form = new MessageFormat
3116                 (rb.getString(".PATTERN.printX509Cert.with.weak"));
3117         PublicKey pkey = cert.getPublicKey();
3118         String sigName = cert.getSigAlgName();
3119         // No need to warn about sigalg of a trust anchor
3120         if (!isTrustedCert(cert)) {
3121             sigName = withWeak(sigName);
3122         }
3123         Object[] source = {cert.getSubjectDN().toString(),
3124                         cert.getIssuerDN().toString(),
3125                         cert.getSerialNumber().toString(16),
3126                         cert.getNotBefore().toString(),
3127                         cert.getNotAfter().toString(),
3128                         getCertFingerPrint("SHA-1", cert),
3129                         getCertFingerPrint("SHA-256", cert),
3130                         sigName,
3131                         withWeak(pkey),
3132                         cert.getVersion()
3133                         };
3134         out.println(form.format(source));
3135 
3136         if (cert instanceof X509CertImpl) {
3137             X509CertImpl impl = (X509CertImpl)cert;
3138             X509CertInfo certInfo = (X509CertInfo)impl.get(X509CertImpl.NAME
3139                                                            + "." +
3140                                                            X509CertImpl.INFO);
3141             CertificateExtensions exts = (CertificateExtensions)
3142                     certInfo.get(X509CertInfo.EXTENSIONS);
3143             if (exts != null) {
3144                 printExtensions(rb.getString("Extensions."), exts, out);
3145             }
3146         }
3147     }
3148 
3149     private static void printExtensions(String title, CertificateExtensions exts, PrintStream out)
3150             throws Exception {
3151         int extnum = 0;
3152         Iterator<Extension> i1 = exts.getAllExtensions().iterator();
3153         Iterator<Extension> i2 = exts.getUnparseableExtensions().values().iterator();
3154         while (i1.hasNext() || i2.hasNext()) {
3155             Extension ext = i1.hasNext()?i1.next():i2.next();
3156             if (extnum == 0) {
3157                 out.println();
3158                 out.println(title);
3159                 out.println();
3160             }
3161             out.print("#"+(++extnum)+": "+ ext);
3162             if (ext.getClass() == Extension.class) {
3163                 byte[] v = ext.getExtensionValue();
3164                 if (v.length == 0) {
3165                     out.println(rb.getString(".Empty.value."));
3166                 } else {
3167                     new sun.misc.HexDumpEncoder().encodeBuffer(ext.getExtensionValue(), out);
3168                     out.println();
3169                 }
3170             }
3171             out.println();
3172         }
3173     }
3174 
3175     /**
3176      * Locates a signer for a given certificate from a given keystore and
3177      * returns the signer's certificate.
3178      * @param cert the certificate whose signer is searched, not null
3179      * @param ks the keystore to search with, not null
3180      * @return <code>cert</code> itself if it's already inside <code>ks</code>,
3181      * or a certificate inside <code>ks</code> who signs <code>cert</code>,
3182      * or null otherwise. A label is added.
3183      */
3184     private static Pair<String,Certificate>
3185             getSigner(Certificate cert, KeyStore ks) throws Exception {
3186         if (ks.getCertificateAlias(cert) != null) {
3187             return new Pair<>("", cert);
3188         }
3189         for (Enumeration<String> aliases = ks.aliases();
3190                 aliases.hasMoreElements(); ) {
3191             String name = aliases.nextElement();
3192             Certificate trustedCert = ks.getCertificate(name);
3193             if (trustedCert != null) {
3194                 try {
3195                     cert.verify(trustedCert.getPublicKey());
3196                     return new Pair<>(name, trustedCert);
3197                 } catch (Exception e) {
3198                     // Not verified, skip to the next one
3199                 }
3200             }
3201         }
3202         return null;
3203     }
3204 
3205     /**
3206      * Gets an X.500 name suitable for inclusion in a certification request.
3207      */
3208     private X500Name getX500Name() throws IOException {
3209         BufferedReader in;
3210         in = new BufferedReader(new InputStreamReader(System.in));
3211         String commonName = "Unknown";
3212         String organizationalUnit = "Unknown";
3213         String organization = "Unknown";
3214         String city = "Unknown";
3215         String state = "Unknown";
3216         String country = "Unknown";
3217         X500Name name;
3218         String userInput = null;
3219 
3220         int maxRetry = 20;
3221         do {
3222             if (maxRetry-- < 0) {
3223                 throw new RuntimeException(rb.getString(
3224                         "Too.many.retries.program.terminated"));
3225             }
3226             commonName = inputString(in,
3227                     rb.getString("What.is.your.first.and.last.name."),
3228                     commonName);
3229             organizationalUnit = inputString(in,
3230                     rb.getString
3231                         ("What.is.the.name.of.your.organizational.unit."),
3232                     organizationalUnit);
3233             organization = inputString(in,
3234                     rb.getString("What.is.the.name.of.your.organization."),
3235                     organization);
3236             city = inputString(in,
3237                     rb.getString("What.is.the.name.of.your.City.or.Locality."),
3238                     city);
3239             state = inputString(in,
3240                     rb.getString("What.is.the.name.of.your.State.or.Province."),
3241                     state);
3242             country = inputString(in,
3243                     rb.getString
3244                         ("What.is.the.two.letter.country.code.for.this.unit."),
3245                     country);
3246             name = new X500Name(commonName, organizationalUnit, organization,
3247                                 city, state, country);
3248             MessageFormat form = new MessageFormat
3249                 (rb.getString("Is.name.correct."));
3250             Object[] source = {name};
3251             userInput = inputString
3252                 (in, form.format(source), rb.getString("no"));
3253         } while (collator.compare(userInput, rb.getString("yes")) != 0 &&
3254                  collator.compare(userInput, rb.getString("y")) != 0);
3255 
3256         System.err.println();
3257         return name;
3258     }
3259 
3260     private String inputString(BufferedReader in, String prompt,
3261                                String defaultValue)
3262         throws IOException
3263     {
3264         System.err.println(prompt);
3265         MessageFormat form = new MessageFormat
3266                 (rb.getString(".defaultValue."));
3267         Object[] source = {defaultValue};
3268         System.err.print(form.format(source));
3269         System.err.flush();
3270 
3271         String value = in.readLine();
3272         if (value == null || collator.compare(value, "") == 0) {
3273             value = defaultValue;
3274         }
3275         return value;
3276     }
3277 
3278     /**
3279      * Writes an X.509 certificate in base64 or binary encoding to an output
3280      * stream.
3281      */
3282     private void dumpCert(Certificate cert, PrintStream out)
3283         throws IOException, CertificateException
3284     {
3285         if (rfc) {
3286             out.println(X509Factory.BEGIN_CERT);
3287             out.println(Base64.getMimeEncoder(64, CRLF).encodeToString(cert.getEncoded()));
3288             out.println(X509Factory.END_CERT);
3289         } else {
3290             out.write(cert.getEncoded()); // binary
3291         }
3292     }
3293 
3294     /**
3295      * Converts a byte to hex digit and writes to the supplied buffer
3296      */
3297     private void byte2hex(byte b, StringBuffer buf) {
3298         char[] hexChars = { '0', '1', '2', '3', '4', '5', '6', '7', '8',
3299                             '9', 'A', 'B', 'C', 'D', 'E', 'F' };
3300         int high = ((b & 0xf0) >> 4);
3301         int low = (b & 0x0f);
3302         buf.append(hexChars[high]);
3303         buf.append(hexChars[low]);
3304     }
3305 
3306     /**
3307      * Converts a byte array to hex string
3308      */
3309     private String toHexString(byte[] block) {
3310         StringBuffer buf = new StringBuffer();
3311         int len = block.length;
3312         for (int i = 0; i < len; i++) {
3313              byte2hex(block[i], buf);
3314              if (i < len-1) {
3315                  buf.append(":");
3316              }
3317         }
3318         return buf.toString();
3319     }
3320 
3321     /**
3322      * Recovers (private) key associated with given alias.
3323      *
3324      * @return an array of objects, where the 1st element in the array is the
3325      * recovered private key, and the 2nd element is the password used to
3326      * recover it.
3327      */
3328     private Pair<Key,char[]> recoverKey(String alias, char[] storePass,
3329                                        char[] keyPass)
3330         throws Exception
3331     {
3332         Key key = null;
3333 
3334         if (KeyStoreUtil.isWindowsKeyStore(storetype)) {
3335             key = keyStore.getKey(alias, null);
3336             return Pair.of(key, null);
3337         }
3338 
3339         if (keyStore.containsAlias(alias) == false) {
3340             MessageFormat form = new MessageFormat
3341                 (rb.getString("Alias.alias.does.not.exist"));
3342             Object[] source = {alias};
3343             throw new Exception(form.format(source));
3344         }
3345         if (!keyStore.entryInstanceOf(alias, KeyStore.PrivateKeyEntry.class) &&
3346                 !keyStore.entryInstanceOf(alias, KeyStore.SecretKeyEntry.class)) {
3347             MessageFormat form = new MessageFormat
3348                 (rb.getString("Alias.alias.has.no.key"));
3349             Object[] source = {alias};
3350             throw new Exception(form.format(source));
3351         }
3352 
3353         if (keyPass == null) {
3354             // Try to recover the key using the keystore password
3355             try {
3356                 key = keyStore.getKey(alias, storePass);
3357 
3358                 keyPass = storePass;
3359                 passwords.add(keyPass);
3360             } catch (UnrecoverableKeyException e) {
3361                 // Did not work out, so prompt user for key password
3362                 if (!token) {
3363                     keyPass = getKeyPasswd(alias, null, null);
3364                     key = keyStore.getKey(alias, keyPass);
3365                 } else {
3366                     throw e;
3367                 }
3368             }
3369         } else {
3370             key = keyStore.getKey(alias, keyPass);
3371         }
3372 
3373         return Pair.of(key, keyPass);
3374     }
3375 
3376     /**
3377      * Recovers entry associated with given alias.
3378      *
3379      * @return an array of objects, where the 1st element in the array is the
3380      * recovered entry, and the 2nd element is the password used to
3381      * recover it (null if no password).
3382      */
3383     private Pair<Entry,char[]> recoverEntry(KeyStore ks,
3384                             String alias,
3385                             char[] pstore,
3386                             char[] pkey) throws Exception {
3387 
3388         if (ks.containsAlias(alias) == false) {
3389             MessageFormat form = new MessageFormat
3390                 (rb.getString("Alias.alias.does.not.exist"));
3391             Object[] source = {alias};
3392             throw new Exception(form.format(source));
3393         }
3394 
3395         PasswordProtection pp = null;
3396         Entry entry;
3397 
3398         try {
3399             // First attempt to access entry without key password
3400             // (PKCS11 entry or trusted certificate entry, for example)
3401 
3402             entry = ks.getEntry(alias, pp);
3403             pkey = null;
3404         } catch (UnrecoverableEntryException une) {
3405 
3406             if(P11KEYSTORE.equalsIgnoreCase(ks.getType()) ||
3407                 KeyStoreUtil.isWindowsKeyStore(ks.getType())) {
3408                 // should not happen, but a possibility
3409                 throw une;
3410             }
3411 
3412             // entry is protected
3413 
3414             if (pkey != null) {
3415 
3416                 // try provided key password
3417 
3418                 pp = new PasswordProtection(pkey);
3419                 entry = ks.getEntry(alias, pp);
3420 
3421             } else {
3422 
3423                 // try store pass
3424 
3425                 try {
3426                     pp = new PasswordProtection(pstore);
3427                     entry = ks.getEntry(alias, pp);
3428                     pkey = pstore;
3429                 } catch (UnrecoverableEntryException une2) {
3430                     if (P12KEYSTORE.equalsIgnoreCase(ks.getType())) {
3431 
3432                         // P12 keystore currently does not support separate
3433                         // store and entry passwords
3434 
3435                         throw une2;
3436                     } else {
3437 
3438                         // prompt for entry password
3439 
3440                         pkey = getKeyPasswd(alias, null, null);
3441                         pp = new PasswordProtection(pkey);
3442                         entry = ks.getEntry(alias, pp);
3443                     }
3444                 }
3445             }
3446         }
3447 
3448         return Pair.of(entry, pkey);
3449     }
3450     /**
3451      * Gets the requested finger print of the certificate.
3452      */
3453     private String getCertFingerPrint(String mdAlg, Certificate cert)
3454         throws Exception
3455     {
3456         byte[] encCertInfo = cert.getEncoded();
3457         MessageDigest md = MessageDigest.getInstance(mdAlg);
3458         byte[] digest = md.digest(encCertInfo);
3459         return toHexString(digest);
3460     }
3461 
3462     /**
3463      * Prints warning about missing integrity check.
3464      */
3465     private void printNoIntegrityWarning() {
3466         System.err.println();
3467         System.err.println(rb.getString
3468             (".WARNING.WARNING.WARNING."));
3469         System.err.println(rb.getString
3470             (".The.integrity.of.the.information.stored.in.your.keystore."));
3471         System.err.println(rb.getString
3472             (".WARNING.WARNING.WARNING."));
3473         System.err.println();
3474     }
3475 
3476     /**
3477      * Validates chain in certification reply, and returns the ordered
3478      * elements of the chain (with user certificate first, and root
3479      * certificate last in the array).
3480      *
3481      * @param alias the alias name
3482      * @param userCert the user certificate of the alias
3483      * @param replyCerts the chain provided in the reply
3484      */
3485     private Certificate[] validateReply(String alias,
3486                                         Certificate userCert,
3487                                         Certificate[] replyCerts)
3488         throws Exception
3489     {
3490 
3491         checkWeak(rb.getString("reply"), replyCerts);
3492 
3493         // order the certs in the reply (bottom-up).
3494         // we know that all certs in the reply are of type X.509, because
3495         // we parsed them using an X.509 certificate factory
3496         int i;
3497         PublicKey userPubKey = userCert.getPublicKey();
3498         for (i=0; i<replyCerts.length; i++) {
3499             if (userPubKey.equals(replyCerts[i].getPublicKey())) {
3500                 break;
3501             }
3502         }
3503         if (i == replyCerts.length) {
3504             MessageFormat form = new MessageFormat(rb.getString
3505                 ("Certificate.reply.does.not.contain.public.key.for.alias."));
3506             Object[] source = {alias};
3507             throw new Exception(form.format(source));
3508         }
3509 
3510         Certificate tmpCert = replyCerts[0];
3511         replyCerts[0] = replyCerts[i];
3512         replyCerts[i] = tmpCert;
3513 
3514         X509Certificate thisCert = (X509Certificate)replyCerts[0];
3515 
3516         for (i=1; i < replyCerts.length-1; i++) {
3517             // find a cert in the reply who signs thisCert
3518             int j;
3519             for (j=i; j<replyCerts.length; j++) {
3520                 if (KeyStoreUtil.signedBy(thisCert, (X509Certificate)replyCerts[j])) {
3521                     tmpCert = replyCerts[i];
3522                     replyCerts[i] = replyCerts[j];
3523                     replyCerts[j] = tmpCert;
3524                     thisCert = (X509Certificate)replyCerts[i];
3525                     break;
3526                 }
3527             }
3528             if (j == replyCerts.length) {
3529                 throw new Exception
3530                     (rb.getString("Incomplete.certificate.chain.in.reply"));
3531             }
3532         }
3533 
3534         if (noprompt) {
3535             return replyCerts;
3536         }
3537 
3538         // do we trust the cert at the top?
3539         Certificate topCert = replyCerts[replyCerts.length-1];
3540         boolean fromKeyStore = true;
3541         Pair<String,Certificate> root = getSigner(topCert, keyStore);
3542         if (root == null && trustcacerts && caks != null) {
3543             root = getSigner(topCert, caks);
3544             fromKeyStore = false;
3545         }
3546         if (root == null) {
3547             System.err.println();
3548             System.err.println
3549                     (rb.getString("Top.level.certificate.in.reply."));
3550             printX509Cert((X509Certificate)topCert, System.out);
3551             System.err.println();
3552             System.err.print(rb.getString(".is.not.trusted."));
3553             printWeakWarnings(true);
3554             String reply = getYesNoReply
3555                     (rb.getString("Install.reply.anyway.no."));
3556             if ("NO".equals(reply)) {
3557                 return null;
3558             }
3559         } else {
3560             if (root.snd != topCert) {
3561                 // append the root CA cert to the chain
3562                 Certificate[] tmpCerts =
3563                     new Certificate[replyCerts.length+1];
3564                 System.arraycopy(replyCerts, 0, tmpCerts, 0,
3565                                  replyCerts.length);
3566                 tmpCerts[tmpCerts.length-1] = root.snd;
3567                 replyCerts = tmpCerts;
3568                 checkWeak(String.format(rb.getString(fromKeyStore ?
3569                                             "alias.in.keystore" :
3570                                             "alias.in.cacerts"),
3571                                         root.fst),
3572                           root.snd);
3573             }
3574         }
3575         return replyCerts;
3576     }
3577 
3578     /**
3579      * Establishes a certificate chain (using trusted certificates in the
3580      * keystore and cacerts), starting with the reply (certToVerify)
3581      * and ending at a self-signed certificate found in the keystore.
3582      *
3583      * @param userCert optional existing certificate, mostly likely be the
3584      *                 original self-signed cert created by -genkeypair.
3585      *                 It must have the same public key as certToVerify
3586      *                 but cannot be the same cert.
3587      * @param certToVerify the starting certificate to build the chain
3588      * @returns the established chain, might be null if user decides not
3589      */
3590     private Certificate[] establishCertChain(Certificate userCert,
3591                                              Certificate certToVerify)
3592         throws Exception
3593     {
3594         if (userCert != null) {
3595             // Make sure that the public key of the certificate reply matches
3596             // the original public key in the keystore
3597             PublicKey origPubKey = userCert.getPublicKey();
3598             PublicKey replyPubKey = certToVerify.getPublicKey();
3599             if (!origPubKey.equals(replyPubKey)) {
3600                 throw new Exception(rb.getString
3601                         ("Public.keys.in.reply.and.keystore.don.t.match"));
3602             }
3603 
3604             // If the two certs are identical, we're done: no need to import
3605             // anything
3606             if (certToVerify.equals(userCert)) {
3607                 throw new Exception(rb.getString
3608                         ("Certificate.reply.and.certificate.in.keystore.are.identical"));
3609             }
3610         }
3611 
3612         // Build a hash table of all certificates in the keystore.
3613         // Use the subject distinguished name as the key into the hash table.
3614         // All certificates associated with the same subject distinguished
3615         // name are stored in the same hash table entry as a vector.
3616         Hashtable<Principal, Vector<Pair<String,X509Certificate>>> certs = null;
3617         if (keyStore.size() > 0) {
3618             certs = new Hashtable<>(11);
3619             keystorecerts2Hashtable(keyStore, certs);
3620         }
3621         if (trustcacerts) {
3622             if (caks!=null && caks.size()>0) {
3623                 if (certs == null) {
3624                     certs = new Hashtable<>(11);
3625                 }
3626                 keystorecerts2Hashtable(caks, certs);
3627             }
3628         }
3629 
3630         // start building chain
3631         Vector<Pair<String,X509Certificate>> chain = new Vector<>(2);
3632         if (buildChain(
3633                 new Pair<>(rb.getString("the.input"),
3634                            (X509Certificate) certToVerify),
3635                 chain, certs)) {
3636             for (Pair<String,X509Certificate> p : chain) {
3637                 checkWeak(p.fst, p.snd);
3638             }
3639             Certificate[] newChain =
3640                     new Certificate[chain.size()];
3641             // buildChain() returns chain with self-signed root-cert first and
3642             // user-cert last, so we need to invert the chain before we store
3643             // it
3644             int j=0;
3645             for (int i=chain.size()-1; i>=0; i--) {
3646                 newChain[j] = chain.elementAt(i).snd;
3647                 j++;
3648             }
3649             return newChain;
3650         } else {
3651             throw new Exception
3652                 (rb.getString("Failed.to.establish.chain.from.reply"));
3653         }
3654     }
3655 
3656     /**
3657      * Recursively tries to establish chain from pool of certs starting from
3658      * certToVerify until a self-signed cert is found, and fill the certs found
3659      * into chain. Each cert in the chain signs the next one.
3660      *
3661      * This method is able to recover from an error, say, if certToVerify
3662      * is signed by certA but certA has no issuer in certs and itself is not
3663      * self-signed, the method can try another certB that also signs
3664      * certToVerify and look for signer of certB, etc, etc.
3665      *
3666      * Each cert in chain comes with a label showing its origin. The label is
3667      * used in the warning message when the cert is considered a risk.
3668      *
3669      * @param certToVerify the cert that needs to be verified.
3670      * @param chain the chain that's being built.
3671      * @param certs the pool of trusted certs
3672      *
3673      * @return true if successful, false otherwise.
3674      */
3675     private boolean buildChain(Pair<String,X509Certificate> certToVerify,
3676             Vector<Pair<String,X509Certificate>> chain,
3677             Hashtable<Principal, Vector<Pair<String,X509Certificate>>> certs) {
3678         if (KeyStoreUtil.isSelfSigned(certToVerify.snd)) {
3679             // reached self-signed root cert;
3680             // no verification needed because it's trusted.
3681             chain.addElement(certToVerify);
3682             return true;
3683         }
3684 
3685         Principal issuer = certToVerify.snd.getIssuerDN();
3686 
3687         // Get the issuer's certificate(s)
3688         Vector<Pair<String,X509Certificate>> vec = certs.get(issuer);
3689         if (vec == null) {
3690             return false;
3691         }
3692 
3693         // Try out each certificate in the vector, until we find one
3694         // whose public key verifies the signature of the certificate
3695         // in question.
3696         for (Enumeration<Pair<String,X509Certificate>> issuerCerts = vec.elements();
3697              issuerCerts.hasMoreElements(); ) {
3698             Pair<String,X509Certificate> issuerCert = issuerCerts.nextElement();
3699             PublicKey issuerPubKey = issuerCert.snd.getPublicKey();
3700             try {
3701                 certToVerify.snd.verify(issuerPubKey);
3702             } catch (Exception e) {
3703                 continue;
3704             }
3705             if (buildChain(issuerCert, chain, certs)) {
3706                 chain.addElement(certToVerify);
3707                 return true;
3708             }
3709         }
3710         return false;
3711     }
3712 
3713     /**
3714      * Prompts user for yes/no decision.
3715      *
3716      * @return the user's decision, can only be "YES" or "NO"
3717      */
3718     private String getYesNoReply(String prompt)
3719         throws IOException
3720     {
3721         String reply = null;
3722         int maxRetry = 20;
3723         do {
3724             if (maxRetry-- < 0) {
3725                 throw new RuntimeException(rb.getString(
3726                         "Too.many.retries.program.terminated"));
3727             }
3728             System.err.print(prompt);
3729             System.err.flush();
3730             reply = (new BufferedReader(new InputStreamReader
3731                                         (System.in))).readLine();
3732             if (collator.compare(reply, "") == 0 ||
3733                 collator.compare(reply, rb.getString("n")) == 0 ||
3734                 collator.compare(reply, rb.getString("no")) == 0) {
3735                 reply = "NO";
3736             } else if (collator.compare(reply, rb.getString("y")) == 0 ||
3737                        collator.compare(reply, rb.getString("yes")) == 0) {
3738                 reply = "YES";
3739             } else {
3740                 System.err.println(rb.getString("Wrong.answer.try.again"));
3741                 reply = null;
3742             }
3743         } while (reply == null);
3744         return reply;
3745     }
3746 
3747     /**
3748      * Stores the (leaf) certificates of a keystore in a hashtable.
3749      * All certs belonging to the same CA are stored in a vector that
3750      * in turn is stored in the hashtable, keyed by the CA's subject DN.
3751      * Each cert comes with a string label that shows its origin and alias.
3752      */
3753     private void keystorecerts2Hashtable(KeyStore ks,
3754                 Hashtable<Principal, Vector<Pair<String,X509Certificate>>> hash)
3755         throws Exception {
3756 
3757         for (Enumeration<String> aliases = ks.aliases();
3758                                         aliases.hasMoreElements(); ) {
3759             String alias = aliases.nextElement();
3760             Certificate cert = ks.getCertificate(alias);
3761             if (cert != null) {
3762                 Principal subjectDN = ((X509Certificate)cert).getSubjectDN();
3763                 Pair<String,X509Certificate> pair = new Pair<>(
3764                         String.format(
3765                                 rb.getString(ks == caks ?
3766                                         "alias.in.cacerts" :
3767                                         "alias.in.keystore"),
3768                                 alias),
3769                         (X509Certificate)cert);
3770                 Vector<Pair<String,X509Certificate>> vec = hash.get(subjectDN);
3771                 if (vec == null) {
3772                     vec = new Vector<>();
3773                     vec.addElement(pair);
3774                 } else {
3775                     if (!vec.contains(pair)) {
3776                         vec.addElement(pair);
3777                     }
3778                 }
3779                 hash.put(subjectDN, vec);
3780             }
3781         }
3782     }
3783 
3784     /**
3785      * Returns the issue time that's specified the -startdate option
3786      * @param s the value of -startdate option
3787      */
3788     private static Date getStartDate(String s) throws IOException {
3789         Calendar c = new GregorianCalendar();
3790         if (s != null) {
3791             IOException ioe = new IOException(
3792                     rb.getString("Illegal.startdate.value"));
3793             int len = s.length();
3794             if (len == 0) {
3795                 throw ioe;
3796             }
3797             if (s.charAt(0) == '-' || s.charAt(0) == '+') {
3798                 // Form 1: ([+-]nnn[ymdHMS])+
3799                 int start = 0;
3800                 while (start < len) {
3801                     int sign = 0;
3802                     switch (s.charAt(start)) {
3803                         case '+': sign = 1; break;
3804                         case '-': sign = -1; break;
3805                         default: throw ioe;
3806                     }
3807                     int i = start+1;
3808                     for (; i<len; i++) {
3809                         char ch = s.charAt(i);
3810                         if (ch < '0' || ch > '9') break;
3811                     }
3812                     if (i == start+1) throw ioe;
3813                     int number = Integer.parseInt(s.substring(start+1, i));
3814                     if (i >= len) throw ioe;
3815                     int unit = 0;
3816                     switch (s.charAt(i)) {
3817                         case 'y': unit = Calendar.YEAR; break;
3818                         case 'm': unit = Calendar.MONTH; break;
3819                         case 'd': unit = Calendar.DATE; break;
3820                         case 'H': unit = Calendar.HOUR; break;
3821                         case 'M': unit = Calendar.MINUTE; break;
3822                         case 'S': unit = Calendar.SECOND; break;
3823                         default: throw ioe;
3824                     }
3825                     c.add(unit, sign * number);
3826                     start = i + 1;
3827                 }
3828             } else  {
3829                 // Form 2: [yyyy/mm/dd] [HH:MM:SS]
3830                 String date = null, time = null;
3831                 if (len == 19) {
3832                     date = s.substring(0, 10);
3833                     time = s.substring(11);
3834                     if (s.charAt(10) != ' ')
3835                         throw ioe;
3836                 } else if (len == 10) {
3837                     date = s;
3838                 } else if (len == 8) {
3839                     time = s;
3840                 } else {
3841                     throw ioe;
3842                 }
3843                 if (date != null) {
3844                     if (date.matches("\\d\\d\\d\\d\\/\\d\\d\\/\\d\\d")) {
3845                         c.set(Integer.valueOf(date.substring(0, 4)),
3846                                 Integer.valueOf(date.substring(5, 7))-1,
3847                                 Integer.valueOf(date.substring(8, 10)));
3848                     } else {
3849                         throw ioe;
3850                     }
3851                 }
3852                 if (time != null) {
3853                     if (time.matches("\\d\\d:\\d\\d:\\d\\d")) {
3854                         c.set(Calendar.HOUR_OF_DAY, Integer.valueOf(time.substring(0, 2)));
3855                         c.set(Calendar.MINUTE, Integer.valueOf(time.substring(0, 2)));
3856                         c.set(Calendar.SECOND, Integer.valueOf(time.substring(0, 2)));
3857                         c.set(Calendar.MILLISECOND, 0);
3858                     } else {
3859                         throw ioe;
3860                     }
3861                 }
3862             }
3863         }
3864         return c.getTime();
3865     }
3866 
3867     /**
3868      * Match a command (may be abbreviated) with a command set.
3869      * @param s the command provided
3870      * @param list the legal command set. If there is a null, commands after it
3871      * are regarded experimental, which means they are supported but their
3872      * existence should not be revealed to user.
3873      * @return the position of a single match, or -1 if none matched
3874      * @throws Exception if s is ambiguous
3875      */
3876     private static int oneOf(String s, String... list) throws Exception {
3877         int[] match = new int[list.length];
3878         int nmatch = 0;
3879         int experiment = Integer.MAX_VALUE;
3880         for (int i = 0; i<list.length; i++) {
3881             String one = list[i];
3882             if (one == null) {
3883                 experiment = i;
3884                 continue;
3885             }
3886             if (one.toLowerCase(Locale.ENGLISH)
3887                     .startsWith(s.toLowerCase(Locale.ENGLISH))) {
3888                 match[nmatch++] = i;
3889             } else {
3890                 StringBuffer sb = new StringBuffer();
3891                 boolean first = true;
3892                 for (char c: one.toCharArray()) {
3893                     if (first) {
3894                         sb.append(c);
3895                         first = false;
3896                     } else {
3897                         if (!Character.isLowerCase(c)) {
3898                             sb.append(c);
3899                         }
3900                     }
3901                 }
3902                 if (sb.toString().equalsIgnoreCase(s)) {
3903                     match[nmatch++] = i;
3904                 }
3905             }
3906         }
3907         if (nmatch == 0) {
3908             return -1;
3909         } else if (nmatch == 1) {
3910             return match[0];
3911         } else {
3912             // If multiple matches is in experimental commands, ignore them
3913             if (match[1] > experiment) {
3914                 return match[0];
3915             }
3916             StringBuffer sb = new StringBuffer();
3917             MessageFormat form = new MessageFormat(rb.getString
3918                 ("command.{0}.is.ambiguous."));
3919             Object[] source = {s};
3920             sb.append(form.format(source));
3921             sb.append("\n    ");
3922             for (int i=0; i<nmatch && match[i]<experiment; i++) {
3923                 sb.append(' ');
3924                 sb.append(list[match[i]]);
3925             }
3926             throw new Exception(sb.toString());
3927         }
3928     }
3929 
3930     /**
3931      * Create a GeneralName object from known types
3932      * @param t one of 5 known types
3933      * @param v value
3934      * @return which one
3935      */
3936     private GeneralName createGeneralName(String t, String v)
3937             throws Exception {
3938         GeneralNameInterface gn;
3939         int p = oneOf(t, "EMAIL", "URI", "DNS", "IP", "OID");
3940         if (p < 0) {
3941             throw new Exception(rb.getString(
3942                     "Unrecognized.GeneralName.type.") + t);
3943         }
3944         switch (p) {
3945             case 0: gn = new RFC822Name(v); break;
3946             case 1: gn = new URIName(v); break;
3947             case 2: gn = new DNSName(v); break;
3948             case 3: gn = new IPAddressName(v); break;
3949             default: gn = new OIDName(v); break; //4
3950         }
3951         return new GeneralName(gn);
3952     }
3953 
3954     private static final String[] extSupported = {
3955                         "BasicConstraints",
3956                         "KeyUsage",
3957                         "ExtendedKeyUsage",
3958                         "SubjectAlternativeName",
3959                         "IssuerAlternativeName",
3960                         "SubjectInfoAccess",
3961                         "AuthorityInfoAccess",
3962                         null,
3963                         "CRLDistributionPoints",
3964     };
3965 
3966     private ObjectIdentifier findOidForExtName(String type)
3967             throws Exception {
3968         switch (oneOf(type, extSupported)) {
3969             case 0: return PKIXExtensions.BasicConstraints_Id;
3970             case 1: return PKIXExtensions.KeyUsage_Id;
3971             case 2: return PKIXExtensions.ExtendedKeyUsage_Id;
3972             case 3: return PKIXExtensions.SubjectAlternativeName_Id;
3973             case 4: return PKIXExtensions.IssuerAlternativeName_Id;
3974             case 5: return PKIXExtensions.SubjectInfoAccess_Id;
3975             case 6: return PKIXExtensions.AuthInfoAccess_Id;
3976             case 8: return PKIXExtensions.CRLDistributionPoints_Id;
3977             default: return new ObjectIdentifier(type);
3978         }
3979     }
3980 
3981     /**
3982      * Create X509v3 extensions from a string representation. Note that the
3983      * SubjectKeyIdentifierExtension will always be created non-critical besides
3984      * the extension requested in the <code>extstr</code> argument.
3985      *
3986      * @param reqex the requested extensions, can be null, used for -gencert
3987      * @param ext the original extensions, can be null, used for -selfcert
3988      * @param extstrs -ext values, Read keytool doc
3989      * @param pkey the public key for the certificate
3990      * @param akey the public key for the authority (issuer)
3991      * @return the created CertificateExtensions
3992      */
3993     private CertificateExtensions createV3Extensions(
3994             CertificateExtensions reqex,
3995             CertificateExtensions ext,
3996             List <String> extstrs,
3997             PublicKey pkey,
3998             PublicKey akey) throws Exception {
3999 
4000         if (ext != null && reqex != null) {
4001             // This should not happen
4002             throw new Exception("One of request and original should be null.");
4003         }
4004         if (ext == null) ext = new CertificateExtensions();
4005         try {
4006             // name{:critical}{=value}
4007             // Honoring requested extensions
4008             if (reqex != null) {
4009                 for(String extstr: extstrs) {
4010                     if (extstr.toLowerCase(Locale.ENGLISH).startsWith("honored=")) {
4011                         List<String> list = Arrays.asList(
4012                                 extstr.toLowerCase(Locale.ENGLISH).substring(8).split(","));
4013                         // First check existence of "all"
4014                         if (list.contains("all")) {
4015                             ext = reqex;    // we know ext was null
4016                         }
4017                         // one by one for others
4018                         for (String item: list) {
4019                             if (item.equals("all")) continue;
4020 
4021                             // add or remove
4022                             boolean add = true;
4023                             // -1, unchanged, 0 crtical, 1 non-critical
4024                             int action = -1;
4025                             String type = null;
4026                             if (item.startsWith("-")) {
4027                                 add = false;
4028                                 type = item.substring(1);
4029                             } else {
4030                                 int colonpos = item.indexOf(':');
4031                                 if (colonpos >= 0) {
4032                                     type = item.substring(0, colonpos);
4033                                     action = oneOf(item.substring(colonpos+1),
4034                                             "critical", "non-critical");
4035                                     if (action == -1) {
4036                                         throw new Exception(rb.getString
4037                                             ("Illegal.value.") + item);
4038                                     }
4039                                 }
4040                             }
4041                             String n = reqex.getNameByOid(findOidForExtName(type));
4042                             if (add) {
4043                                 Extension e = reqex.get(n);
4044                                 if (!e.isCritical() && action == 0
4045                                         || e.isCritical() && action == 1) {
4046                                     e = Extension.newExtension(
4047                                             e.getExtensionId(),
4048                                             !e.isCritical(),
4049                                             e.getExtensionValue());
4050                                     ext.set(n, e);
4051                                 }
4052                             } else {
4053                                 ext.delete(n);
4054                             }
4055                         }
4056                         break;
4057                     }
4058                 }
4059             }
4060             for(String extstr: extstrs) {
4061                 String name, value;
4062                 boolean isCritical = false;
4063 
4064                 int eqpos = extstr.indexOf('=');
4065                 if (eqpos >= 0) {
4066                     name = extstr.substring(0, eqpos);
4067                     value = extstr.substring(eqpos+1);
4068                 } else {
4069                     name = extstr;
4070                     value = null;
4071                 }
4072 
4073                 int colonpos = name.indexOf(':');
4074                 if (colonpos >= 0) {
4075                     if (oneOf(name.substring(colonpos+1), "critical") == 0) {
4076                         isCritical = true;
4077                     }
4078                     name = name.substring(0, colonpos);
4079                 }
4080 
4081                 if (name.equalsIgnoreCase("honored")) {
4082                     continue;
4083                 }
4084                 int exttype = oneOf(name, extSupported);
4085                 switch (exttype) {
4086                     case 0:     // BC
4087                         int pathLen = -1;
4088                         boolean isCA = false;
4089                         if (value == null) {
4090                             isCA = true;
4091                         } else {
4092                             try {   // the abbr format
4093                                 pathLen = Integer.parseInt(value);
4094                                 isCA = true;
4095                             } catch (NumberFormatException ufe) {
4096                                 // ca:true,pathlen:1
4097                                 for (String part: value.split(",")) {
4098                                     String[] nv = part.split(":");
4099                                     if (nv.length != 2) {
4100                                         throw new Exception(rb.getString
4101                                                 ("Illegal.value.") + extstr);
4102                                     } else {
4103                                         if (nv[0].equalsIgnoreCase("ca")) {
4104                                             isCA = Boolean.parseBoolean(nv[1]);
4105                                         } else if (nv[0].equalsIgnoreCase("pathlen")) {
4106                                             pathLen = Integer.parseInt(nv[1]);
4107                                         } else {
4108                                             throw new Exception(rb.getString
4109                                                 ("Illegal.value.") + extstr);
4110                                         }
4111                                     }
4112                                 }
4113                             }
4114                         }
4115                         ext.set(BasicConstraintsExtension.NAME,
4116                                 new BasicConstraintsExtension(isCritical, isCA,
4117                                 pathLen));
4118                         break;
4119                     case 1:     // KU
4120                         if(value != null) {
4121                             boolean[] ok = new boolean[9];
4122                             for (String s: value.split(",")) {
4123                                 int p = oneOf(s,
4124                                        "digitalSignature",  // (0),
4125                                        "nonRepudiation",    // (1)
4126                                        "keyEncipherment",   // (2),
4127                                        "dataEncipherment",  // (3),
4128                                        "keyAgreement",      // (4),
4129                                        "keyCertSign",       // (5),
4130                                        "cRLSign",           // (6),
4131                                        "encipherOnly",      // (7),
4132                                        "decipherOnly",      // (8)
4133                                        "contentCommitment"  // also (1)
4134                                        );
4135                                 if (p < 0) {
4136                                     throw new Exception(rb.getString("Unknown.keyUsage.type.") + s);
4137                                 }
4138                                 if (p == 9) p = 1;
4139                                 ok[p] = true;
4140                             }
4141                             KeyUsageExtension kue = new KeyUsageExtension(ok);
4142                             // The above KeyUsageExtension constructor does not
4143                             // allow isCritical value, so...
4144                             ext.set(KeyUsageExtension.NAME, Extension.newExtension(
4145                                     kue.getExtensionId(),
4146                                     isCritical,
4147                                     kue.getExtensionValue()));
4148                         } else {
4149                             throw new Exception(rb.getString
4150                                     ("Illegal.value.") + extstr);
4151                         }
4152                         break;
4153                     case 2:     // EKU
4154                         if(value != null) {
4155                             Vector<ObjectIdentifier> v = new Vector<>();
4156                             for (String s: value.split(",")) {
4157                                 int p = oneOf(s,
4158                                         "anyExtendedKeyUsage",
4159                                         "serverAuth",       //1
4160                                         "clientAuth",       //2
4161                                         "codeSigning",      //3
4162                                         "emailProtection",  //4
4163                                         "",                 //5
4164                                         "",                 //6
4165                                         "",                 //7
4166                                         "timeStamping",     //8
4167                                         "OCSPSigning"       //9
4168                                        );
4169                                 if (p < 0) {
4170                                     try {
4171                                         v.add(new ObjectIdentifier(s));
4172                                     } catch (Exception e) {
4173                                         throw new Exception(rb.getString(
4174                                                 "Unknown.extendedkeyUsage.type.") + s);
4175                                     }
4176                                 } else if (p == 0) {
4177                                     v.add(new ObjectIdentifier("2.5.29.37.0"));
4178                                 } else {
4179                                     v.add(new ObjectIdentifier("1.3.6.1.5.5.7.3." + p));
4180                                 }
4181                             }
4182                             ext.set(ExtendedKeyUsageExtension.NAME,
4183                                     new ExtendedKeyUsageExtension(isCritical, v));
4184                         } else {
4185                             throw new Exception(rb.getString
4186                                     ("Illegal.value.") + extstr);
4187                         }
4188                         break;
4189                     case 3:     // SAN
4190                     case 4:     // IAN
4191                         if(value != null) {
4192                             String[] ps = value.split(",");
4193                             GeneralNames gnames = new GeneralNames();
4194                             for(String item: ps) {
4195                                 colonpos = item.indexOf(':');
4196                                 if (colonpos < 0) {
4197                                     throw new Exception("Illegal item " + item + " in " + extstr);
4198                                 }
4199                                 String t = item.substring(0, colonpos);
4200                                 String v = item.substring(colonpos+1);
4201                                 gnames.add(createGeneralName(t, v));
4202                             }
4203                             if (exttype == 3) {
4204                                 ext.set(SubjectAlternativeNameExtension.NAME,
4205                                         new SubjectAlternativeNameExtension(
4206                                             isCritical, gnames));
4207                             } else {
4208                                 ext.set(IssuerAlternativeNameExtension.NAME,
4209                                         new IssuerAlternativeNameExtension(
4210                                             isCritical, gnames));
4211                             }
4212                         } else {
4213                             throw new Exception(rb.getString
4214                                     ("Illegal.value.") + extstr);
4215                         }
4216                         break;
4217                     case 5:     // SIA, always non-critical
4218                     case 6:     // AIA, always non-critical
4219                         if (isCritical) {
4220                             throw new Exception(rb.getString(
4221                                     "This.extension.cannot.be.marked.as.critical.") + extstr);
4222                         }
4223                         if(value != null) {
4224                             List<AccessDescription> accessDescriptions =
4225                                     new ArrayList<>();
4226                             String[] ps = value.split(",");
4227                             for(String item: ps) {
4228                                 colonpos = item.indexOf(':');
4229                                 int colonpos2 = item.indexOf(':', colonpos+1);
4230                                 if (colonpos < 0 || colonpos2 < 0) {
4231                                     throw new Exception(rb.getString
4232                                             ("Illegal.value.") + extstr);
4233                                 }
4234                                 String m = item.substring(0, colonpos);
4235                                 String t = item.substring(colonpos+1, colonpos2);
4236                                 String v = item.substring(colonpos2+1);
4237                                 int p = oneOf(m,
4238                                         "",
4239                                         "ocsp",         //1
4240                                         "caIssuers",    //2
4241                                         "timeStamping", //3
4242                                         "",
4243                                         "caRepository"  //5
4244                                         );
4245                                 ObjectIdentifier oid;
4246                                 if (p < 0) {
4247                                     try {
4248                                         oid = new ObjectIdentifier(m);
4249                                     } catch (Exception e) {
4250                                         throw new Exception(rb.getString(
4251                                                 "Unknown.AccessDescription.type.") + m);
4252                                     }
4253                                 } else {
4254                                     oid = new ObjectIdentifier("1.3.6.1.5.5.7.48." + p);
4255                                 }
4256                                 accessDescriptions.add(new AccessDescription(
4257                                         oid, createGeneralName(t, v)));
4258                             }
4259                             if (exttype == 5) {
4260                                 ext.set(SubjectInfoAccessExtension.NAME,
4261                                         new SubjectInfoAccessExtension(accessDescriptions));
4262                             } else {
4263                                 ext.set(AuthorityInfoAccessExtension.NAME,
4264                                         new AuthorityInfoAccessExtension(accessDescriptions));
4265                             }
4266                         } else {
4267                             throw new Exception(rb.getString
4268                                     ("Illegal.value.") + extstr);
4269                         }
4270                         break;
4271                     case 8: // CRL, experimental, only support 1 distributionpoint
4272                         if(value != null) {
4273                             String[] ps = value.split(",");
4274                             GeneralNames gnames = new GeneralNames();
4275                             for(String item: ps) {
4276                                 colonpos = item.indexOf(':');
4277                                 if (colonpos < 0) {
4278                                     throw new Exception("Illegal item " + item + " in " + extstr);
4279                                 }
4280                                 String t = item.substring(0, colonpos);
4281                                 String v = item.substring(colonpos+1);
4282                                 gnames.add(createGeneralName(t, v));
4283                             }
4284                             ext.set(CRLDistributionPointsExtension.NAME,
4285                                     new CRLDistributionPointsExtension(
4286                                         isCritical, Collections.singletonList(
4287                                         new DistributionPoint(gnames, null, null))));
4288                         } else {
4289                             throw new Exception(rb.getString
4290                                     ("Illegal.value.") + extstr);
4291                         }
4292                         break;
4293                     case -1:
4294                         ObjectIdentifier oid = new ObjectIdentifier(name);
4295                         byte[] data = null;
4296                         if (value != null) {
4297                             data = new byte[value.length() / 2 + 1];
4298                             int pos = 0;
4299                             for (char c: value.toCharArray()) {
4300                                 int hex;
4301                                 if (c >= '0' && c <= '9') {
4302                                     hex = c - '0' ;
4303                                 } else if (c >= 'A' && c <= 'F') {
4304                                     hex = c - 'A' + 10;
4305                                 } else if (c >= 'a' && c <= 'f') {
4306                                     hex = c - 'a' + 10;
4307                                 } else {
4308                                     continue;
4309                                 }
4310                                 if (pos % 2 == 0) {
4311                                     data[pos/2] = (byte)(hex << 4);
4312                                 } else {
4313                                     data[pos/2] += hex;
4314                                 }
4315                                 pos++;
4316                             }
4317                             if (pos % 2 != 0) {
4318                                 throw new Exception(rb.getString(
4319                                         "Odd.number.of.hex.digits.found.") + extstr);
4320                             }
4321                             data = Arrays.copyOf(data, pos/2);
4322                         } else {
4323                             data = new byte[0];
4324                         }
4325                         ext.set(oid.toString(), new Extension(oid, isCritical,
4326                                 new DerValue(DerValue.tag_OctetString, data)
4327                                         .toByteArray()));
4328                         break;
4329                     default:
4330                         throw new Exception(rb.getString(
4331                                 "Unknown.extension.type.") + extstr);
4332                 }
4333             }
4334             // always non-critical
4335             ext.set(SubjectKeyIdentifierExtension.NAME,
4336                     new SubjectKeyIdentifierExtension(
4337                         new KeyIdentifier(pkey).getIdentifier()));
4338             if (akey != null && !pkey.equals(akey)) {
4339                 ext.set(AuthorityKeyIdentifierExtension.NAME,
4340                         new AuthorityKeyIdentifierExtension(
4341                         new KeyIdentifier(akey), null, null));
4342             }
4343         } catch(IOException e) {
4344             throw new RuntimeException(e);
4345         }
4346         return ext;
4347     }
4348 
4349     private boolean isTrustedCert(Certificate cert) throws KeyStoreException {
4350         if (caks != null && caks.getCertificateAlias(cert) != null) {
4351             return true;
4352         } else {
4353             String inKS = keyStore.getCertificateAlias(cert);
4354             return inKS != null && keyStore.isCertificateEntry(inKS);
4355         }
4356     }
4357 
4358     private void checkWeak(String label, String sigAlg, Key key) {
4359 
4360         if (sigAlg != null && !DISABLED_CHECK.permits(
4361                 SIG_PRIMITIVE_SET, sigAlg, null)) {
4362             weakWarnings.add(String.format(
4363                     rb.getString("whose.sigalg.risk"), label, sigAlg));
4364         }
4365         if (key != null && !DISABLED_CHECK.permits(SIG_PRIMITIVE_SET, key)) {
4366             weakWarnings.add(String.format(
4367                     rb.getString("whose.key.risk"),
4368                     label,
4369                     String.format(rb.getString("key.bit"),
4370                             KeyUtil.getKeySize(key), key.getAlgorithm())));
4371         }
4372     }
4373 
4374     private void checkWeak(String label, Certificate[] certs)
4375             throws KeyStoreException {
4376         for (int i = 0; i < certs.length; i++) {
4377             Certificate cert = certs[i];
4378             if (cert instanceof X509Certificate) {
4379                 X509Certificate xc = (X509Certificate)cert;
4380                 String fullLabel = label;
4381                 if (certs.length > 1) {
4382                     fullLabel = oneInMany(label, i, certs.length);
4383                 }
4384                 checkWeak(fullLabel, xc);
4385             }
4386         }
4387     }
4388 
4389     private void checkWeak(String label, Certificate cert)
4390             throws KeyStoreException {
4391         if (cert instanceof X509Certificate) {
4392             X509Certificate xc = (X509Certificate)cert;
4393             // No need to check the sigalg of a trust anchor
4394             String sigAlg = isTrustedCert(cert) ? null : xc.getSigAlgName();
4395             checkWeak(label, sigAlg, xc.getPublicKey());
4396         }
4397     }
4398 
4399     private void checkWeak(String label, PKCS10 p10) {
4400         checkWeak(label, p10.getSigAlg(), p10.getSubjectPublicKeyInfo());
4401     }
4402 
4403     private void checkWeak(String label, CRL crl, Key key) {
4404         if (crl instanceof X509CRLImpl) {
4405             X509CRLImpl impl = (X509CRLImpl)crl;
4406             checkWeak(label, impl.getSigAlgName(), key);
4407         }
4408     }
4409 
4410     private void printWeakWarnings(boolean newLine) {
4411         if (!weakWarnings.isEmpty() && !nowarn) {
4412             System.err.println("\nWarning:");
4413             for (String warning : weakWarnings) {
4414                 System.err.println(warning);
4415             }
4416             if (newLine) {
4417                 // When calling before a yes/no prompt, add a new line
4418                 System.err.println();
4419             }
4420         }
4421         weakWarnings.clear();
4422     }
4423 
4424     /**
4425      * Prints the usage of this tool.
4426      */
4427     private void usage() {
4428         if (command != null) {
4429             System.err.println("keytool " + command +
4430                     rb.getString(".OPTION."));
4431             System.err.println();
4432             System.err.println(rb.getString(command.description));
4433             System.err.println();
4434             System.err.println(rb.getString("Options."));
4435             System.err.println();
4436 
4437             // Left and right sides of the options list
4438             String[] left = new String[command.options.length];
4439             String[] right = new String[command.options.length];
4440 
4441             // Check if there's an unknown option
4442             boolean found = false;
4443 
4444             // Length of left side of options list
4445             int lenLeft = 0;
4446             for (int j=0; j<left.length; j++) {
4447                 Option opt = command.options[j];
4448                 left[j] = opt.toString();
4449                 if (opt.arg != null) left[j] += " " + opt.arg;
4450                 if (left[j].length() > lenLeft) {
4451                     lenLeft = left[j].length();
4452                 }
4453                 right[j] = rb.getString(opt.description);
4454             }
4455             for (int j=0; j<left.length; j++) {
4456                 System.err.printf(" %-" + lenLeft + "s  %s\n",
4457                         left[j], right[j]);
4458             }
4459             System.err.println();
4460             System.err.println(rb.getString(
4461                     "Use.keytool.help.for.all.available.commands"));
4462         } else {
4463             System.err.println(rb.getString(
4464                     "Key.and.Certificate.Management.Tool"));
4465             System.err.println();
4466             System.err.println(rb.getString("Commands."));
4467             System.err.println();
4468             for (Command c: Command.values()) {
4469                 if (c == KEYCLONE) break;
4470                 System.err.printf(" %-20s%s\n", c, rb.getString(c.description));
4471             }
4472             System.err.println();
4473             System.err.println(rb.getString(
4474                     "Use.keytool.command.name.help.for.usage.of.command.name"));
4475         }
4476     }
4477 
4478     private void tinyHelp() {
4479         usage();
4480         if (debug) {
4481             throw new RuntimeException("NO BIG ERROR, SORRY");
4482         } else {
4483             System.exit(1);
4484         }
4485     }
4486 
4487     private void errorNeedArgument(String flag) {
4488         Object[] source = {flag};
4489         System.err.println(new MessageFormat(
4490                 rb.getString("Command.option.flag.needs.an.argument.")).format(source));
4491         tinyHelp();
4492     }
4493 
4494     private char[] getPass(String modifier, String arg) {
4495         char[] output = KeyStoreUtil.getPassWithModifier(modifier, arg, rb);
4496         if (output != null) return output;
4497         tinyHelp();
4498         return null;    // Useless, tinyHelp() already exits.
4499     }
4500 }
4501 
4502 // This class is exactly the same as com.sun.tools.javac.util.Pair,
4503 // it's copied here since the original one is not included in JRE.
4504 class Pair<A, B> {
4505 
4506     public final A fst;
4507     public final B snd;
4508 
4509     public Pair(A fst, B snd) {
4510         this.fst = fst;
4511         this.snd = snd;
4512     }
4513 
4514     public String toString() {
4515         return "Pair[" + fst + "," + snd + "]";
4516     }
4517 
4518     public boolean equals(Object other) {
4519         return
4520             other instanceof Pair &&
4521             Objects.equals(fst, ((Pair)other).fst) &&
4522             Objects.equals(snd, ((Pair)other).snd);
4523     }
4524 
4525     public int hashCode() {
4526         if (fst == null) return (snd == null) ? 0 : snd.hashCode() + 1;
4527         else if (snd == null) return fst.hashCode() + 2;
4528         else return fst.hashCode() * 17 + snd.hashCode();
4529     }
4530 
4531     public static <A,B> Pair<A,B> of(A a, B b) {
4532         return new Pair<>(a,b);
4533     }
4534 }
4535