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