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