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