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