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