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