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