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