Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/security/tools/keytool/Main.java
38923 views
/*1* Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.security.tools.keytool;2627import java.io.*;28import java.nio.file.Files;29import java.nio.file.Paths;30import java.security.CodeSigner;31import java.security.CryptoPrimitive;32import java.security.KeyStore;33import java.security.KeyStoreException;34import java.security.MessageDigest;35import java.security.Key;36import java.security.PublicKey;37import java.security.PrivateKey;38import java.security.Security;39import java.security.Signature;40import java.security.Timestamp;41import java.security.UnrecoverableEntryException;42import java.security.UnrecoverableKeyException;43import java.security.NoSuchAlgorithmException;44import java.security.Principal;45import java.security.Provider;46import java.security.cert.Certificate;47import java.security.cert.CertificateFactory;48import java.security.cert.CertStoreException;49import java.security.cert.CRL;50import java.security.cert.X509Certificate;51import java.security.cert.CertificateException;52import java.security.interfaces.ECKey;53import java.security.spec.AlgorithmParameterSpec;54import java.security.spec.ECParameterSpec;55import java.text.Collator;56import java.text.MessageFormat;57import java.util.*;58import java.util.jar.JarEntry;59import java.util.jar.JarFile;60import java.lang.reflect.Constructor;61import java.math.BigInteger;62import java.net.URI;63import java.net.URL;64import java.net.URLClassLoader;65import java.security.cert.CertStore;6667import java.security.cert.X509CRL;68import java.security.cert.X509CRLEntry;69import java.security.cert.X509CRLSelector;70import javax.security.auth.x500.X500Principal;71import java.util.Base64;7273import sun.security.util.DisabledAlgorithmConstraints;74import sun.security.util.KeyUtil;75import sun.security.util.NamedCurve;76import sun.security.util.ObjectIdentifier;77import sun.security.pkcs10.PKCS10;78import sun.security.pkcs10.PKCS10Attribute;79import sun.security.provider.X509Factory;80import sun.security.provider.certpath.CertStoreHelper;81import sun.security.util.Password;82import sun.security.util.SecurityProviderConstants;83import sun.security.util.SignatureUtil;84import javax.crypto.KeyGenerator;85import javax.crypto.SecretKey;86import javax.crypto.SecretKeyFactory;87import javax.crypto.spec.PBEKeySpec;8889import sun.security.pkcs.PKCS9Attribute;90import sun.security.tools.KeyStoreUtil;91import sun.security.tools.PathList;92import sun.security.util.DerValue;93import sun.security.util.Pem;94import sun.security.x509.*;9596import static java.security.KeyStore.*;97import static sun.security.tools.keytool.Main.Command.*;98import static sun.security.tools.keytool.Main.Option.*;99100/**101* This tool manages keystores.102*103* @author Jan Luehe104*105*106* @see java.security.KeyStore107* @see sun.security.provider.KeyProtector108* @see sun.security.provider.JavaKeyStore109*110* @since 1.2111*/112public final class Main {113114private static final byte[] CRLF = new byte[] {'\r', '\n'};115116private boolean debug = false;117private Command command = null;118private String sigAlgName = null;119private String keyAlgName = null;120private boolean verbose = false;121private int keysize = -1;122private boolean rfc = false;123private long validity = (long)90;124private String alias = null;125private String dname = null;126private String dest = null;127private String filename = null;128private String infilename = null;129private String outfilename = null;130private String srcksfname = null;131132// User-specified providers are added before any command is called.133// However, they are not removed before the end of the main() method.134// If you're calling KeyTool.main() directly in your own Java program,135// please programtically add any providers you need and do not specify136// them through the command line.137138private Set<Pair <String, String>> providers = null;139private String storetype = null;140private String srcProviderName = null;141private String providerName = null;142private String pathlist = null;143private char[] storePass = null;144private char[] storePassNew = null;145private char[] keyPass = null;146private char[] keyPassNew = null;147private char[] newPass = null;148private char[] destKeyPass = null;149private char[] srckeyPass = null;150private String ksfname = null;151private File ksfile = null;152private InputStream ksStream = null; // keystore stream153private String sslserver = null;154private String jarfile = null;155private KeyStore keyStore = null;156private boolean token = false;157private boolean nullStream = false;158private boolean kssave = false;159private boolean noprompt = false;160private boolean trustcacerts = false;161private boolean nowarn = false;162private boolean protectedPath = false;163private boolean srcprotectedPath = false;164private CertificateFactory cf = null;165private KeyStore caks = null; // "cacerts" keystore166private char[] srcstorePass = null;167private String srcstoretype = null;168private Set<char[]> passwords = new HashSet<>();169private String startDate = null;170171private List<String> ids = new ArrayList<>(); // used in GENCRL172private List<String> v3ext = new ArrayList<>();173174// In-place importkeystore is special.175// A backup is needed, and no need to prompt for deststorepass.176private boolean inplaceImport = false;177private String inplaceBackupName = null;178179// Warnings on weak algorithms etc180private List<String> weakWarnings = new ArrayList<>();181182private static final DisabledAlgorithmConstraints DISABLED_CHECK =183new DisabledAlgorithmConstraints(184DisabledAlgorithmConstraints.PROPERTY_CERTPATH_DISABLED_ALGS);185186private static final DisabledAlgorithmConstraints LEGACY_CHECK =187new DisabledAlgorithmConstraints(188DisabledAlgorithmConstraints.PROPERTY_SECURITY_LEGACY_ALGS);189190private static final Set<CryptoPrimitive> SIG_PRIMITIVE_SET = Collections191.unmodifiableSet(EnumSet.of(CryptoPrimitive.SIGNATURE));192193enum Command {194CERTREQ("Generates.a.certificate.request",195ALIAS, SIGALG, FILEOUT, KEYPASS, KEYSTORE, DNAME,196STOREPASS, STORETYPE, PROVIDERNAME, PROVIDERCLASS,197PROVIDERARG, PROVIDERPATH, V, PROTECTED),198CHANGEALIAS("Changes.an.entry.s.alias",199ALIAS, DESTALIAS, KEYPASS, KEYSTORE, STOREPASS,200STORETYPE, PROVIDERNAME, PROVIDERCLASS, PROVIDERARG,201PROVIDERPATH, V, PROTECTED),202DELETE("Deletes.an.entry",203ALIAS, KEYSTORE, STOREPASS, STORETYPE,204PROVIDERNAME, PROVIDERCLASS, PROVIDERARG,205PROVIDERPATH, V, PROTECTED),206EXPORTCERT("Exports.certificate",207RFC, ALIAS, FILEOUT, KEYSTORE, STOREPASS,208STORETYPE, PROVIDERNAME, PROVIDERCLASS, PROVIDERARG,209PROVIDERPATH, V, PROTECTED),210GENKEYPAIR("Generates.a.key.pair",211ALIAS, KEYALG, KEYSIZE, SIGALG, DESTALIAS, DNAME,212STARTDATE, EXT, VALIDITY, KEYPASS, KEYSTORE,213STOREPASS, STORETYPE, PROVIDERNAME, PROVIDERCLASS,214PROVIDERARG, PROVIDERPATH, V, PROTECTED),215GENSECKEY("Generates.a.secret.key",216ALIAS, KEYPASS, KEYALG, KEYSIZE, KEYSTORE,217STOREPASS, STORETYPE, PROVIDERNAME, PROVIDERCLASS,218PROVIDERARG, PROVIDERPATH, V, PROTECTED),219GENCERT("Generates.certificate.from.a.certificate.request",220RFC, INFILE, OUTFILE, ALIAS, SIGALG, DNAME,221STARTDATE, EXT, VALIDITY, KEYPASS, KEYSTORE,222STOREPASS, STORETYPE, PROVIDERNAME, PROVIDERCLASS,223PROVIDERARG, PROVIDERPATH, V, PROTECTED),224IMPORTCERT("Imports.a.certificate.or.a.certificate.chain",225NOPROMPT, TRUSTCACERTS, PROTECTED, ALIAS, FILEIN,226KEYPASS, KEYSTORE, STOREPASS, STORETYPE,227PROVIDERNAME, PROVIDERCLASS, PROVIDERARG,228PROVIDERPATH, V),229IMPORTPASS("Imports.a.password",230ALIAS, KEYPASS, KEYALG, KEYSIZE, KEYSTORE,231STOREPASS, STORETYPE, PROVIDERNAME, PROVIDERCLASS,232PROVIDERARG, PROVIDERPATH, V, PROTECTED),233IMPORTKEYSTORE("Imports.one.or.all.entries.from.another.keystore",234SRCKEYSTORE, DESTKEYSTORE, SRCSTORETYPE,235DESTSTORETYPE, SRCSTOREPASS, DESTSTOREPASS,236SRCPROTECTED, SRCPROVIDERNAME, DESTPROVIDERNAME,237SRCALIAS, DESTALIAS, SRCKEYPASS, DESTKEYPASS,238NOPROMPT, PROVIDERCLASS, PROVIDERARG, PROVIDERPATH,239V),240KEYPASSWD("Changes.the.key.password.of.an.entry",241ALIAS, KEYPASS, NEW, KEYSTORE, STOREPASS,242STORETYPE, PROVIDERNAME, PROVIDERCLASS, PROVIDERARG,243PROVIDERPATH, V),244LIST("Lists.entries.in.a.keystore",245RFC, ALIAS, KEYSTORE, STOREPASS, STORETYPE,246PROVIDERNAME, PROVIDERCLASS, PROVIDERARG,247PROVIDERPATH, V, PROTECTED),248PRINTCERT("Prints.the.content.of.a.certificate",249RFC, FILEIN, SSLSERVER, JARFILE, V),250PRINTCERTREQ("Prints.the.content.of.a.certificate.request",251FILEIN, V),252PRINTCRL("Prints.the.content.of.a.CRL.file",253FILEIN, V),254STOREPASSWD("Changes.the.store.password.of.a.keystore",255NEW, KEYSTORE, STOREPASS, STORETYPE, PROVIDERNAME,256PROVIDERCLASS, PROVIDERARG, PROVIDERPATH, V),257258// Undocumented start here, KEYCLONE is used a marker in -help;259260KEYCLONE("Clones.a.key.entry",261ALIAS, DESTALIAS, KEYPASS, NEW, STORETYPE,262KEYSTORE, STOREPASS, PROVIDERNAME, PROVIDERCLASS,263PROVIDERARG, PROVIDERPATH, V),264SELFCERT("Generates.a.self.signed.certificate",265ALIAS, SIGALG, DNAME, STARTDATE, VALIDITY, KEYPASS,266STORETYPE, KEYSTORE, STOREPASS, PROVIDERNAME,267PROVIDERCLASS, PROVIDERARG, PROVIDERPATH, V),268GENCRL("Generates.CRL",269RFC, FILEOUT, ID,270ALIAS, SIGALG, EXT, KEYPASS, KEYSTORE,271STOREPASS, STORETYPE, PROVIDERNAME, PROVIDERCLASS,272PROVIDERARG, PROVIDERPATH, V, PROTECTED),273IDENTITYDB("Imports.entries.from.a.JDK.1.1.x.style.identity.database",274FILEIN, STORETYPE, KEYSTORE, STOREPASS, PROVIDERNAME,275PROVIDERCLASS, PROVIDERARG, PROVIDERPATH, V);276277final String description;278final Option[] options;279Command(String d, Option... o) {280description = d;281options = o;282}283@Override284public String toString() {285return "-" + name().toLowerCase(Locale.ENGLISH);286}287};288289enum Option {290ALIAS("alias", "<alias>", "alias.name.of.the.entry.to.process"),291DESTALIAS("destalias", "<destalias>", "destination.alias"),292DESTKEYPASS("destkeypass", "<arg>", "destination.key.password"),293DESTKEYSTORE("destkeystore", "<destkeystore>", "destination.keystore.name"),294DESTPROTECTED("destprotected", null, "destination.keystore.password.protected"),295DESTPROVIDERNAME("destprovidername", "<destprovidername>", "destination.keystore.provider.name"),296DESTSTOREPASS("deststorepass", "<arg>", "destination.keystore.password"),297DESTSTORETYPE("deststoretype", "<deststoretype>", "destination.keystore.type"),298DNAME("dname", "<dname>", "distinguished.name"),299EXT("ext", "<value>", "X.509.extension"),300FILEOUT("file", "<filename>", "output.file.name"),301FILEIN("file", "<filename>", "input.file.name"),302ID("id", "<id:reason>", "Serial.ID.of.cert.to.revoke"),303INFILE("infile", "<filename>", "input.file.name"),304KEYALG("keyalg", "<keyalg>", "key.algorithm.name"),305KEYPASS("keypass", "<arg>", "key.password"),306KEYSIZE("keysize", "<keysize>", "key.bit.size"),307KEYSTORE("keystore", "<keystore>", "keystore.name"),308NEW("new", "<arg>", "new.password"),309NOPROMPT("noprompt", null, "do.not.prompt"),310OUTFILE("outfile", "<filename>", "output.file.name"),311PROTECTED("protected", null, "password.through.protected.mechanism"),312PROVIDERARG("providerarg", "<arg>", "provider.argument"),313PROVIDERCLASS("providerclass", "<providerclass>", "provider.class.name"),314PROVIDERNAME("providername", "<providername>", "provider.name"),315PROVIDERPATH("providerpath", "<pathlist>", "provider.classpath"),316RFC("rfc", null, "output.in.RFC.style"),317SIGALG("sigalg", "<sigalg>", "signature.algorithm.name"),318SRCALIAS("srcalias", "<srcalias>", "source.alias"),319SRCKEYPASS("srckeypass", "<arg>", "source.key.password"),320SRCKEYSTORE("srckeystore", "<srckeystore>", "source.keystore.name"),321SRCPROTECTED("srcprotected", null, "source.keystore.password.protected"),322SRCPROVIDERNAME("srcprovidername", "<srcprovidername>", "source.keystore.provider.name"),323SRCSTOREPASS("srcstorepass", "<arg>", "source.keystore.password"),324SRCSTORETYPE("srcstoretype", "<srcstoretype>", "source.keystore.type"),325SSLSERVER("sslserver", "<server[:port]>", "SSL.server.host.and.port"),326JARFILE("jarfile", "<filename>", "signed.jar.file"),327STARTDATE("startdate", "<startdate>", "certificate.validity.start.date.time"),328STOREPASS("storepass", "<arg>", "keystore.password"),329STORETYPE("storetype", "<storetype>", "keystore.type"),330TRUSTCACERTS("trustcacerts", null, "trust.certificates.from.cacerts"),331V("v", null, "verbose.output"),332VALIDITY("validity", "<valDays>", "validity.number.of.days");333334final String name, arg, description;335Option(String name, String arg, String description) {336this.name = name;337this.arg = arg;338this.description = description;339}340@Override341public String toString() {342return "-" + name;343}344};345346private static final Class<?>[] PARAM_STRING = { String.class };347348private static final String NONE = "NONE";349private static final String P11KEYSTORE = "PKCS11";350private static final String P12KEYSTORE = "PKCS12";351private static final String keyAlias = "mykey";352353// for i18n354private static final java.util.ResourceBundle rb =355java.util.ResourceBundle.getBundle(356"sun.security.tools.keytool.Resources");357private static final Collator collator = Collator.getInstance();358static {359// this is for case insensitive string comparisons360collator.setStrength(Collator.PRIMARY);361};362363private Main() { }364365public static void main(String[] args) throws Exception {366Main kt = new Main();367kt.run(args, System.out);368}369370private void run(String[] args, PrintStream out) throws Exception {371try {372parseArgs(args);373if (command != null) {374doCommands(out);375}376} catch (Exception e) {377System.out.println(rb.getString("keytool.error.") + e);378if (verbose) {379e.printStackTrace(System.out);380}381if (!debug) {382System.exit(1);383} else {384throw e;385}386} finally {387printWeakWarnings(false);388for (char[] pass : passwords) {389if (pass != null) {390Arrays.fill(pass, ' ');391pass = null;392}393}394395if (ksStream != null) {396ksStream.close();397}398}399}400401/**402* Parse command line arguments.403*/404void parseArgs(String[] args) {405406int i=0;407boolean help = args.length == 0;408409for (i=0; (i < args.length) && args[i].startsWith("-"); i++) {410411String flags = args[i];412413// Check if the last option needs an arg414if (i == args.length - 1) {415for (Option option: Option.values()) {416// Only options with an arg need to be checked417if (collator.compare(flags, option.toString()) == 0) {418if (option.arg != null) errorNeedArgument(flags);419break;420}421}422}423424/*425* Check modifiers426*/427String modifier = null;428int pos = flags.indexOf(':');429if (pos > 0) {430modifier = flags.substring(pos+1);431flags = flags.substring(0, pos);432}433/*434* command modes435*/436boolean isCommand = false;437for (Command c: Command.values()) {438if (collator.compare(flags, c.toString()) == 0) {439command = c;440isCommand = true;441break;442}443}444445if (isCommand) {446// already recognized as a command447} else if (collator.compare(flags, "-export") == 0) {448command = EXPORTCERT;449} else if (collator.compare(flags, "-genkey") == 0) {450command = GENKEYPAIR;451} else if (collator.compare(flags, "-import") == 0) {452command = IMPORTCERT;453} else if (collator.compare(flags, "-importpassword") == 0) {454command = IMPORTPASS;455} else if (collator.compare(flags, "-help") == 0) {456help = true;457} else if (collator.compare(flags, "-nowarn") == 0) {458nowarn = true;459}460461/*462* specifiers463*/464else if (collator.compare(flags, "-keystore") == 0 ||465collator.compare(flags, "-destkeystore") == 0) {466ksfname = args[++i];467} else if (collator.compare(flags, "-storepass") == 0 ||468collator.compare(flags, "-deststorepass") == 0) {469storePass = getPass(modifier, args[++i]);470passwords.add(storePass);471} else if (collator.compare(flags, "-storetype") == 0 ||472collator.compare(flags, "-deststoretype") == 0) {473storetype = KeyStoreUtil.niceStoreTypeName(args[++i]);474} else if (collator.compare(flags, "-srcstorepass") == 0) {475srcstorePass = getPass(modifier, args[++i]);476passwords.add(srcstorePass);477} else if (collator.compare(flags, "-srcstoretype") == 0) {478srcstoretype = KeyStoreUtil.niceStoreTypeName(args[++i]);479} else if (collator.compare(flags, "-srckeypass") == 0) {480srckeyPass = getPass(modifier, args[++i]);481passwords.add(srckeyPass);482} else if (collator.compare(flags, "-srcprovidername") == 0) {483srcProviderName = args[++i];484} else if (collator.compare(flags, "-providername") == 0 ||485collator.compare(flags, "-destprovidername") == 0) {486providerName = args[++i];487} else if (collator.compare(flags, "-providerpath") == 0) {488pathlist = args[++i];489} else if (collator.compare(flags, "-keypass") == 0) {490keyPass = getPass(modifier, args[++i]);491passwords.add(keyPass);492} else if (collator.compare(flags, "-new") == 0) {493newPass = getPass(modifier, args[++i]);494passwords.add(newPass);495} else if (collator.compare(flags, "-destkeypass") == 0) {496destKeyPass = getPass(modifier, args[++i]);497passwords.add(destKeyPass);498} else if (collator.compare(flags, "-alias") == 0 ||499collator.compare(flags, "-srcalias") == 0) {500alias = args[++i];501} else if (collator.compare(flags, "-dest") == 0 ||502collator.compare(flags, "-destalias") == 0) {503dest = args[++i];504} else if (collator.compare(flags, "-dname") == 0) {505dname = args[++i];506} else if (collator.compare(flags, "-keysize") == 0) {507keysize = Integer.parseInt(args[++i]);508} else if (collator.compare(flags, "-keyalg") == 0) {509keyAlgName = args[++i];510} else if (collator.compare(flags, "-sigalg") == 0) {511sigAlgName = args[++i];512} else if (collator.compare(flags, "-startdate") == 0) {513startDate = args[++i];514} else if (collator.compare(flags, "-validity") == 0) {515validity = Long.parseLong(args[++i]);516} else if (collator.compare(flags, "-ext") == 0) {517v3ext.add(args[++i]);518} else if (collator.compare(flags, "-id") == 0) {519ids.add(args[++i]);520} else if (collator.compare(flags, "-file") == 0) {521filename = args[++i];522} else if (collator.compare(flags, "-infile") == 0) {523infilename = args[++i];524} else if (collator.compare(flags, "-outfile") == 0) {525outfilename = args[++i];526} else if (collator.compare(flags, "-sslserver") == 0) {527sslserver = args[++i];528} else if (collator.compare(flags, "-jarfile") == 0) {529jarfile = args[++i];530} else if (collator.compare(flags, "-srckeystore") == 0) {531srcksfname = args[++i];532} else if ((collator.compare(flags, "-provider") == 0) ||533(collator.compare(flags, "-providerclass") == 0)) {534if (providers == null) {535providers = new HashSet<Pair <String, String>> (3);536}537String providerClass = args[++i];538String providerArg = null;539540if (args.length > (i+1)) {541flags = args[i+1];542if (collator.compare(flags, "-providerarg") == 0) {543if (args.length == (i+2)) errorNeedArgument(flags);544providerArg = args[i+2];545i += 2;546}547}548providers.add(549Pair.of(providerClass, providerArg));550}551552/*553* options554*/555else if (collator.compare(flags, "-v") == 0) {556verbose = true;557} else if (collator.compare(flags, "-debug") == 0) {558debug = true;559} else if (collator.compare(flags, "-rfc") == 0) {560rfc = true;561} else if (collator.compare(flags, "-noprompt") == 0) {562noprompt = true;563} else if (collator.compare(flags, "-trustcacerts") == 0) {564trustcacerts = true;565} else if (collator.compare(flags, "-protected") == 0 ||566collator.compare(flags, "-destprotected") == 0) {567protectedPath = true;568} else if (collator.compare(flags, "-srcprotected") == 0) {569srcprotectedPath = true;570} else {571System.err.println(rb.getString("Illegal.option.") + flags);572tinyHelp();573}574}575576if (i<args.length) {577System.err.println(rb.getString("Illegal.option.") + args[i]);578tinyHelp();579}580581if (command == null) {582if (help) {583usage();584} else {585System.err.println(rb.getString("Usage.error.no.command.provided"));586tinyHelp();587}588} else if (help) {589usage();590command = null;591}592}593594boolean isKeyStoreRelated(Command cmd) {595return cmd != PRINTCERT && cmd != PRINTCERTREQ;596}597598599/**600* Execute the commands.601*/602void doCommands(PrintStream out) throws Exception {603if (P11KEYSTORE.equalsIgnoreCase(storetype) ||604KeyStoreUtil.isWindowsKeyStore(storetype)) {605token = true;606if (ksfname == null) {607ksfname = NONE;608}609}610if (NONE.equals(ksfname)) {611nullStream = true;612}613614if (token && !nullStream) {615System.err.println(MessageFormat.format(rb.getString616(".keystore.must.be.NONE.if.storetype.is.{0}"), storetype));617System.err.println();618tinyHelp();619}620621if (token &&622(command == KEYPASSWD || command == STOREPASSWD)) {623throw new UnsupportedOperationException(MessageFormat.format(rb.getString624(".storepasswd.and.keypasswd.commands.not.supported.if.storetype.is.{0}"), storetype));625}626627if (token && (keyPass != null || newPass != null || destKeyPass != null)) {628throw new IllegalArgumentException(MessageFormat.format(rb.getString629(".keypass.and.new.can.not.be.specified.if.storetype.is.{0}"), storetype));630}631632if (protectedPath) {633if (storePass != null || keyPass != null ||634newPass != null || destKeyPass != null) {635throw new IllegalArgumentException(rb.getString636("if.protected.is.specified.then.storepass.keypass.and.new.must.not.be.specified"));637}638}639640if (srcprotectedPath) {641if (srcstorePass != null || srckeyPass != null) {642throw new IllegalArgumentException(rb.getString643("if.srcprotected.is.specified.then.srcstorepass.and.srckeypass.must.not.be.specified"));644}645}646647if (KeyStoreUtil.isWindowsKeyStore(storetype)) {648if (storePass != null || keyPass != null ||649newPass != null || destKeyPass != null) {650throw new IllegalArgumentException(rb.getString651("if.keystore.is.not.password.protected.then.storepass.keypass.and.new.must.not.be.specified"));652}653}654655if (KeyStoreUtil.isWindowsKeyStore(srcstoretype)) {656if (srcstorePass != null || srckeyPass != null) {657throw new IllegalArgumentException(rb.getString658("if.source.keystore.is.not.password.protected.then.srcstorepass.and.srckeypass.must.not.be.specified"));659}660}661662if (validity <= (long)0) {663throw new Exception664(rb.getString("Validity.must.be.greater.than.zero"));665}666667// Try to load and install specified provider668if (providers != null) {669ClassLoader cl = null;670if (pathlist != null) {671String path = null;672path = PathList.appendPath(673path, System.getProperty("java.class.path"));674path = PathList.appendPath(675path, System.getProperty("env.class.path"));676path = PathList.appendPath(path, pathlist);677678URL[] urls = PathList.pathToURLs(path);679cl = new URLClassLoader(urls);680} else {681cl = ClassLoader.getSystemClassLoader();682}683684for (Pair <String, String> provider: providers) {685String provName = provider.fst;686Class<?> provClass;687if (cl != null) {688provClass = cl.loadClass(provName);689} else {690provClass = Class.forName(provName);691}692693String provArg = provider.snd;694Object obj;695if (provArg == null) {696obj = provClass.newInstance();697} else {698Constructor<?> c = provClass.getConstructor(PARAM_STRING);699obj = c.newInstance(provArg);700}701if (!(obj instanceof Provider)) {702MessageFormat form = new MessageFormat703(rb.getString("provName.not.a.provider"));704Object[] source = {provName};705throw new Exception(form.format(source));706}707Security.addProvider((Provider)obj);708}709}710711if (command == LIST && verbose && rfc) {712System.err.println(rb.getString713("Must.not.specify.both.v.and.rfc.with.list.command"));714tinyHelp();715}716717// Make sure provided passwords are at least 6 characters long718if (command == GENKEYPAIR && keyPass!=null && keyPass.length < 6) {719throw new Exception(rb.getString720("Key.password.must.be.at.least.6.characters"));721}722if (newPass != null && newPass.length < 6) {723throw new Exception(rb.getString724("New.password.must.be.at.least.6.characters"));725}726if (destKeyPass != null && destKeyPass.length < 6) {727throw new Exception(rb.getString728("New.password.must.be.at.least.6.characters"));729}730731// Set this before inplaceImport check so we can compare name.732if (ksfname == null) {733ksfname = System.getProperty("user.home") + File.separator734+ ".keystore";735}736737KeyStore srcKeyStore = null;738if (command == IMPORTKEYSTORE) {739inplaceImport = inplaceImportCheck();740if (inplaceImport) {741// We load srckeystore first so we have srcstorePass that742// can be assigned to storePass743srcKeyStore = loadSourceKeyStore();744if (storePass == null) {745storePass = srcstorePass;746}747}748}749750// Check if keystore exists.751// If no keystore has been specified at the command line, try to use752// the default, which is located in $HOME/.keystore.753// If the command is "genkey", "identitydb", "import", or "printcert",754// it is OK not to have a keystore.755756// DO NOT open the existing keystore if this is an in-place import.757// The keystore should be created as brand new.758if (isKeyStoreRelated(command) && !nullStream && !inplaceImport) {759try {760ksfile = new File(ksfname);761// Check if keystore file is empty762if (ksfile.exists() && ksfile.length() == 0) {763throw new Exception(rb.getString764("Keystore.file.exists.but.is.empty.") + ksfname);765}766ksStream = new FileInputStream(ksfile);767} catch (FileNotFoundException e) {768if (command != GENKEYPAIR &&769command != GENSECKEY &&770command != IDENTITYDB &&771command != IMPORTCERT &&772command != IMPORTPASS &&773command != IMPORTKEYSTORE &&774command != PRINTCRL) {775throw new Exception(rb.getString776("Keystore.file.does.not.exist.") + ksfname);777}778}779}780781if ((command == KEYCLONE || command == CHANGEALIAS)782&& dest == null) {783dest = getAlias("destination");784if ("".equals(dest)) {785throw new Exception(rb.getString786("Must.specify.destination.alias"));787}788}789790if (command == DELETE && alias == null) {791alias = getAlias(null);792if ("".equals(alias)) {793throw new Exception(rb.getString("Must.specify.alias"));794}795}796797// Create new keystore798if (storetype == null) {799storetype = KeyStore.getDefaultType();800}801if (providerName == null) {802keyStore = KeyStore.getInstance(storetype);803} else {804keyStore = KeyStore.getInstance(storetype, providerName);805}806807/*808* Load the keystore data.809*810* At this point, it's OK if no keystore password has been provided.811* We want to make sure that we can load the keystore data, i.e.,812* the keystore data has the right format. If we cannot load the813* keystore, why bother asking the user for his or her password?814* Only if we were able to load the keystore, and no keystore815* password has been provided, will we prompt the user for the816* keystore password to verify the keystore integrity.817* This means that the keystore is loaded twice: first load operation818* checks the keystore format, second load operation verifies the819* keystore integrity.820*821* If the keystore password has already been provided (at the822* command line), however, the keystore is loaded only once, and the823* keystore format and integrity are checked "at the same time".824*825* Null stream keystores are loaded later.826*/827if (!nullStream) {828if (inplaceImport) {829keyStore.load(null, storePass);830} else {831keyStore.load(ksStream, storePass);832}833if (ksStream != null) {834ksStream.close();835}836}837838if (P12KEYSTORE.equalsIgnoreCase(storetype) && command == KEYPASSWD) {839throw new UnsupportedOperationException(rb.getString840(".keypasswd.commands.not.supported.if.storetype.is.PKCS12"));841}842843// All commands that create or modify the keystore require a keystore844// password.845846if (nullStream && storePass != null) {847keyStore.load(null, storePass);848} else if (!nullStream && storePass != null) {849// If we are creating a new non nullStream-based keystore,850// insist that the password be at least 6 characters851if (ksStream == null && storePass.length < 6) {852throw new Exception(rb.getString853("Keystore.password.must.be.at.least.6.characters"));854}855} else if (storePass == null) {856857// only prompt if (protectedPath == false)858859if (!protectedPath && !KeyStoreUtil.isWindowsKeyStore(storetype) &&860(command == CERTREQ ||861command == DELETE ||862command == GENKEYPAIR ||863command == GENSECKEY ||864command == IMPORTCERT ||865command == IMPORTPASS ||866command == IMPORTKEYSTORE ||867command == KEYCLONE ||868command == CHANGEALIAS ||869command == SELFCERT ||870command == STOREPASSWD ||871command == KEYPASSWD ||872command == IDENTITYDB)) {873int count = 0;874do {875if (command == IMPORTKEYSTORE) {876System.err.print877(rb.getString("Enter.destination.keystore.password."));878} else {879System.err.print880(rb.getString("Enter.keystore.password."));881}882System.err.flush();883storePass = Password.readPassword(System.in);884passwords.add(storePass);885886// If we are creating a new non nullStream-based keystore,887// insist that the password be at least 6 characters888if (!nullStream && (storePass == null || storePass.length < 6)) {889System.err.println(rb.getString890("Keystore.password.is.too.short.must.be.at.least.6.characters"));891storePass = null;892}893894// If the keystore file does not exist and needs to be895// created, the storepass should be prompted twice.896if (storePass != null && !nullStream && ksStream == null) {897System.err.print(rb.getString("Re.enter.new.password."));898char[] storePassAgain = Password.readPassword(System.in);899passwords.add(storePassAgain);900if (!Arrays.equals(storePass, storePassAgain)) {901System.err.println902(rb.getString("They.don.t.match.Try.again"));903storePass = null;904}905}906907count++;908} while ((storePass == null) && count < 3);909910911if (storePass == null) {912System.err.println913(rb.getString("Too.many.failures.try.later"));914return;915}916} else if (!protectedPath917&& !KeyStoreUtil.isWindowsKeyStore(storetype)918&& isKeyStoreRelated(command)) {919// here we have EXPORTCERT and LIST (info valid until STOREPASSWD)920if (command != PRINTCRL) {921System.err.print(rb.getString("Enter.keystore.password."));922System.err.flush();923storePass = Password.readPassword(System.in);924passwords.add(storePass);925}926}927928// Now load a nullStream-based keystore,929// or verify the integrity of an input stream-based keystore930if (nullStream) {931keyStore.load(null, storePass);932} else if (ksStream != null) {933ksStream = new FileInputStream(ksfile);934keyStore.load(ksStream, storePass);935ksStream.close();936}937}938939if (storePass != null && P12KEYSTORE.equalsIgnoreCase(storetype)) {940MessageFormat form = new MessageFormat(rb.getString(941"Warning.Different.store.and.key.passwords.not.supported.for.PKCS12.KeyStores.Ignoring.user.specified.command.value."));942if (keyPass != null && !Arrays.equals(storePass, keyPass)) {943Object[] source = {"-keypass"};944System.err.println(form.format(source));945keyPass = storePass;946}947if (newPass != null && !Arrays.equals(storePass, newPass)) {948Object[] source = {"-new"};949System.err.println(form.format(source));950newPass = storePass;951}952if (destKeyPass != null && !Arrays.equals(storePass, destKeyPass)) {953Object[] source = {"-destkeypass"};954System.err.println(form.format(source));955destKeyPass = storePass;956}957}958959// Create a certificate factory960if (command == PRINTCERT || command == IMPORTCERT961|| command == IDENTITYDB || command == PRINTCRL) {962cf = CertificateFactory.getInstance("X509");963}964965// -trustcacerts can only be specified on -importcert.966// Reset it so that warnings on CA cert will remain for967// -printcert, etc.968if (command != IMPORTCERT) {969trustcacerts = false;970}971972if (trustcacerts) {973caks = KeyStoreUtil.getCacertsKeyStore();974}975976// Perform the specified command977if (command == CERTREQ) {978if (filename != null) {979try (PrintStream ps = new PrintStream(new FileOutputStream980(filename))) {981doCertReq(alias, sigAlgName, ps);982}983} else {984doCertReq(alias, sigAlgName, out);985}986if (verbose && filename != null) {987MessageFormat form = new MessageFormat(rb.getString988("Certification.request.stored.in.file.filename."));989Object[] source = {filename};990System.err.println(form.format(source));991System.err.println(rb.getString("Submit.this.to.your.CA"));992}993} else if (command == DELETE) {994doDeleteEntry(alias);995kssave = true;996} else if (command == EXPORTCERT) {997if (filename != null) {998try (PrintStream ps = new PrintStream(new FileOutputStream999(filename))) {1000doExportCert(alias, ps);1001}1002} else {1003doExportCert(alias, out);1004}1005if (filename != null) {1006MessageFormat form = new MessageFormat(rb.getString1007("Certificate.stored.in.file.filename."));1008Object[] source = {filename};1009System.err.println(form.format(source));1010}1011} else if (command == GENKEYPAIR) {1012if (keyAlgName == null) {1013keyAlgName = "DSA";1014}1015doGenKeyPair(alias, dname, keyAlgName, keysize, sigAlgName);1016kssave = true;1017} else if (command == GENSECKEY) {1018if (keyAlgName == null) {1019keyAlgName = "DES";1020}1021doGenSecretKey(alias, keyAlgName, keysize);1022kssave = true;1023} else if (command == IMPORTPASS) {1024if (keyAlgName == null) {1025keyAlgName = "PBE";1026}1027// password is stored as a secret key1028doGenSecretKey(alias, keyAlgName, keysize);1029kssave = true;1030} else if (command == IDENTITYDB) {1031if (filename != null) {1032try (InputStream inStream = new FileInputStream(filename)) {1033doImportIdentityDatabase(inStream);1034}1035} else {1036doImportIdentityDatabase(System.in);1037}1038} else if (command == IMPORTCERT) {1039InputStream inStream = System.in;1040if (filename != null) {1041inStream = new FileInputStream(filename);1042}1043String importAlias = (alias!=null)?alias:keyAlias;1044try {1045if (keyStore.entryInstanceOf(1046importAlias, KeyStore.PrivateKeyEntry.class)) {1047kssave = installReply(importAlias, inStream);1048if (kssave) {1049System.err.println(rb.getString1050("Certificate.reply.was.installed.in.keystore"));1051} else {1052System.err.println(rb.getString1053("Certificate.reply.was.not.installed.in.keystore"));1054}1055} else if (!keyStore.containsAlias(importAlias) ||1056keyStore.entryInstanceOf(importAlias,1057KeyStore.TrustedCertificateEntry.class)) {1058kssave = addTrustedCert(importAlias, inStream);1059if (kssave) {1060System.err.println(rb.getString1061("Certificate.was.added.to.keystore"));1062} else {1063System.err.println(rb.getString1064("Certificate.was.not.added.to.keystore"));1065}1066}1067} finally {1068if (inStream != System.in) {1069inStream.close();1070}1071}1072} else if (command == IMPORTKEYSTORE) {1073// When not in-place import, srcKeyStore is not loaded yet.1074if (srcKeyStore == null) {1075srcKeyStore = loadSourceKeyStore();1076}1077doImportKeyStore(srcKeyStore);1078kssave = true;1079} else if (command == KEYCLONE) {1080keyPassNew = newPass;10811082// added to make sure only key can go thru1083if (alias == null) {1084alias = keyAlias;1085}1086if (keyStore.containsAlias(alias) == false) {1087MessageFormat form = new MessageFormat1088(rb.getString("Alias.alias.does.not.exist"));1089Object[] source = {alias};1090throw new Exception(form.format(source));1091}1092if (!keyStore.entryInstanceOf(alias, KeyStore.PrivateKeyEntry.class)) {1093MessageFormat form = new MessageFormat(rb.getString(1094"Alias.alias.references.an.entry.type.that.is.not.a.private.key.entry.The.keyclone.command.only.supports.cloning.of.private.key"));1095Object[] source = {alias};1096throw new Exception(form.format(source));1097}10981099doCloneEntry(alias, dest, true); // Now everything can be cloned1100kssave = true;1101} else if (command == CHANGEALIAS) {1102if (alias == null) {1103alias = keyAlias;1104}1105doCloneEntry(alias, dest, false);1106// in PKCS11, clone a PrivateKeyEntry will delete the old one1107if (keyStore.containsAlias(alias)) {1108doDeleteEntry(alias);1109}1110kssave = true;1111} else if (command == KEYPASSWD) {1112keyPassNew = newPass;1113doChangeKeyPasswd(alias);1114kssave = true;1115} else if (command == LIST) {1116if (storePass == null1117&& !KeyStoreUtil.isWindowsKeyStore(storetype)) {1118printNoIntegrityWarning();1119}11201121if (alias != null) {1122doPrintEntry(rb.getString("the.certificate"), alias, out);1123} else {1124doPrintEntries(out);1125}1126} else if (command == PRINTCERT) {1127doPrintCert(out);1128} else if (command == SELFCERT) {1129doSelfCert(alias, dname, sigAlgName);1130kssave = true;1131} else if (command == STOREPASSWD) {1132storePassNew = newPass;1133if (storePassNew == null) {1134storePassNew = getNewPasswd("keystore password", storePass);1135}1136kssave = true;1137} else if (command == GENCERT) {1138if (alias == null) {1139alias = keyAlias;1140}1141InputStream inStream = System.in;1142if (infilename != null) {1143inStream = new FileInputStream(infilename);1144}1145PrintStream ps = null;1146if (outfilename != null) {1147ps = new PrintStream(new FileOutputStream(outfilename));1148out = ps;1149}1150try {1151doGenCert(alias, sigAlgName, inStream, out);1152} finally {1153if (inStream != System.in) {1154inStream.close();1155}1156if (ps != null) {1157ps.close();1158}1159}1160} else if (command == GENCRL) {1161if (alias == null) {1162alias = keyAlias;1163}1164if (filename != null) {1165try (PrintStream ps =1166new PrintStream(new FileOutputStream(filename))) {1167doGenCRL(ps);1168}1169} else {1170doGenCRL(out);1171}1172} else if (command == PRINTCERTREQ) {1173if (filename != null) {1174try (InputStream inStream = new FileInputStream(filename)) {1175doPrintCertReq(inStream, out);1176}1177} else {1178doPrintCertReq(System.in, out);1179}1180} else if (command == PRINTCRL) {1181doPrintCRL(filename, out);1182}11831184// If we need to save the keystore, do so.1185if (kssave) {1186if (verbose) {1187MessageFormat form = new MessageFormat1188(rb.getString(".Storing.ksfname."));1189Object[] source = {nullStream ? "keystore" : ksfname};1190System.err.println(form.format(source));1191}11921193if (token) {1194keyStore.store(null, null);1195} else {1196char[] pass = (storePassNew!=null) ? storePassNew : storePass;1197if (nullStream) {1198keyStore.store(null, pass);1199} else {1200ByteArrayOutputStream bout = new ByteArrayOutputStream();1201keyStore.store(bout, pass);1202try (FileOutputStream fout = new FileOutputStream(ksfname)) {1203fout.write(bout.toByteArray());1204}1205}1206}1207}12081209if (isKeyStoreRelated(command)1210&& !token && !nullStream && ksfname != null) {12111212// JKS storetype warning on the final result keystore1213File f = new File(ksfname);1214if (f.exists()) {1215// Read the first 4 bytes to determine1216// if we're dealing with JKS/JCEKS type store1217String realType = keyStoreType(f);1218if (realType.equalsIgnoreCase("JKS")1219|| realType.equalsIgnoreCase("JCEKS")) {1220boolean allCerts = true;1221for (String a : Collections.list(keyStore.aliases())) {1222if (!keyStore.entryInstanceOf(1223a, TrustedCertificateEntry.class)) {1224allCerts = false;1225break;1226}1227}1228// Don't warn for "cacerts" style keystore.1229if (!allCerts) {1230weakWarnings.add(String.format(1231rb.getString("jks.storetype.warning"),1232realType, ksfname));1233}1234}1235if (inplaceImport) {1236String realSourceStoreType =1237keyStoreType(new File(inplaceBackupName));1238String format =1239realType.equalsIgnoreCase(realSourceStoreType) ?1240rb.getString("backup.keystore.warning") :1241rb.getString("migrate.keystore.warning");1242weakWarnings.add(1243String.format(format,1244srcksfname,1245realSourceStoreType,1246inplaceBackupName,1247realType));1248}1249}1250}1251}12521253private String keyStoreType(File f) throws IOException {1254int MAGIC = 0xfeedfeed;1255int JCEKS_MAGIC = 0xcececece;1256try (DataInputStream dis = new DataInputStream(1257new FileInputStream(f))) {1258int xMagic = dis.readInt();1259if (xMagic == MAGIC) {1260return "JKS";1261} else if (xMagic == JCEKS_MAGIC) {1262return "JCEKS";1263} else {1264return "Non JKS/JCEKS";1265}1266}1267}12681269/**1270* Generate a certificate: Read PKCS10 request from in, and print1271* certificate to out. Use alias as CA, sigAlgName as the signature1272* type.1273*/1274private void doGenCert(String alias, String sigAlgName, InputStream in, PrintStream out)1275throws Exception {127612771278if (keyStore.containsAlias(alias) == false) {1279MessageFormat form = new MessageFormat1280(rb.getString("Alias.alias.does.not.exist"));1281Object[] source = {alias};1282throw new Exception(form.format(source));1283}1284Certificate signerCert = keyStore.getCertificate(alias);1285byte[] encoded = signerCert.getEncoded();1286X509CertImpl signerCertImpl = new X509CertImpl(encoded);1287X509CertInfo signerCertInfo = (X509CertInfo)signerCertImpl.get(1288X509CertImpl.NAME + "." + X509CertImpl.INFO);1289X500Name issuer = (X500Name)signerCertInfo.get(X509CertInfo.SUBJECT + "." +1290X509CertInfo.DN_NAME);12911292Date firstDate = getStartDate(startDate);1293Date lastDate = new Date();1294lastDate.setTime(firstDate.getTime() + validity*1000L*24L*60L*60L);1295CertificateValidity interval = new CertificateValidity(firstDate,1296lastDate);12971298PrivateKey privateKey =1299(PrivateKey)recoverKey(alias, storePass, keyPass).fst;1300if (sigAlgName == null) {1301sigAlgName = getCompatibleSigAlgName(privateKey.getAlgorithm());1302}1303Signature signature = Signature.getInstance(sigAlgName);1304AlgorithmParameterSpec params = AlgorithmId1305.getDefaultAlgorithmParameterSpec(sigAlgName, privateKey);13061307SignatureUtil.initSignWithParam(signature, privateKey, params, null);13081309X509CertInfo info = new X509CertInfo();1310AlgorithmId algID = AlgorithmId.getWithParameterSpec(sigAlgName, params);1311info.set(X509CertInfo.VALIDITY, interval);1312info.set(X509CertInfo.SERIAL_NUMBER, new CertificateSerialNumber(1313new java.util.Random().nextInt() & 0x7fffffff));1314info.set(X509CertInfo.VERSION,1315new CertificateVersion(CertificateVersion.V3));1316info.set(X509CertInfo.ALGORITHM_ID,1317new CertificateAlgorithmId(algID));1318info.set(X509CertInfo.ISSUER, issuer);13191320BufferedReader reader = new BufferedReader(new InputStreamReader(in));1321boolean canRead = false;1322StringBuffer sb = new StringBuffer();1323while (true) {1324String s = reader.readLine();1325if (s == null) break;1326// OpenSSL does not use NEW1327//if (s.startsWith("-----BEGIN NEW CERTIFICATE REQUEST-----")) {1328if (s.startsWith("-----BEGIN") && s.indexOf("REQUEST") >= 0) {1329canRead = true;1330//} else if (s.startsWith("-----END NEW CERTIFICATE REQUEST-----")) {1331} else if (s.startsWith("-----END") && s.indexOf("REQUEST") >= 0) {1332break;1333} else if (canRead) {1334sb.append(s);1335}1336}1337byte[] rawReq = Pem.decode(new String(sb));1338PKCS10 req = new PKCS10(rawReq);13391340checkWeak(rb.getString("the.certificate.request"), req);13411342info.set(X509CertInfo.KEY, new CertificateX509Key(req.getSubjectPublicKeyInfo()));1343info.set(X509CertInfo.SUBJECT,1344dname==null?req.getSubjectName():new X500Name(dname));1345CertificateExtensions reqex = null;1346Iterator<PKCS10Attribute> attrs = req.getAttributes().getAttributes().iterator();1347while (attrs.hasNext()) {1348PKCS10Attribute attr = attrs.next();1349if (attr.getAttributeId().equals((Object)PKCS9Attribute.EXTENSION_REQUEST_OID)) {1350reqex = (CertificateExtensions)attr.getAttributeValue();1351}1352}1353CertificateExtensions ext = createV3Extensions(1354reqex,1355null,1356v3ext,1357req.getSubjectPublicKeyInfo(),1358signerCert.getPublicKey());1359info.set(X509CertInfo.EXTENSIONS, ext);1360X509CertImpl cert = new X509CertImpl(info);1361cert.sign(privateKey, params, sigAlgName, null);1362dumpCert(cert, out);1363for (Certificate ca: keyStore.getCertificateChain(alias)) {1364if (ca instanceof X509Certificate) {1365X509Certificate xca = (X509Certificate)ca;1366if (!KeyStoreUtil.isSelfSigned(xca)) {1367dumpCert(xca, out);1368}1369}1370}13711372checkWeak(rb.getString("the.issuer"), keyStore.getCertificateChain(alias));1373checkWeak(rb.getString("the.generated.certificate"), cert);1374}13751376private void doGenCRL(PrintStream out)1377throws Exception {1378if (ids == null) {1379throw new Exception("Must provide -id when -gencrl");1380}1381Certificate signerCert = keyStore.getCertificate(alias);1382byte[] encoded = signerCert.getEncoded();1383X509CertImpl signerCertImpl = new X509CertImpl(encoded);1384X509CertInfo signerCertInfo = (X509CertInfo)signerCertImpl.get(1385X509CertImpl.NAME + "." + X509CertImpl.INFO);1386X500Name owner = (X500Name)signerCertInfo.get(X509CertInfo.SUBJECT + "." +1387X509CertInfo.DN_NAME);13881389Date firstDate = getStartDate(startDate);1390Date lastDate = (Date) firstDate.clone();1391lastDate.setTime(lastDate.getTime() + validity*1000*24*60*60);1392CertificateValidity interval = new CertificateValidity(firstDate,1393lastDate);139413951396PrivateKey privateKey =1397(PrivateKey)recoverKey(alias, storePass, keyPass).fst;1398if (sigAlgName == null) {1399sigAlgName = getCompatibleSigAlgName(privateKey.getAlgorithm());1400}14011402X509CRLEntry[] badCerts = new X509CRLEntry[ids.size()];1403for (int i=0; i<ids.size(); i++) {1404String id = ids.get(i);1405int d = id.indexOf(':');1406if (d >= 0) {1407CRLExtensions ext = new CRLExtensions();1408ext.set("Reason", new CRLReasonCodeExtension(Integer.parseInt(id.substring(d+1))));1409badCerts[i] = new X509CRLEntryImpl(new BigInteger(id.substring(0, d)),1410firstDate, ext);1411} else {1412badCerts[i] = new X509CRLEntryImpl(new BigInteger(ids.get(i)), firstDate);1413}1414}1415X509CRLImpl crl = new X509CRLImpl(owner, firstDate, lastDate, badCerts);1416crl.sign(privateKey, sigAlgName);1417if (rfc) {1418out.println("-----BEGIN X509 CRL-----");1419out.println(Base64.getMimeEncoder(64, CRLF).encodeToString(crl.getEncodedInternal()));1420out.println("-----END X509 CRL-----");1421} else {1422out.write(crl.getEncodedInternal());1423}1424checkWeak(rb.getString("the.generated.crl"), crl, privateKey);1425}14261427/**1428* Creates a PKCS#10 cert signing request, corresponding to the1429* keys (and name) associated with a given alias.1430*/1431private void doCertReq(String alias, String sigAlgName, PrintStream out)1432throws Exception1433{1434if (alias == null) {1435alias = keyAlias;1436}14371438Pair<Key,char[]> objs = recoverKey(alias, storePass, keyPass);1439PrivateKey privKey = (PrivateKey)objs.fst;1440if (keyPass == null) {1441keyPass = objs.snd;1442}14431444Certificate cert = keyStore.getCertificate(alias);1445if (cert == null) {1446MessageFormat form = new MessageFormat1447(rb.getString("alias.has.no.public.key.certificate."));1448Object[] source = {alias};1449throw new Exception(form.format(source));1450}1451PKCS10 request = new PKCS10(cert.getPublicKey());1452CertificateExtensions ext = createV3Extensions(null, null, v3ext, cert.getPublicKey(), null);1453// Attribute name is not significant1454request.getAttributes().setAttribute(X509CertInfo.EXTENSIONS,1455new PKCS10Attribute(PKCS9Attribute.EXTENSION_REQUEST_OID, ext));14561457// Construct a Signature object, so that we can sign the request1458if (sigAlgName == null) {1459sigAlgName = getCompatibleSigAlgName(privKey.getAlgorithm());1460}14611462Signature signature = Signature.getInstance(sigAlgName);1463AlgorithmParameterSpec params = AlgorithmId1464.getDefaultAlgorithmParameterSpec(sigAlgName, privKey);1465SignatureUtil.initSignWithParam(signature, privKey, params, null);14661467X500Name subject = dname == null?1468new X500Name(((X509Certificate)cert).getSubjectDN().toString()):1469new X500Name(dname);14701471// Sign the request and base-64 encode it1472request.encodeAndSign(subject, signature);1473request.print(out);14741475checkWeak(rb.getString("the.generated.certificate.request"), request);1476}14771478/**1479* Deletes an entry from the keystore.1480*/1481private void doDeleteEntry(String alias) throws Exception {1482if (keyStore.containsAlias(alias) == false) {1483MessageFormat form = new MessageFormat1484(rb.getString("Alias.alias.does.not.exist"));1485Object[] source = {alias};1486throw new Exception(form.format(source));1487}1488keyStore.deleteEntry(alias);1489}14901491/**1492* Exports a certificate from the keystore.1493*/1494private void doExportCert(String alias, PrintStream out)1495throws Exception1496{1497if (storePass == null1498&& !KeyStoreUtil.isWindowsKeyStore(storetype)) {1499printNoIntegrityWarning();1500}1501if (alias == null) {1502alias = keyAlias;1503}1504if (keyStore.containsAlias(alias) == false) {1505MessageFormat form = new MessageFormat1506(rb.getString("Alias.alias.does.not.exist"));1507Object[] source = {alias};1508throw new Exception(form.format(source));1509}15101511X509Certificate cert = (X509Certificate)keyStore.getCertificate(alias);1512if (cert == null) {1513MessageFormat form = new MessageFormat1514(rb.getString("Alias.alias.has.no.certificate"));1515Object[] source = {alias};1516throw new Exception(form.format(source));1517}1518dumpCert(cert, out);1519checkWeak(rb.getString("the.certificate"), cert);1520}15211522/**1523* Prompt the user for a keypass when generating a key entry.1524* @param alias the entry we will set password for1525* @param orig the original entry of doing a dup, null if generate new1526* @param origPass the password to copy from if user press ENTER1527*/1528private char[] promptForKeyPass(String alias, String orig, char[] origPass) throws Exception{1529if (P12KEYSTORE.equalsIgnoreCase(storetype)) {1530return origPass;1531} else if (!token && !protectedPath) {1532// Prompt for key password1533int count;1534for (count = 0; count < 3; count++) {1535MessageFormat form = new MessageFormat(rb.getString1536("Enter.key.password.for.alias."));1537Object[] source = {alias};1538System.err.println(form.format(source));1539if (orig == null) {1540System.err.print(rb.getString1541(".RETURN.if.same.as.keystore.password."));1542} else {1543form = new MessageFormat(rb.getString1544(".RETURN.if.same.as.for.otherAlias."));1545Object[] src = {orig};1546System.err.print(form.format(src));1547}1548System.err.flush();1549char[] entered = Password.readPassword(System.in);1550passwords.add(entered);1551if (entered == null) {1552return origPass;1553} else if (entered.length >= 6) {1554System.err.print(rb.getString("Re.enter.new.password."));1555char[] passAgain = Password.readPassword(System.in);1556passwords.add(passAgain);1557if (!Arrays.equals(entered, passAgain)) {1558System.err.println1559(rb.getString("They.don.t.match.Try.again"));1560continue;1561}1562return entered;1563} else {1564System.err.println(rb.getString1565("Key.password.is.too.short.must.be.at.least.6.characters"));1566}1567}1568if (count == 3) {1569if (command == KEYCLONE) {1570throw new Exception(rb.getString1571("Too.many.failures.Key.entry.not.cloned"));1572} else {1573throw new Exception(rb.getString1574("Too.many.failures.key.not.added.to.keystore"));1575}1576}1577}1578return null; // PKCS11, MSCAPI, or -protected1579}15801581/*1582* Prompt the user for the password credential to be stored.1583*/1584private char[] promptForCredential() throws Exception {1585// Handle password supplied via stdin1586if (System.console() == null) {1587char[] importPass = Password.readPassword(System.in);1588passwords.add(importPass);1589return importPass;1590}15911592int count;1593for (count = 0; count < 3; count++) {1594System.err.print(1595rb.getString("Enter.the.password.to.be.stored."));1596System.err.flush();1597char[] entered = Password.readPassword(System.in);1598passwords.add(entered);1599System.err.print(rb.getString("Re.enter.password."));1600char[] passAgain = Password.readPassword(System.in);1601passwords.add(passAgain);1602if (!Arrays.equals(entered, passAgain)) {1603System.err.println(rb.getString("They.don.t.match.Try.again"));1604continue;1605}1606return entered;1607}16081609if (count == 3) {1610throw new Exception(rb.getString1611("Too.many.failures.key.not.added.to.keystore"));1612}16131614return null;1615}16161617/**1618* Creates a new secret key.1619*/1620private void doGenSecretKey(String alias, String keyAlgName,1621int keysize)1622throws Exception1623{1624if (alias == null) {1625alias = keyAlias;1626}1627if (keyStore.containsAlias(alias)) {1628MessageFormat form = new MessageFormat(rb.getString1629("Secret.key.not.generated.alias.alias.already.exists"));1630Object[] source = {alias};1631throw new Exception(form.format(source));1632}16331634// Use the keystore's default PBE algorithm for entry protection1635boolean useDefaultPBEAlgorithm = true;1636SecretKey secKey = null;16371638if (keyAlgName.toUpperCase(Locale.ENGLISH).startsWith("PBE")) {1639SecretKeyFactory factory = SecretKeyFactory.getInstance("PBE");16401641// User is prompted for PBE credential1642secKey =1643factory.generateSecret(new PBEKeySpec(promptForCredential()));16441645// Check whether a specific PBE algorithm was specified1646if (!"PBE".equalsIgnoreCase(keyAlgName)) {1647useDefaultPBEAlgorithm = false;1648}16491650if (verbose) {1651MessageFormat form = new MessageFormat(rb.getString(1652"Generated.keyAlgName.secret.key"));1653Object[] source =1654{useDefaultPBEAlgorithm ? "PBE" : secKey.getAlgorithm()};1655System.err.println(form.format(source));1656}1657} else {1658KeyGenerator keygen = KeyGenerator.getInstance(keyAlgName);1659if (keysize == -1) {1660if ("DES".equalsIgnoreCase(keyAlgName)) {1661keysize = 56;1662} else if ("DESede".equalsIgnoreCase(keyAlgName)) {1663keysize = 168;1664} else {1665throw new Exception(rb.getString1666("Please.provide.keysize.for.secret.key.generation"));1667}1668}1669keygen.init(keysize);1670secKey = keygen.generateKey();16711672if (verbose) {1673MessageFormat form = new MessageFormat(rb.getString1674("Generated.keysize.bit.keyAlgName.secret.key"));1675Object[] source = {new Integer(keysize),1676secKey.getAlgorithm()};1677System.err.println(form.format(source));1678}1679}16801681if (keyPass == null) {1682keyPass = promptForKeyPass(alias, null, storePass);1683}16841685if (useDefaultPBEAlgorithm) {1686keyStore.setKeyEntry(alias, secKey, keyPass, null);1687} else {1688keyStore.setEntry(alias, new KeyStore.SecretKeyEntry(secKey),1689new KeyStore.PasswordProtection(keyPass, keyAlgName, null));1690}1691}16921693/**1694* If no signature algorithm was specified at the command line,1695* we choose one that is compatible with the selected private key1696*/1697private static String getCompatibleSigAlgName(String keyAlgName)1698throws Exception {1699if ("DSA".equalsIgnoreCase(keyAlgName)) {1700return "SHA256WithDSA";1701} else if ("RSA".equalsIgnoreCase(keyAlgName)) {1702return "SHA256WithRSA";1703} else if ("EC".equalsIgnoreCase(keyAlgName)) {1704return "SHA256withECDSA";1705} else {1706throw new Exception(rb.getString1707("Cannot.derive.signature.algorithm"));1708}1709}1710/**1711* Creates a new key pair and self-signed certificate.1712*/1713private void doGenKeyPair(String alias, String dname, String keyAlgName,1714int keysize, String sigAlgName)1715throws Exception1716{1717if (keysize == -1) {1718if ("EC".equalsIgnoreCase(keyAlgName)) {1719keysize = SecurityProviderConstants.DEF_EC_KEY_SIZE;1720} else if ("RSA".equalsIgnoreCase(keyAlgName)) {1721keysize = SecurityProviderConstants.DEF_RSA_KEY_SIZE;1722} else if ("RSASSA-PSS".equalsIgnoreCase(keyAlgName)) {1723keysize = SecurityProviderConstants.DEF_RSASSA_PSS_KEY_SIZE;1724} else if ("DSA".equalsIgnoreCase(keyAlgName)) {1725keysize = SecurityProviderConstants.DEF_DSA_KEY_SIZE;1726}1727}17281729if (alias == null) {1730alias = keyAlias;1731}17321733if (keyStore.containsAlias(alias)) {1734MessageFormat form = new MessageFormat(rb.getString1735("Key.pair.not.generated.alias.alias.already.exists"));1736Object[] source = {alias};1737throw new Exception(form.format(source));1738}17391740if (sigAlgName == null) {1741sigAlgName = getCompatibleSigAlgName(keyAlgName);1742}1743CertAndKeyGen keypair =1744new CertAndKeyGen(keyAlgName, sigAlgName, providerName);174517461747// If DN is provided, parse it. Otherwise, prompt the user for it.1748X500Name x500Name;1749if (dname == null) {1750x500Name = getX500Name();1751} else {1752x500Name = new X500Name(dname);1753}17541755keypair.generate(keysize);1756PrivateKey privKey = keypair.getPrivateKey();17571758CertificateExtensions ext = createV3Extensions(1759null,1760null,1761v3ext,1762keypair.getPublicKeyAnyway(),1763null);17641765X509Certificate[] chain = new X509Certificate[1];1766chain[0] = keypair.getSelfCertificate(1767x500Name, getStartDate(startDate), validity*24L*60L*60L, ext);17681769if (verbose) {1770MessageFormat form = new MessageFormat(rb.getString1771("Generating.keysize.bit.keyAlgName.key.pair.and.self.signed.certificate.sigAlgName.with.a.validity.of.validality.days.for"));1772Object[] source = {new Integer(keysize),1773privKey.getAlgorithm(),1774chain[0].getSigAlgName(),1775new Long(validity),1776x500Name};1777System.err.println(form.format(source));1778}17791780if (keyPass == null) {1781keyPass = promptForKeyPass(alias, null, storePass);1782}1783checkWeak(rb.getString("the.generated.certificate"), chain[0]);1784keyStore.setKeyEntry(alias, privKey, keyPass, chain);1785}17861787/**1788* Clones an entry1789* @param orig original alias1790* @param dest destination alias1791* @changePassword if the password can be changed1792*/1793private void doCloneEntry(String orig, String dest, boolean changePassword)1794throws Exception1795{1796if (orig == null) {1797orig = keyAlias;1798}17991800if (keyStore.containsAlias(dest)) {1801MessageFormat form = new MessageFormat1802(rb.getString("Destination.alias.dest.already.exists"));1803Object[] source = {dest};1804throw new Exception(form.format(source));1805}18061807Pair<Entry,char[]> objs = recoverEntry(keyStore, orig, storePass, keyPass);1808Entry entry = objs.fst;1809keyPass = objs.snd;18101811PasswordProtection pp = null;18121813if (keyPass != null) { // protected1814if (!changePassword || P12KEYSTORE.equalsIgnoreCase(storetype)) {1815keyPassNew = keyPass;1816} else {1817if (keyPassNew == null) {1818keyPassNew = promptForKeyPass(dest, orig, keyPass);1819}1820}1821pp = new PasswordProtection(keyPassNew);1822}1823keyStore.setEntry(dest, entry, pp);1824}18251826/**1827* Changes a key password.1828*/1829private void doChangeKeyPasswd(String alias) throws Exception1830{18311832if (alias == null) {1833alias = keyAlias;1834}1835Pair<Key,char[]> objs = recoverKey(alias, storePass, keyPass);1836Key privKey = objs.fst;1837if (keyPass == null) {1838keyPass = objs.snd;1839}18401841if (keyPassNew == null) {1842MessageFormat form = new MessageFormat1843(rb.getString("key.password.for.alias."));1844Object[] source = {alias};1845keyPassNew = getNewPasswd(form.format(source), keyPass);1846}1847keyStore.setKeyEntry(alias, privKey, keyPassNew,1848keyStore.getCertificateChain(alias));1849}18501851/**1852* Imports a JDK 1.1-style identity database. We can only store one1853* certificate per identity, because we use the identity's name as the1854* alias (which references a keystore entry), and aliases must be unique.1855*/1856private void doImportIdentityDatabase(InputStream in)1857throws Exception1858{1859System.err.println(rb.getString1860("No.entries.from.identity.database.added"));1861}18621863/**1864* Prints a single keystore entry.1865*/1866private void doPrintEntry(String label, String alias, PrintStream out)1867throws Exception1868{1869if (keyStore.containsAlias(alias) == false) {1870MessageFormat form = new MessageFormat1871(rb.getString("Alias.alias.does.not.exist"));1872Object[] source = {alias};1873throw new Exception(form.format(source));1874}18751876if (verbose || rfc || debug) {1877MessageFormat form = new MessageFormat1878(rb.getString("Alias.name.alias"));1879Object[] source = {alias};1880out.println(form.format(source));18811882if (!token) {1883form = new MessageFormat(rb.getString1884("Creation.date.keyStore.getCreationDate.alias."));1885Object[] src = {keyStore.getCreationDate(alias)};1886out.println(form.format(src));1887}1888} else {1889if (!token) {1890MessageFormat form = new MessageFormat1891(rb.getString("alias.keyStore.getCreationDate.alias."));1892Object[] source = {alias, keyStore.getCreationDate(alias)};1893out.print(form.format(source));1894} else {1895MessageFormat form = new MessageFormat1896(rb.getString("alias."));1897Object[] source = {alias};1898out.print(form.format(source));1899}1900}19011902if (keyStore.entryInstanceOf(alias, KeyStore.SecretKeyEntry.class)) {1903if (verbose || rfc || debug) {1904Object[] source = {"SecretKeyEntry"};1905out.println(new MessageFormat(1906rb.getString("Entry.type.type.")).format(source));1907} else {1908out.println("SecretKeyEntry, ");1909}1910} else if (keyStore.entryInstanceOf(alias, KeyStore.PrivateKeyEntry.class)) {1911if (verbose || rfc || debug) {1912Object[] source = {"PrivateKeyEntry"};1913out.println(new MessageFormat(1914rb.getString("Entry.type.type.")).format(source));1915} else {1916out.println("PrivateKeyEntry, ");1917}19181919// Get the chain1920Certificate[] chain = keyStore.getCertificateChain(alias);1921if (chain != null) {1922if (verbose || rfc || debug) {1923out.println(rb.getString1924("Certificate.chain.length.") + chain.length);1925for (int i = 0; i < chain.length; i ++) {1926MessageFormat form = new MessageFormat1927(rb.getString("Certificate.i.1."));1928Object[] source = {new Integer((i + 1))};1929out.println(form.format(source));1930if (verbose && (chain[i] instanceof X509Certificate)) {1931printX509Cert((X509Certificate)(chain[i]), out);1932} else if (debug) {1933out.println(chain[i].toString());1934} else {1935dumpCert(chain[i], out);1936}1937checkWeak(label, chain[i]);1938}1939} else {1940// Print the digest of the user cert only1941out.println1942(rb.getString("Certificate.fingerprint.SHA.256.") +1943getCertFingerPrint("SHA-256", chain[0]));1944checkWeak(label, chain[0]);1945}1946}1947} else if (keyStore.entryInstanceOf(alias,1948KeyStore.TrustedCertificateEntry.class)) {1949// We have a trusted certificate entry1950Certificate cert = keyStore.getCertificate(alias);1951Object[] source = {"trustedCertEntry"};1952String mf = new MessageFormat(1953rb.getString("Entry.type.type.")).format(source) + "\n";1954if (verbose && (cert instanceof X509Certificate)) {1955out.println(mf);1956printX509Cert((X509Certificate)cert, out);1957} else if (rfc) {1958out.println(mf);1959dumpCert(cert, out);1960} else if (debug) {1961out.println(cert.toString());1962} else {1963out.println("trustedCertEntry, ");1964out.println(rb.getString("Certificate.fingerprint.SHA.256.")1965+ getCertFingerPrint("SHA-256", cert));1966}1967checkWeak(label, cert);1968} else {1969out.println(rb.getString("Unknown.Entry.Type"));1970}1971}19721973boolean inplaceImportCheck() throws Exception {1974if (P11KEYSTORE.equalsIgnoreCase(srcstoretype) ||1975KeyStoreUtil.isWindowsKeyStore(srcstoretype)) {1976return false;1977}19781979if (srcksfname != null) {1980File srcksfile = new File(srcksfname);1981if (srcksfile.exists() && srcksfile.length() == 0) {1982throw new Exception(rb.getString1983("Source.keystore.file.exists.but.is.empty.") +1984srcksfname);1985}1986if (srcksfile.getCanonicalFile()1987.equals(new File(ksfname).getCanonicalFile())) {1988return true;1989} else {1990// Informational, especially if destkeystore is not1991// provided, which default to ~/.keystore.1992System.err.println(String.format(rb.getString(1993"importing.keystore.status"), srcksfname, ksfname));1994return false;1995}1996} else {1997throw new Exception(rb.getString1998("Please.specify.srckeystore"));1999}2000}20012002/**2003* Load the srckeystore from a stream, used in -importkeystore2004* @returns the src KeyStore2005*/2006KeyStore loadSourceKeyStore() throws Exception {20072008InputStream is = null;2009File srcksfile = null;20102011if (P11KEYSTORE.equalsIgnoreCase(srcstoretype) ||2012KeyStoreUtil.isWindowsKeyStore(srcstoretype)) {2013if (!NONE.equals(srcksfname)) {2014System.err.println(MessageFormat.format(rb.getString2015(".keystore.must.be.NONE.if.storetype.is.{0}"), srcstoretype));2016System.err.println();2017tinyHelp();2018}2019} else {2020srcksfile = new File(srcksfname);2021is = new FileInputStream(srcksfile);2022}20232024KeyStore store;2025try {2026if (srcstoretype == null) {2027srcstoretype = KeyStore.getDefaultType();2028}2029if (srcProviderName == null) {2030store = KeyStore.getInstance(srcstoretype);2031} else {2032store = KeyStore.getInstance(srcstoretype, srcProviderName);2033}20342035if (srcstorePass == null2036&& !srcprotectedPath2037&& !KeyStoreUtil.isWindowsKeyStore(srcstoretype)) {2038System.err.print(rb.getString("Enter.source.keystore.password."));2039System.err.flush();2040srcstorePass = Password.readPassword(System.in);2041passwords.add(srcstorePass);2042}20432044// always let keypass be storepass when using pkcs122045if (P12KEYSTORE.equalsIgnoreCase(srcstoretype)) {2046if (srckeyPass != null && srcstorePass != null &&2047!Arrays.equals(srcstorePass, srckeyPass)) {2048MessageFormat form = new MessageFormat(rb.getString(2049"Warning.Different.store.and.key.passwords.not.supported.for.PKCS12.KeyStores.Ignoring.user.specified.command.value."));2050Object[] source = {"-srckeypass"};2051System.err.println(form.format(source));2052srckeyPass = srcstorePass;2053}2054}20552056store.load(is, srcstorePass); // "is" already null in PKCS112057} finally {2058if (is != null) {2059is.close();2060}2061}20622063if (srcstorePass == null2064&& !KeyStoreUtil.isWindowsKeyStore(srcstoretype)) {2065// anti refactoring, copied from printNoIntegrityWarning(),2066// but change 2 lines2067System.err.println();2068System.err.println(rb.getString2069(".WARNING.WARNING.WARNING."));2070System.err.println(rb.getString2071(".The.integrity.of.the.information.stored.in.the.srckeystore."));2072System.err.println(rb.getString2073(".WARNING.WARNING.WARNING."));2074System.err.println();2075}20762077return store;2078}20792080/**2081* import all keys and certs from importkeystore.2082* keep alias unchanged if no name conflict, otherwise, prompt.2083* keep keypass unchanged for keys2084*/2085private void doImportKeyStore(KeyStore srcKS) throws Exception {20862087if (alias != null) {2088doImportKeyStoreSingle(srcKS, alias);2089} else {2090if (dest != null || srckeyPass != null) {2091throw new Exception(rb.getString(2092"if.alias.not.specified.destalias.and.srckeypass.must.not.be.specified"));2093}2094doImportKeyStoreAll(srcKS);2095}20962097if (inplaceImport) {2098// Backup to file.old or file.old2...2099// The keystore is not rewritten yet now.2100for (int n = 1; /* forever */; n++) {2101inplaceBackupName = srcksfname + ".old" + (n == 1 ? "" : n);2102File bkFile = new File(inplaceBackupName);2103if (!bkFile.exists()) {2104Files.copy(Paths.get(srcksfname), bkFile.toPath());2105break;2106}2107}21082109}21102111/*2112* Information display rule of -importkeystore2113* 1. inside single, shows failure2114* 2. inside all, shows sucess2115* 3. inside all where there is a failure, prompt for continue2116* 4. at the final of all, shows summary2117*/2118}21192120/**2121* Import a single entry named alias from srckeystore2122* @returns 1 if the import action succeed2123* 0 if user choose to ignore an alias-dumplicated entry2124* 2 if setEntry throws Exception2125*/2126private int doImportKeyStoreSingle(KeyStore srckeystore, String alias)2127throws Exception {21282129String newAlias = (dest==null) ? alias : dest;21302131if (keyStore.containsAlias(newAlias)) {2132Object[] source = {alias};2133if (noprompt) {2134System.err.println(new MessageFormat(rb.getString(2135"Warning.Overwriting.existing.alias.alias.in.destination.keystore")).format(source));2136} else {2137String reply = getYesNoReply(new MessageFormat(rb.getString(2138"Existing.entry.alias.alias.exists.overwrite.no.")).format(source));2139if ("NO".equals(reply)) {2140newAlias = inputStringFromStdin(rb.getString2141("Enter.new.alias.name.RETURN.to.cancel.import.for.this.entry."));2142if ("".equals(newAlias)) {2143System.err.println(new MessageFormat(rb.getString(2144"Entry.for.alias.alias.not.imported.")).format(2145source));2146return 0;2147}2148}2149}2150}21512152Pair<Entry,char[]> objs = recoverEntry(srckeystore, alias, srcstorePass, srckeyPass);2153Entry entry = objs.fst;21542155PasswordProtection pp = null;21562157// According to keytool.html, "The destination entry will be protected2158// using destkeypass. If destkeypass is not provided, the destination2159// entry will be protected with the source entry password."2160// so always try to protect with destKeyPass.2161char[] newPass = null;2162if (destKeyPass != null) {2163newPass = destKeyPass;2164pp = new PasswordProtection(destKeyPass);2165} else if (objs.snd != null) {2166newPass = objs.snd;2167pp = new PasswordProtection(objs.snd);2168}21692170try {2171Certificate c = srckeystore.getCertificate(alias);2172if (c != null) {2173checkWeak("<" + newAlias + ">", c);2174}2175keyStore.setEntry(newAlias, entry, pp);2176// Place the check so that only successful imports are blocked.2177// For example, we don't block a failed SecretEntry import.2178if (P12KEYSTORE.equalsIgnoreCase(storetype)) {2179if (newPass != null && !Arrays.equals(newPass, storePass)) {2180throw new Exception(rb.getString(2181"The.destination.pkcs12.keystore.has.different.storepass.and.keypass.Please.retry.with.destkeypass.specified."));2182}2183}2184return 1;2185} catch (KeyStoreException kse) {2186Object[] source2 = {alias, kse.toString()};2187MessageFormat form = new MessageFormat(rb.getString(2188"Problem.importing.entry.for.alias.alias.exception.Entry.for.alias.alias.not.imported."));2189System.err.println(form.format(source2));2190return 2;2191}2192}21932194private void doImportKeyStoreAll(KeyStore srckeystore) throws Exception {21952196int ok = 0;2197int count = srckeystore.size();2198for (Enumeration<String> e = srckeystore.aliases();2199e.hasMoreElements(); ) {2200String alias = e.nextElement();2201int result = doImportKeyStoreSingle(srckeystore, alias);2202if (result == 1) {2203ok++;2204Object[] source = {alias};2205MessageFormat form = new MessageFormat(rb.getString("Entry.for.alias.alias.successfully.imported."));2206System.err.println(form.format(source));2207} else if (result == 2) {2208if (!noprompt) {2209String reply = getYesNoReply("Do you want to quit the import process? [no]: ");2210if ("YES".equals(reply)) {2211break;2212}2213}2214}2215}2216Object[] source = {ok, count-ok};2217MessageFormat form = new MessageFormat(rb.getString(2218"Import.command.completed.ok.entries.successfully.imported.fail.entries.failed.or.cancelled"));2219System.err.println(form.format(source));2220}22212222/**2223* Prints all keystore entries.2224*/2225private void doPrintEntries(PrintStream out)2226throws Exception2227{2228// Adjust displayed keystore type if needed.2229String keystoreTypeToPrint = keyStore.getType();2230if ("JKS".equalsIgnoreCase(keystoreTypeToPrint)) {2231if (ksfile != null && ksfile.exists()) {2232String realType = keyStoreType(ksfile);2233// If the magic number does not conform to JKS2234// then it must be PKCS122235if (!"JKS".equalsIgnoreCase(realType)) {2236keystoreTypeToPrint = P12KEYSTORE;2237}2238}2239}2240out.println(rb.getString("Keystore.type.") + keystoreTypeToPrint);2241out.println(rb.getString("Keystore.provider.") +2242keyStore.getProvider().getName());2243out.println();22442245MessageFormat form;2246form = (keyStore.size() == 1) ?2247new MessageFormat(rb.getString2248("Your.keystore.contains.keyStore.size.entry")) :2249new MessageFormat(rb.getString2250("Your.keystore.contains.keyStore.size.entries"));2251Object[] source = {new Integer(keyStore.size())};2252out.println(form.format(source));2253out.println();22542255List<String> aliases = Collections.list(keyStore.aliases());2256aliases.sort(String::compareTo);2257for (String alias : aliases) {2258doPrintEntry("<" + alias + ">", alias, out);2259if (verbose || rfc) {2260out.println(rb.getString("NEWLINE"));2261out.println(rb.getString2262("STAR"));2263out.println(rb.getString2264("STARNN"));2265}2266}2267}22682269private static <T> Iterable<T> e2i(final Enumeration<T> e) {2270return new Iterable<T>() {2271@Override2272public Iterator<T> iterator() {2273return new Iterator<T>() {2274@Override2275public boolean hasNext() {2276return e.hasMoreElements();2277}2278@Override2279public T next() {2280return e.nextElement();2281}2282public void remove() {2283throw new UnsupportedOperationException("Not supported yet.");2284}2285};2286}2287};2288}22892290/**2291* Loads CRLs from a source. This method is also called in JarSigner.2292* @param src the source, which means System.in if null, or a URI,2293* or a bare file path name2294*/2295public static Collection<? extends CRL> loadCRLs(String src) throws Exception {2296InputStream in = null;2297URI uri = null;2298if (src == null) {2299in = System.in;2300} else {2301try {2302uri = new URI(src);2303if (uri.getScheme().equals("ldap")) {2304// No input stream for LDAP2305} else {2306in = uri.toURL().openStream();2307}2308} catch (Exception e) {2309try {2310in = new FileInputStream(src);2311} catch (Exception e2) {2312if (uri == null || uri.getScheme() == null) {2313throw e2; // More likely a bare file path2314} else {2315throw e; // More likely a protocol or network problem2316}2317}2318}2319}2320if (in != null) {2321try {2322// Read the full stream before feeding to X509Factory,2323// otherwise, keytool -gencrl | keytool -printcrl2324// might not work properly, since -gencrl is slow2325// and there's no data in the pipe at the beginning.2326ByteArrayOutputStream bout = new ByteArrayOutputStream();2327byte[] b = new byte[4096];2328while (true) {2329int len = in.read(b);2330if (len < 0) break;2331bout.write(b, 0, len);2332}2333return CertificateFactory.getInstance("X509").generateCRLs(2334new ByteArrayInputStream(bout.toByteArray()));2335} finally {2336if (in != System.in) {2337in.close();2338}2339}2340} else { // must be LDAP, and uri is not null2341// Lazily load LDAPCertStoreHelper if present2342CertStoreHelper helper = CertStoreHelper.getInstance("LDAP");2343String path = uri.getPath();2344if (path.charAt(0) == '/') path = path.substring(1);2345CertStore s = helper.getCertStore(uri);2346X509CRLSelector sel =2347helper.wrap(new X509CRLSelector(), null, path);2348return s.getCRLs(sel);2349}2350}23512352/**2353* Returns CRLs described in a X509Certificate's CRLDistributionPoints2354* Extension. Only those containing a general name of type URI are read.2355*/2356public static List<CRL> readCRLsFromCert(X509Certificate cert)2357throws Exception {2358List<CRL> crls = new ArrayList<>();2359CRLDistributionPointsExtension ext =2360X509CertImpl.toImpl(cert).getCRLDistributionPointsExtension();2361if (ext == null) return crls;2362List<DistributionPoint> distPoints =2363ext.get(CRLDistributionPointsExtension.POINTS);2364for (DistributionPoint o: distPoints) {2365GeneralNames names = o.getFullName();2366if (names != null) {2367for (GeneralName name: names.names()) {2368if (name.getType() == GeneralNameInterface.NAME_URI) {2369URIName uriName = (URIName)name.getName();2370for (CRL crl: loadCRLs(uriName.getName())) {2371if (crl instanceof X509CRL) {2372crls.add((X509CRL)crl);2373}2374}2375break; // Different name should point to same CRL2376}2377}2378}2379}2380return crls;2381}23822383private static String verifyCRL(KeyStore ks, CRL crl)2384throws Exception {2385X509CRLImpl xcrl = (X509CRLImpl)crl;2386X500Principal issuer = xcrl.getIssuerX500Principal();2387for (String s: e2i(ks.aliases())) {2388Certificate cert = ks.getCertificate(s);2389if (cert instanceof X509Certificate) {2390X509Certificate xcert = (X509Certificate)cert;2391if (xcert.getSubjectX500Principal().equals(issuer)) {2392try {2393((X509CRLImpl)crl).verify(cert.getPublicKey());2394return s;2395} catch (Exception e) {2396}2397}2398}2399}2400return null;2401}24022403private void doPrintCRL(String src, PrintStream out)2404throws Exception {2405for (CRL crl: loadCRLs(src)) {2406printCRL(crl, out);2407String issuer = null;2408Certificate signer = null;2409if (caks != null) {2410issuer = verifyCRL(caks, crl);2411if (issuer != null) {2412signer = caks.getCertificate(issuer);2413out.printf(rb.getString(2414"verified.by.s.in.s.weak"),2415issuer,2416"cacerts",2417withWeak(signer.getPublicKey()));2418out.println();2419}2420}2421if (issuer == null && keyStore != null) {2422issuer = verifyCRL(keyStore, crl);2423if (issuer != null) {2424signer = keyStore.getCertificate(issuer);2425out.printf(rb.getString(2426"verified.by.s.in.s.weak"),2427issuer,2428"keystore",2429withWeak(signer.getPublicKey()));2430out.println();2431}2432}2433if (issuer == null) {2434out.println(rb.getString2435("STAR"));2436out.println(rb.getString2437("warning.not.verified.make.sure.keystore.is.correct"));2438out.println(rb.getString2439("STARNN"));2440}2441checkWeak(rb.getString("the.crl"), crl, signer == null ? null : signer.getPublicKey());2442}2443}24442445private void printCRL(CRL crl, PrintStream out)2446throws Exception {2447X509CRL xcrl = (X509CRL)crl;2448if (rfc) {2449out.println("-----BEGIN X509 CRL-----");2450out.println(Base64.getMimeEncoder(64, CRLF).encodeToString(xcrl.getEncoded()));2451out.println("-----END X509 CRL-----");2452} else {2453String s;2454if (crl instanceof X509CRLImpl) {2455X509CRLImpl x509crl = (X509CRLImpl) crl;2456s = x509crl.toStringWithAlgName(withWeak("" + x509crl.getSigAlgId()));2457} else {2458s = crl.toString();2459}2460out.println(s);2461}2462}24632464private void doPrintCertReq(InputStream in, PrintStream out)2465throws Exception {24662467BufferedReader reader = new BufferedReader(new InputStreamReader(in));2468StringBuffer sb = new StringBuffer();2469boolean started = false;2470while (true) {2471String s = reader.readLine();2472if (s == null) break;2473if (!started) {2474if (s.startsWith("-----")) {2475started = true;2476}2477} else {2478if (s.startsWith("-----")) {2479break;2480}2481sb.append(s);2482}2483}2484PKCS10 req = new PKCS10(Pem.decode(new String(sb)));24852486PublicKey pkey = req.getSubjectPublicKeyInfo();2487out.printf(rb.getString("PKCS.10.with.weak"),2488req.getSubjectName(),2489pkey.getFormat(),2490withWeak(pkey),2491withWeak(req.getSigAlg()));2492for (PKCS10Attribute attr: req.getAttributes().getAttributes()) {2493ObjectIdentifier oid = attr.getAttributeId();2494if (oid.equals((Object)PKCS9Attribute.EXTENSION_REQUEST_OID)) {2495CertificateExtensions exts = (CertificateExtensions)attr.getAttributeValue();2496if (exts != null) {2497printExtensions(rb.getString("Extension.Request."), exts, out);2498}2499} else {2500out.println("Attribute: " + attr.getAttributeId());2501PKCS9Attribute pkcs9Attr =2502new PKCS9Attribute(attr.getAttributeId(),2503attr.getAttributeValue());2504out.print(pkcs9Attr.getName() + ": ");2505Object attrVal = attr.getAttributeValue();2506out.println(attrVal instanceof String[] ?2507Arrays.toString((String[]) attrVal) :2508attrVal);2509}2510}2511if (debug) {2512out.println(req); // Just to see more, say, public key length...2513}2514checkWeak(rb.getString("the.certificate.request"), req);2515}25162517/**2518* Reads a certificate (or certificate chain) and prints its contents in2519* a human readable format.2520*/2521private void printCertFromStream(InputStream in, PrintStream out)2522throws Exception2523{2524Collection<? extends Certificate> c = null;2525try {2526c = cf.generateCertificates(in);2527} catch (CertificateException ce) {2528throw new Exception(rb.getString("Failed.to.parse.input"), ce);2529}2530if (c.isEmpty()) {2531throw new Exception(rb.getString("Empty.input"));2532}2533Certificate[] certs = c.toArray(new Certificate[c.size()]);2534for (int i=0; i<certs.length; i++) {2535X509Certificate x509Cert = null;2536try {2537x509Cert = (X509Certificate)certs[i];2538} catch (ClassCastException cce) {2539throw new Exception(rb.getString("Not.X.509.certificate"));2540}2541if (certs.length > 1) {2542MessageFormat form = new MessageFormat2543(rb.getString("Certificate.i.1."));2544Object[] source = {new Integer(i + 1)};2545out.println(form.format(source));2546}2547if (rfc)2548dumpCert(x509Cert, out);2549else2550printX509Cert(x509Cert, out);2551if (i < (certs.length-1)) {2552out.println();2553}2554checkWeak(oneInMany(rb.getString("the.certificate"), i, certs.length), x509Cert);2555}2556}25572558private static String oneInMany(String label, int i, int num) {2559if (num == 1) {2560return label;2561} else {2562return String.format(rb.getString("one.in.many"), label, i+1, num);2563}2564}25652566private void doPrintCert(final PrintStream out) throws Exception {2567if (jarfile != null) {2568JarFile jf = new JarFile(jarfile, true);2569Enumeration<JarEntry> entries = jf.entries();2570Set<CodeSigner> ss = new HashSet<>();2571byte[] buffer = new byte[8192];2572int pos = 0;2573while (entries.hasMoreElements()) {2574JarEntry je = entries.nextElement();2575try (InputStream is = jf.getInputStream(je)) {2576while (is.read(buffer) != -1) {2577// we just read. this will throw a SecurityException2578// if a signature/digest check fails. This also2579// populate the signers2580}2581}2582CodeSigner[] signers = je.getCodeSigners();2583if (signers != null) {2584for (CodeSigner signer: signers) {2585if (!ss.contains(signer)) {2586ss.add(signer);2587out.printf(rb.getString("Signer.d."), ++pos);2588out.println();2589out.println();2590out.println(rb.getString("Signature."));2591out.println();25922593List<? extends Certificate> certs2594= signer.getSignerCertPath().getCertificates();2595int cc = 0;2596for (Certificate cert: certs) {2597X509Certificate x = (X509Certificate)cert;2598if (rfc) {2599out.println(rb.getString("Certificate.owner.") + x.getSubjectDN() + "\n");2600dumpCert(x, out);2601} else {2602printX509Cert(x, out);2603}2604out.println();2605checkWeak(oneInMany(rb.getString("the.certificate"), cc++, certs.size()), x);2606}2607Timestamp ts = signer.getTimestamp();2608if (ts != null) {2609out.println(rb.getString("Timestamp."));2610out.println();2611certs = ts.getSignerCertPath().getCertificates();2612cc = 0;2613for (Certificate cert: certs) {2614X509Certificate x = (X509Certificate)cert;2615if (rfc) {2616out.println(rb.getString("Certificate.owner.") + x.getSubjectDN() + "\n");2617dumpCert(x, out);2618} else {2619printX509Cert(x, out);2620}2621out.println();2622checkWeak(oneInMany(rb.getString("the.tsa.certificate"), cc++, certs.size()), x);2623}2624}2625}2626}2627}2628}2629jf.close();2630if (ss.isEmpty()) {2631out.println(rb.getString("Not.a.signed.jar.file"));2632}2633} else if (sslserver != null) {2634// Lazily load SSLCertStoreHelper if present2635CertStoreHelper helper = CertStoreHelper.getInstance("SSLServer");2636CertStore cs = helper.getCertStore(new URI("https://" + sslserver));2637Collection<? extends Certificate> chain;2638try {2639chain = cs.getCertificates(null);2640if (chain.isEmpty()) {2641// If the certs are not retrieved, we consider it an error2642// even if the URL connection is successful.2643throw new Exception(rb.getString(2644"No.certificate.from.the.SSL.server"));2645}2646} catch (CertStoreException cse) {2647if (cse.getCause() instanceof IOException) {2648throw new Exception(rb.getString(2649"No.certificate.from.the.SSL.server"),2650cse.getCause());2651} else {2652throw cse;2653}2654}26552656int i = 0;2657for (Certificate cert : chain) {2658try {2659if (rfc) {2660dumpCert(cert, out);2661} else {2662out.println("Certificate #" + i);2663out.println("====================================");2664printX509Cert((X509Certificate)cert, out);2665out.println();2666}2667checkWeak(oneInMany(rb.getString("the.certificate"), i++, chain.size()), cert);2668} catch (Exception e) {2669if (debug) {2670e.printStackTrace();2671}2672}2673}2674} else {2675if (filename != null) {2676try (FileInputStream inStream = new FileInputStream(filename)) {2677printCertFromStream(inStream, out);2678}2679} else {2680printCertFromStream(System.in, out);2681}2682}2683}2684/**2685* Creates a self-signed certificate, and stores it as a single-element2686* certificate chain.2687*/2688private void doSelfCert(String alias, String dname, String sigAlgName)2689throws Exception2690{2691if (alias == null) {2692alias = keyAlias;2693}26942695Pair<Key,char[]> objs = recoverKey(alias, storePass, keyPass);2696PrivateKey privKey = (PrivateKey)objs.fst;2697if (keyPass == null)2698keyPass = objs.snd;26992700// Determine the signature algorithm2701if (sigAlgName == null) {2702sigAlgName = getCompatibleSigAlgName(privKey.getAlgorithm());2703}27042705// Get the old certificate2706Certificate oldCert = keyStore.getCertificate(alias);2707if (oldCert == null) {2708MessageFormat form = new MessageFormat2709(rb.getString("alias.has.no.public.key"));2710Object[] source = {alias};2711throw new Exception(form.format(source));2712}2713if (!(oldCert instanceof X509Certificate)) {2714MessageFormat form = new MessageFormat2715(rb.getString("alias.has.no.X.509.certificate"));2716Object[] source = {alias};2717throw new Exception(form.format(source));2718}27192720// convert to X509CertImpl, so that we can modify selected fields2721// (no public APIs available yet)2722byte[] encoded = oldCert.getEncoded();2723X509CertImpl certImpl = new X509CertImpl(encoded);2724X509CertInfo certInfo = (X509CertInfo)certImpl.get(X509CertImpl.NAME2725+ "." +2726X509CertImpl.INFO);27272728// Extend its validity2729Date firstDate = getStartDate(startDate);2730Date lastDate = new Date();2731lastDate.setTime(firstDate.getTime() + validity*1000L*24L*60L*60L);2732CertificateValidity interval = new CertificateValidity(firstDate,2733lastDate);2734certInfo.set(X509CertInfo.VALIDITY, interval);27352736// Make new serial number2737certInfo.set(X509CertInfo.SERIAL_NUMBER, new CertificateSerialNumber(2738new java.util.Random().nextInt() & 0x7fffffff));27392740// Set owner and issuer fields2741X500Name owner;2742if (dname == null) {2743// Get the owner name from the certificate2744owner = (X500Name)certInfo.get(X509CertInfo.SUBJECT + "." +2745X509CertInfo.DN_NAME);2746} else {2747// Use the owner name specified at the command line2748owner = new X500Name(dname);2749certInfo.set(X509CertInfo.SUBJECT + "." +2750X509CertInfo.DN_NAME, owner);2751}2752// Make issuer same as owner (self-signed!)2753certInfo.set(X509CertInfo.ISSUER + "." +2754X509CertInfo.DN_NAME, owner);27552756// The inner and outer signature algorithms have to match.2757// The way we achieve that is really ugly, but there seems to be no2758// other solution: We first sign the cert, then retrieve the2759// outer sigalg and use it to set the inner sigalg2760X509CertImpl newCert = new X509CertImpl(certInfo);2761AlgorithmParameterSpec params = AlgorithmId2762.getDefaultAlgorithmParameterSpec(sigAlgName, privKey);2763newCert.sign(privKey, params, sigAlgName, null);2764AlgorithmId sigAlgid = (AlgorithmId)newCert.get(X509CertImpl.SIG_ALG);2765certInfo.set(CertificateAlgorithmId.NAME + "." +2766CertificateAlgorithmId.ALGORITHM, sigAlgid);27672768certInfo.set(X509CertInfo.VERSION,2769new CertificateVersion(CertificateVersion.V3));27702771CertificateExtensions ext = createV3Extensions(2772null,2773(CertificateExtensions)certInfo.get(X509CertInfo.EXTENSIONS),2774v3ext,2775oldCert.getPublicKey(),2776null);2777certInfo.set(X509CertInfo.EXTENSIONS, ext);2778// Sign the new certificate2779newCert = new X509CertImpl(certInfo);2780newCert.sign(privKey, params, sigAlgName, null);27812782// Store the new certificate as a single-element certificate chain2783keyStore.setKeyEntry(alias, privKey,2784(keyPass != null) ? keyPass : storePass,2785new Certificate[] { newCert } );27862787if (verbose) {2788System.err.println(rb.getString("New.certificate.self.signed."));2789System.err.print(newCert.toString());2790System.err.println();2791}2792}27932794/**2795* Processes a certificate reply from a certificate authority.2796*2797* <p>Builds a certificate chain on top of the certificate reply,2798* using trusted certificates from the keystore. The chain is complete2799* after a self-signed certificate has been encountered. The self-signed2800* certificate is considered a root certificate authority, and is stored2801* at the end of the chain.2802*2803* <p>The newly generated chain replaces the old chain associated with the2804* key entry.2805*2806* @return true if the certificate reply was installed, otherwise false.2807*/2808private boolean installReply(String alias, InputStream in)2809throws Exception2810{2811if (alias == null) {2812alias = keyAlias;2813}28142815Pair<Key,char[]> objs = recoverKey(alias, storePass, keyPass);2816PrivateKey privKey = (PrivateKey)objs.fst;2817if (keyPass == null) {2818keyPass = objs.snd;2819}28202821Certificate userCert = keyStore.getCertificate(alias);2822if (userCert == null) {2823MessageFormat form = new MessageFormat2824(rb.getString("alias.has.no.public.key.certificate."));2825Object[] source = {alias};2826throw new Exception(form.format(source));2827}28282829// Read the certificates in the reply2830Collection<? extends Certificate> c = cf.generateCertificates(in);2831if (c.isEmpty()) {2832throw new Exception(rb.getString("Reply.has.no.certificates"));2833}2834Certificate[] replyCerts = c.toArray(new Certificate[c.size()]);2835Certificate[] newChain;2836if (replyCerts.length == 1) {2837// single-cert reply2838newChain = establishCertChain(userCert, replyCerts[0]);2839} else {2840// cert-chain reply (e.g., PKCS#7)2841newChain = validateReply(alias, userCert, replyCerts);2842}28432844// Now store the newly established chain in the keystore. The new2845// chain replaces the old one. The chain can be null if user chooses no.2846if (newChain != null) {2847keyStore.setKeyEntry(alias, privKey,2848(keyPass != null) ? keyPass : storePass,2849newChain);2850return true;2851} else {2852return false;2853}2854}28552856/**2857* Imports a certificate and adds it to the list of trusted certificates.2858*2859* @return true if the certificate was added, otherwise false.2860*/2861private boolean addTrustedCert(String alias, InputStream in)2862throws Exception2863{2864if (alias == null) {2865throw new Exception(rb.getString("Must.specify.alias"));2866}2867if (keyStore.containsAlias(alias)) {2868MessageFormat form = new MessageFormat(rb.getString2869("Certificate.not.imported.alias.alias.already.exists"));2870Object[] source = {alias};2871throw new Exception(form.format(source));2872}28732874// Read the certificate2875X509Certificate cert = null;2876try {2877cert = (X509Certificate)cf.generateCertificate(in);2878} catch (ClassCastException | CertificateException ce) {2879throw new Exception(rb.getString("Input.not.an.X.509.certificate"));2880}28812882if (noprompt) {2883checkWeak(rb.getString("the.input"), cert);2884keyStore.setCertificateEntry(alias, cert);2885return true;2886}28872888// if certificate is self-signed, make sure it verifies2889boolean selfSigned = false;2890if (KeyStoreUtil.isSelfSigned(cert)) {2891cert.verify(cert.getPublicKey());2892selfSigned = true;2893}28942895// check if cert already exists in keystore2896String reply = null;2897String trustalias = keyStore.getCertificateAlias(cert);2898if (trustalias != null) {2899MessageFormat form = new MessageFormat(rb.getString2900("Certificate.already.exists.in.keystore.under.alias.trustalias."));2901Object[] source = {trustalias};2902System.err.println(form.format(source));2903checkWeak(rb.getString("the.input"), cert);2904printWeakWarnings(true);2905reply = getYesNoReply2906(rb.getString("Do.you.still.want.to.add.it.no."));2907} else if (selfSigned) {2908if (trustcacerts && (caks != null) &&2909((trustalias=caks.getCertificateAlias(cert)) != null)) {2910MessageFormat form = new MessageFormat(rb.getString2911("Certificate.already.exists.in.system.wide.CA.keystore.under.alias.trustalias."));2912Object[] source = {trustalias};2913System.err.println(form.format(source));2914checkWeak(rb.getString("the.input"), cert);2915printWeakWarnings(true);2916reply = getYesNoReply2917(rb.getString("Do.you.still.want.to.add.it.to.your.own.keystore.no."));2918}2919if (trustalias == null) {2920// Print the cert and ask user if they really want to add2921// it to their keystore2922printX509Cert(cert, System.out);2923checkWeak(rb.getString("the.input"), cert);2924printWeakWarnings(true);2925reply = getYesNoReply2926(rb.getString("Trust.this.certificate.no."));2927}2928}2929if (reply != null) {2930if ("YES".equals(reply)) {2931keyStore.setCertificateEntry(alias, cert);2932return true;2933} else {2934return false;2935}2936}29372938// Not found in this keystore and not self-signed2939// Try to establish trust chain2940try {2941Certificate[] chain = establishCertChain(null, cert);2942if (chain != null) {2943keyStore.setCertificateEntry(alias, cert);2944return true;2945}2946} catch (Exception e) {2947// Print the cert and ask user if they really want to add it to2948// their keystore2949printX509Cert(cert, System.out);2950checkWeak(rb.getString("the.input"), cert);2951printWeakWarnings(true);2952reply = getYesNoReply2953(rb.getString("Trust.this.certificate.no."));2954if ("YES".equals(reply)) {2955keyStore.setCertificateEntry(alias, cert);2956return true;2957} else {2958return false;2959}2960}29612962return false;2963}29642965/**2966* Prompts user for new password. New password must be different from2967* old one.2968*2969* @param prompt the message that gets prompted on the screen2970* @param oldPasswd the current (i.e., old) password2971*/2972private char[] getNewPasswd(String prompt, char[] oldPasswd)2973throws Exception2974{2975char[] entered = null;2976char[] reentered = null;29772978for (int count = 0; count < 3; count++) {2979MessageFormat form = new MessageFormat2980(rb.getString("New.prompt."));2981Object[] source = {prompt};2982System.err.print(form.format(source));2983entered = Password.readPassword(System.in);2984passwords.add(entered);2985if (entered == null || entered.length < 6) {2986System.err.println(rb.getString2987("Password.is.too.short.must.be.at.least.6.characters"));2988} else if (Arrays.equals(entered, oldPasswd)) {2989System.err.println(rb.getString("Passwords.must.differ"));2990} else {2991form = new MessageFormat2992(rb.getString("Re.enter.new.prompt."));2993Object[] src = {prompt};2994System.err.print(form.format(src));2995reentered = Password.readPassword(System.in);2996passwords.add(reentered);2997if (!Arrays.equals(entered, reentered)) {2998System.err.println2999(rb.getString("They.don.t.match.Try.again"));3000} else {3001Arrays.fill(reentered, ' ');3002return entered;3003}3004}3005if (entered != null) {3006Arrays.fill(entered, ' ');3007entered = null;3008}3009if (reentered != null) {3010Arrays.fill(reentered, ' ');3011reentered = null;3012}3013}3014throw new Exception(rb.getString("Too.many.failures.try.later"));3015}30163017/**3018* Prompts user for alias name.3019* @param prompt the {0} of "Enter {0} alias name: " in prompt line3020* @returns the string entered by the user, without the \n at the end3021*/3022private String getAlias(String prompt) throws Exception {3023if (prompt != null) {3024MessageFormat form = new MessageFormat3025(rb.getString("Enter.prompt.alias.name."));3026Object[] source = {prompt};3027System.err.print(form.format(source));3028} else {3029System.err.print(rb.getString("Enter.alias.name."));3030}3031return (new BufferedReader(new InputStreamReader(3032System.in))).readLine();3033}30343035/**3036* Prompts user for an input string from the command line (System.in)3037* @prompt the prompt string printed3038* @returns the string entered by the user, without the \n at the end3039*/3040private String inputStringFromStdin(String prompt) throws Exception {3041System.err.print(prompt);3042return (new BufferedReader(new InputStreamReader(3043System.in))).readLine();3044}30453046/**3047* Prompts user for key password. User may select to choose the same3048* password (<code>otherKeyPass</code>) as for <code>otherAlias</code>.3049*/3050private char[] getKeyPasswd(String alias, String otherAlias,3051char[] otherKeyPass)3052throws Exception3053{3054int count = 0;3055char[] keyPass = null;30563057do {3058if (otherKeyPass != null) {3059MessageFormat form = new MessageFormat(rb.getString3060("Enter.key.password.for.alias."));3061Object[] source = {alias};3062System.err.println(form.format(source));30633064form = new MessageFormat(rb.getString3065(".RETURN.if.same.as.for.otherAlias."));3066Object[] src = {otherAlias};3067System.err.print(form.format(src));3068} else {3069MessageFormat form = new MessageFormat(rb.getString3070("Enter.key.password.for.alias."));3071Object[] source = {alias};3072System.err.print(form.format(source));3073}3074System.err.flush();3075keyPass = Password.readPassword(System.in);3076passwords.add(keyPass);3077if (keyPass == null) {3078keyPass = otherKeyPass;3079}3080count++;3081} while ((keyPass == null) && count < 3);30823083if (keyPass == null) {3084throw new Exception(rb.getString("Too.many.failures.try.later"));3085}30863087return keyPass;3088}30893090private String withWeak(String alg) {3091if (DISABLED_CHECK.permits(SIG_PRIMITIVE_SET, alg, null)) {3092if (LEGACY_CHECK.permits(SIG_PRIMITIVE_SET, alg, null)) {3093return alg;3094} else {3095return String.format(rb.getString("with.weak"), alg);3096}3097} else {3098return String.format(rb.getString("with.disabled"), alg);3099}3100}31013102private String fullDisplayAlgName(Key key) {3103String result = key.getAlgorithm();3104if (key instanceof ECKey) {3105ECParameterSpec paramSpec = ((ECKey) key).getParams();3106if (paramSpec instanceof NamedCurve) {3107result += " (" + paramSpec.toString().split(" ")[0] + ")";3108}3109}3110return result;3111}31123113private String withWeak(PublicKey key) {3114int kLen = KeyUtil.getKeySize(key);3115String displayAlg = fullDisplayAlgName(key);3116if (DISABLED_CHECK.permits(SIG_PRIMITIVE_SET, key)) {3117if (LEGACY_CHECK.permits(SIG_PRIMITIVE_SET, key)) {3118if (kLen >= 0) {3119return String.format(rb.getString("key.bit"), kLen, displayAlg);3120} else {3121return String.format(rb.getString("unknown.size.1"), displayAlg);3122}3123} else {3124return String.format(rb.getString("key.bit.weak"), kLen, displayAlg);3125}3126} else {3127return String.format(rb.getString("key.bit.disabled"), kLen, displayAlg);3128}3129}31303131/**3132* Prints a certificate in a human readable format.3133*/3134private void printX509Cert(X509Certificate cert, PrintStream out)3135throws Exception3136{31373138MessageFormat form = new MessageFormat3139(rb.getString(".PATTERN.printX509Cert.with.weak"));3140PublicKey pkey = cert.getPublicKey();3141String sigName = cert.getSigAlgName();3142// No need to warn about sigalg of a trust anchor3143if (!isTrustedCert(cert)) {3144sigName = withWeak(sigName);3145}3146Object[] source = {cert.getSubjectDN().toString(),3147cert.getIssuerDN().toString(),3148cert.getSerialNumber().toString(16),3149cert.getNotBefore().toString(),3150cert.getNotAfter().toString(),3151getCertFingerPrint("SHA-1", cert),3152getCertFingerPrint("SHA-256", cert),3153sigName,3154withWeak(pkey),3155cert.getVersion()3156};3157out.println(form.format(source));31583159if (cert instanceof X509CertImpl) {3160X509CertImpl impl = (X509CertImpl)cert;3161X509CertInfo certInfo = (X509CertInfo)impl.get(X509CertImpl.NAME3162+ "." +3163X509CertImpl.INFO);3164CertificateExtensions exts = (CertificateExtensions)3165certInfo.get(X509CertInfo.EXTENSIONS);3166if (exts != null) {3167printExtensions(rb.getString("Extensions."), exts, out);3168}3169}3170}31713172private static void printExtensions(String title, CertificateExtensions exts, PrintStream out)3173throws Exception {3174int extnum = 0;3175Iterator<Extension> i1 = exts.getAllExtensions().iterator();3176Iterator<Extension> i2 = exts.getUnparseableExtensions().values().iterator();3177while (i1.hasNext() || i2.hasNext()) {3178Extension ext = i1.hasNext()?i1.next():i2.next();3179if (extnum == 0) {3180out.println();3181out.println(title);3182out.println();3183}3184out.print("#"+(++extnum)+": "+ ext);3185if (ext.getClass() == Extension.class) {3186byte[] v = ext.getExtensionValue();3187if (v.length == 0) {3188out.println(rb.getString(".Empty.value."));3189} else {3190new sun.misc.HexDumpEncoder().encodeBuffer(ext.getExtensionValue(), out);3191out.println();3192}3193}3194out.println();3195}3196}31973198/**3199* Locates a signer for a given certificate from a given keystore and3200* returns the signer's certificate.3201* @param cert the certificate whose signer is searched, not null3202* @param ks the keystore to search with, not null3203* @return <code>cert</code> itself if it's already inside <code>ks</code>,3204* or a certificate inside <code>ks</code> who signs <code>cert</code>,3205* or null otherwise. A label is added.3206*/3207private static Pair<String,Certificate>3208getSigner(Certificate cert, KeyStore ks) throws Exception {3209if (ks.getCertificateAlias(cert) != null) {3210return new Pair<>("", cert);3211}3212for (Enumeration<String> aliases = ks.aliases();3213aliases.hasMoreElements(); ) {3214String name = aliases.nextElement();3215Certificate trustedCert = ks.getCertificate(name);3216if (trustedCert != null) {3217try {3218cert.verify(trustedCert.getPublicKey());3219return new Pair<>(name, trustedCert);3220} catch (Exception e) {3221// Not verified, skip to the next one3222}3223}3224}3225return null;3226}32273228/**3229* Gets an X.500 name suitable for inclusion in a certification request.3230*/3231private X500Name getX500Name() throws IOException {3232BufferedReader in;3233in = new BufferedReader(new InputStreamReader(System.in));3234String commonName = "Unknown";3235String organizationalUnit = "Unknown";3236String organization = "Unknown";3237String city = "Unknown";3238String state = "Unknown";3239String country = "Unknown";3240X500Name name;3241String userInput = null;32423243int maxRetry = 20;3244do {3245if (maxRetry-- < 0) {3246throw new RuntimeException(rb.getString(3247"Too.many.retries.program.terminated"));3248}3249commonName = inputString(in,3250rb.getString("What.is.your.first.and.last.name."),3251commonName);3252organizationalUnit = inputString(in,3253rb.getString3254("What.is.the.name.of.your.organizational.unit."),3255organizationalUnit);3256organization = inputString(in,3257rb.getString("What.is.the.name.of.your.organization."),3258organization);3259city = inputString(in,3260rb.getString("What.is.the.name.of.your.City.or.Locality."),3261city);3262state = inputString(in,3263rb.getString("What.is.the.name.of.your.State.or.Province."),3264state);3265country = inputString(in,3266rb.getString3267("What.is.the.two.letter.country.code.for.this.unit."),3268country);3269name = new X500Name(commonName, organizationalUnit, organization,3270city, state, country);3271MessageFormat form = new MessageFormat3272(rb.getString("Is.name.correct."));3273Object[] source = {name};3274userInput = inputString3275(in, form.format(source), rb.getString("no"));3276} while (collator.compare(userInput, rb.getString("yes")) != 0 &&3277collator.compare(userInput, rb.getString("y")) != 0);32783279System.err.println();3280return name;3281}32823283private String inputString(BufferedReader in, String prompt,3284String defaultValue)3285throws IOException3286{3287System.err.println(prompt);3288MessageFormat form = new MessageFormat3289(rb.getString(".defaultValue."));3290Object[] source = {defaultValue};3291System.err.print(form.format(source));3292System.err.flush();32933294String value = in.readLine();3295if (value == null || collator.compare(value, "") == 0) {3296value = defaultValue;3297}3298return value;3299}33003301/**3302* Writes an X.509 certificate in base64 or binary encoding to an output3303* stream.3304*/3305private void dumpCert(Certificate cert, PrintStream out)3306throws IOException, CertificateException3307{3308if (rfc) {3309out.println(X509Factory.BEGIN_CERT);3310out.println(Base64.getMimeEncoder(64, CRLF).encodeToString(cert.getEncoded()));3311out.println(X509Factory.END_CERT);3312} else {3313out.write(cert.getEncoded()); // binary3314}3315}33163317/**3318* Converts a byte to hex digit and writes to the supplied buffer3319*/3320private void byte2hex(byte b, StringBuffer buf) {3321char[] hexChars = { '0', '1', '2', '3', '4', '5', '6', '7', '8',3322'9', 'A', 'B', 'C', 'D', 'E', 'F' };3323int high = ((b & 0xf0) >> 4);3324int low = (b & 0x0f);3325buf.append(hexChars[high]);3326buf.append(hexChars[low]);3327}33283329/**3330* Converts a byte array to hex string3331*/3332private String toHexString(byte[] block) {3333StringBuffer buf = new StringBuffer();3334int len = block.length;3335for (int i = 0; i < len; i++) {3336byte2hex(block[i], buf);3337if (i < len-1) {3338buf.append(":");3339}3340}3341return buf.toString();3342}33433344/**3345* Recovers (private) key associated with given alias.3346*3347* @return an array of objects, where the 1st element in the array is the3348* recovered private key, and the 2nd element is the password used to3349* recover it.3350*/3351private Pair<Key,char[]> recoverKey(String alias, char[] storePass,3352char[] keyPass)3353throws Exception3354{3355Key key = null;33563357if (KeyStoreUtil.isWindowsKeyStore(storetype)) {3358key = keyStore.getKey(alias, null);3359return Pair.of(key, null);3360}33613362if (keyStore.containsAlias(alias) == false) {3363MessageFormat form = new MessageFormat3364(rb.getString("Alias.alias.does.not.exist"));3365Object[] source = {alias};3366throw new Exception(form.format(source));3367}3368if (!keyStore.entryInstanceOf(alias, KeyStore.PrivateKeyEntry.class) &&3369!keyStore.entryInstanceOf(alias, KeyStore.SecretKeyEntry.class)) {3370MessageFormat form = new MessageFormat3371(rb.getString("Alias.alias.has.no.key"));3372Object[] source = {alias};3373throw new Exception(form.format(source));3374}33753376if (keyPass == null) {3377// Try to recover the key using the keystore password3378try {3379key = keyStore.getKey(alias, storePass);33803381keyPass = storePass;3382passwords.add(keyPass);3383} catch (UnrecoverableKeyException e) {3384// Did not work out, so prompt user for key password3385if (!token) {3386keyPass = getKeyPasswd(alias, null, null);3387key = keyStore.getKey(alias, keyPass);3388} else {3389throw e;3390}3391}3392} else {3393key = keyStore.getKey(alias, keyPass);3394}33953396return Pair.of(key, keyPass);3397}33983399/**3400* Recovers entry associated with given alias.3401*3402* @return an array of objects, where the 1st element in the array is the3403* recovered entry, and the 2nd element is the password used to3404* recover it (null if no password).3405*/3406private Pair<Entry,char[]> recoverEntry(KeyStore ks,3407String alias,3408char[] pstore,3409char[] pkey) throws Exception {34103411if (ks.containsAlias(alias) == false) {3412MessageFormat form = new MessageFormat3413(rb.getString("Alias.alias.does.not.exist"));3414Object[] source = {alias};3415throw new Exception(form.format(source));3416}34173418PasswordProtection pp = null;3419Entry entry;34203421try {3422// First attempt to access entry without key password3423// (PKCS11 entry or trusted certificate entry, for example)34243425entry = ks.getEntry(alias, pp);3426pkey = null;3427} catch (UnrecoverableEntryException une) {34283429if(P11KEYSTORE.equalsIgnoreCase(ks.getType()) ||3430KeyStoreUtil.isWindowsKeyStore(ks.getType())) {3431// should not happen, but a possibility3432throw une;3433}34343435// entry is protected34363437if (pkey != null) {34383439// try provided key password34403441pp = new PasswordProtection(pkey);3442entry = ks.getEntry(alias, pp);34433444} else {34453446// try store pass34473448try {3449pp = new PasswordProtection(pstore);3450entry = ks.getEntry(alias, pp);3451pkey = pstore;3452} catch (UnrecoverableEntryException une2) {3453if (P12KEYSTORE.equalsIgnoreCase(ks.getType())) {34543455// P12 keystore currently does not support separate3456// store and entry passwords34573458throw une2;3459} else {34603461// prompt for entry password34623463pkey = getKeyPasswd(alias, null, null);3464pp = new PasswordProtection(pkey);3465entry = ks.getEntry(alias, pp);3466}3467}3468}3469}34703471return Pair.of(entry, pkey);3472}3473/**3474* Gets the requested finger print of the certificate.3475*/3476private String getCertFingerPrint(String mdAlg, Certificate cert)3477throws Exception3478{3479byte[] encCertInfo = cert.getEncoded();3480MessageDigest md = MessageDigest.getInstance(mdAlg);3481byte[] digest = md.digest(encCertInfo);3482return toHexString(digest);3483}34843485/**3486* Prints warning about missing integrity check.3487*/3488private void printNoIntegrityWarning() {3489System.err.println();3490System.err.println(rb.getString3491(".WARNING.WARNING.WARNING."));3492System.err.println(rb.getString3493(".The.integrity.of.the.information.stored.in.your.keystore."));3494System.err.println(rb.getString3495(".WARNING.WARNING.WARNING."));3496System.err.println();3497}34983499/**3500* Validates chain in certification reply, and returns the ordered3501* elements of the chain (with user certificate first, and root3502* certificate last in the array).3503*3504* @param alias the alias name3505* @param userCert the user certificate of the alias3506* @param replyCerts the chain provided in the reply3507*/3508private Certificate[] validateReply(String alias,3509Certificate userCert,3510Certificate[] replyCerts)3511throws Exception3512{35133514checkWeak(rb.getString("reply"), replyCerts);35153516// order the certs in the reply (bottom-up).3517// we know that all certs in the reply are of type X.509, because3518// we parsed them using an X.509 certificate factory3519int i;3520PublicKey userPubKey = userCert.getPublicKey();3521for (i=0; i<replyCerts.length; i++) {3522if (userPubKey.equals(replyCerts[i].getPublicKey())) {3523break;3524}3525}3526if (i == replyCerts.length) {3527MessageFormat form = new MessageFormat(rb.getString3528("Certificate.reply.does.not.contain.public.key.for.alias."));3529Object[] source = {alias};3530throw new Exception(form.format(source));3531}35323533Certificate tmpCert = replyCerts[0];3534replyCerts[0] = replyCerts[i];3535replyCerts[i] = tmpCert;35363537X509Certificate thisCert = (X509Certificate)replyCerts[0];35383539for (i=1; i < replyCerts.length-1; i++) {3540// find a cert in the reply who signs thisCert3541int j;3542for (j=i; j<replyCerts.length; j++) {3543if (KeyStoreUtil.signedBy(thisCert, (X509Certificate)replyCerts[j])) {3544tmpCert = replyCerts[i];3545replyCerts[i] = replyCerts[j];3546replyCerts[j] = tmpCert;3547thisCert = (X509Certificate)replyCerts[i];3548break;3549}3550}3551if (j == replyCerts.length) {3552throw new Exception3553(rb.getString("Incomplete.certificate.chain.in.reply"));3554}3555}35563557if (noprompt) {3558return replyCerts;3559}35603561// do we trust the cert at the top?3562Certificate topCert = replyCerts[replyCerts.length-1];3563boolean fromKeyStore = true;3564Pair<String,Certificate> root = getSigner(topCert, keyStore);3565if (root == null && trustcacerts && caks != null) {3566root = getSigner(topCert, caks);3567fromKeyStore = false;3568}3569if (root == null) {3570System.err.println();3571System.err.println3572(rb.getString("Top.level.certificate.in.reply."));3573printX509Cert((X509Certificate)topCert, System.out);3574System.err.println();3575System.err.print(rb.getString(".is.not.trusted."));3576printWeakWarnings(true);3577String reply = getYesNoReply3578(rb.getString("Install.reply.anyway.no."));3579if ("NO".equals(reply)) {3580return null;3581}3582} else {3583if (root.snd != topCert) {3584// append the root CA cert to the chain3585Certificate[] tmpCerts =3586new Certificate[replyCerts.length+1];3587System.arraycopy(replyCerts, 0, tmpCerts, 0,3588replyCerts.length);3589tmpCerts[tmpCerts.length-1] = root.snd;3590replyCerts = tmpCerts;3591checkWeak(String.format(rb.getString(fromKeyStore ?3592"alias.in.keystore" :3593"alias.in.cacerts"),3594root.fst),3595root.snd);3596}3597}3598return replyCerts;3599}36003601/**3602* Establishes a certificate chain (using trusted certificates in the3603* keystore and cacerts), starting with the reply (certToVerify)3604* and ending at a self-signed certificate found in the keystore.3605*3606* @param userCert optional existing certificate, mostly likely be the3607* original self-signed cert created by -genkeypair.3608* It must have the same public key as certToVerify3609* but cannot be the same cert.3610* @param certToVerify the starting certificate to build the chain3611* @returns the established chain, might be null if user decides not3612*/3613private Certificate[] establishCertChain(Certificate userCert,3614Certificate certToVerify)3615throws Exception3616{3617if (userCert != null) {3618// Make sure that the public key of the certificate reply matches3619// the original public key in the keystore3620PublicKey origPubKey = userCert.getPublicKey();3621PublicKey replyPubKey = certToVerify.getPublicKey();3622if (!origPubKey.equals(replyPubKey)) {3623throw new Exception(rb.getString3624("Public.keys.in.reply.and.keystore.don.t.match"));3625}36263627// If the two certs are identical, we're done: no need to import3628// anything3629if (certToVerify.equals(userCert)) {3630throw new Exception(rb.getString3631("Certificate.reply.and.certificate.in.keystore.are.identical"));3632}3633}36343635// Build a hash table of all certificates in the keystore.3636// Use the subject distinguished name as the key into the hash table.3637// All certificates associated with the same subject distinguished3638// name are stored in the same hash table entry as a vector.3639Hashtable<Principal, Vector<Pair<String,X509Certificate>>> certs = null;3640if (keyStore.size() > 0) {3641certs = new Hashtable<>(11);3642keystorecerts2Hashtable(keyStore, certs);3643}3644if (trustcacerts) {3645if (caks!=null && caks.size()>0) {3646if (certs == null) {3647certs = new Hashtable<>(11);3648}3649keystorecerts2Hashtable(caks, certs);3650}3651}36523653// start building chain3654Vector<Pair<String,X509Certificate>> chain = new Vector<>(2);3655if (buildChain(3656new Pair<>(rb.getString("the.input"),3657(X509Certificate) certToVerify),3658chain, certs)) {3659for (Pair<String,X509Certificate> p : chain) {3660checkWeak(p.fst, p.snd);3661}3662Certificate[] newChain =3663new Certificate[chain.size()];3664// buildChain() returns chain with self-signed root-cert first and3665// user-cert last, so we need to invert the chain before we store3666// it3667int j=0;3668for (int i=chain.size()-1; i>=0; i--) {3669newChain[j] = chain.elementAt(i).snd;3670j++;3671}3672return newChain;3673} else {3674throw new Exception3675(rb.getString("Failed.to.establish.chain.from.reply"));3676}3677}36783679/**3680* Recursively tries to establish chain from pool of certs starting from3681* certToVerify until a self-signed cert is found, and fill the certs found3682* into chain. Each cert in the chain signs the next one.3683*3684* This method is able to recover from an error, say, if certToVerify3685* is signed by certA but certA has no issuer in certs and itself is not3686* self-signed, the method can try another certB that also signs3687* certToVerify and look for signer of certB, etc, etc.3688*3689* Each cert in chain comes with a label showing its origin. The label is3690* used in the warning message when the cert is considered a risk.3691*3692* @param certToVerify the cert that needs to be verified.3693* @param chain the chain that's being built.3694* @param certs the pool of trusted certs3695*3696* @return true if successful, false otherwise.3697*/3698private boolean buildChain(Pair<String,X509Certificate> certToVerify,3699Vector<Pair<String,X509Certificate>> chain,3700Hashtable<Principal, Vector<Pair<String,X509Certificate>>> certs) {3701if (KeyStoreUtil.isSelfSigned(certToVerify.snd)) {3702// reached self-signed root cert;3703// no verification needed because it's trusted.3704chain.addElement(certToVerify);3705return true;3706}37073708Principal issuer = certToVerify.snd.getIssuerDN();37093710// Get the issuer's certificate(s)3711Vector<Pair<String,X509Certificate>> vec = certs.get(issuer);3712if (vec == null) {3713return false;3714}37153716// Try out each certificate in the vector, until we find one3717// whose public key verifies the signature of the certificate3718// in question.3719for (Enumeration<Pair<String,X509Certificate>> issuerCerts = vec.elements();3720issuerCerts.hasMoreElements(); ) {3721Pair<String,X509Certificate> issuerCert = issuerCerts.nextElement();3722PublicKey issuerPubKey = issuerCert.snd.getPublicKey();3723try {3724certToVerify.snd.verify(issuerPubKey);3725} catch (Exception e) {3726continue;3727}3728if (buildChain(issuerCert, chain, certs)) {3729chain.addElement(certToVerify);3730return true;3731}3732}3733return false;3734}37353736/**3737* Prompts user for yes/no decision.3738*3739* @return the user's decision, can only be "YES" or "NO"3740*/3741private String getYesNoReply(String prompt)3742throws IOException3743{3744String reply = null;3745int maxRetry = 20;3746do {3747if (maxRetry-- < 0) {3748throw new RuntimeException(rb.getString(3749"Too.many.retries.program.terminated"));3750}3751System.err.print(prompt);3752System.err.flush();3753reply = (new BufferedReader(new InputStreamReader3754(System.in))).readLine();3755if (collator.compare(reply, "") == 0 ||3756collator.compare(reply, rb.getString("n")) == 0 ||3757collator.compare(reply, rb.getString("no")) == 0) {3758reply = "NO";3759} else if (collator.compare(reply, rb.getString("y")) == 0 ||3760collator.compare(reply, rb.getString("yes")) == 0) {3761reply = "YES";3762} else {3763System.err.println(rb.getString("Wrong.answer.try.again"));3764reply = null;3765}3766} while (reply == null);3767return reply;3768}37693770/**3771* Stores the (leaf) certificates of a keystore in a hashtable.3772* All certs belonging to the same CA are stored in a vector that3773* in turn is stored in the hashtable, keyed by the CA's subject DN.3774* Each cert comes with a string label that shows its origin and alias.3775*/3776private void keystorecerts2Hashtable(KeyStore ks,3777Hashtable<Principal, Vector<Pair<String,X509Certificate>>> hash)3778throws Exception {37793780for (Enumeration<String> aliases = ks.aliases();3781aliases.hasMoreElements(); ) {3782String alias = aliases.nextElement();3783Certificate cert = ks.getCertificate(alias);3784if (cert != null) {3785Principal subjectDN = ((X509Certificate)cert).getSubjectDN();3786Pair<String,X509Certificate> pair = new Pair<>(3787String.format(3788rb.getString(ks == caks ?3789"alias.in.cacerts" :3790"alias.in.keystore"),3791alias),3792(X509Certificate)cert);3793Vector<Pair<String,X509Certificate>> vec = hash.get(subjectDN);3794if (vec == null) {3795vec = new Vector<>();3796vec.addElement(pair);3797} else {3798if (!vec.contains(pair)) {3799vec.addElement(pair);3800}3801}3802hash.put(subjectDN, vec);3803}3804}3805}38063807/**3808* Returns the issue time that's specified the -startdate option3809* @param s the value of -startdate option3810*/3811private static Date getStartDate(String s) throws IOException {3812Calendar c = new GregorianCalendar();3813if (s != null) {3814IOException ioe = new IOException(3815rb.getString("Illegal.startdate.value"));3816int len = s.length();3817if (len == 0) {3818throw ioe;3819}3820if (s.charAt(0) == '-' || s.charAt(0) == '+') {3821// Form 1: ([+-]nnn[ymdHMS])+3822int start = 0;3823while (start < len) {3824int sign = 0;3825switch (s.charAt(start)) {3826case '+': sign = 1; break;3827case '-': sign = -1; break;3828default: throw ioe;3829}3830int i = start+1;3831for (; i<len; i++) {3832char ch = s.charAt(i);3833if (ch < '0' || ch > '9') break;3834}3835if (i == start+1) throw ioe;3836int number = Integer.parseInt(s.substring(start+1, i));3837if (i >= len) throw ioe;3838int unit = 0;3839switch (s.charAt(i)) {3840case 'y': unit = Calendar.YEAR; break;3841case 'm': unit = Calendar.MONTH; break;3842case 'd': unit = Calendar.DATE; break;3843case 'H': unit = Calendar.HOUR; break;3844case 'M': unit = Calendar.MINUTE; break;3845case 'S': unit = Calendar.SECOND; break;3846default: throw ioe;3847}3848c.add(unit, sign * number);3849start = i + 1;3850}3851} else {3852// Form 2: [yyyy/mm/dd] [HH:MM:SS]3853String date = null, time = null;3854if (len == 19) {3855date = s.substring(0, 10);3856time = s.substring(11);3857if (s.charAt(10) != ' ')3858throw ioe;3859} else if (len == 10) {3860date = s;3861} else if (len == 8) {3862time = s;3863} else {3864throw ioe;3865}3866if (date != null) {3867if (date.matches("\\d\\d\\d\\d\\/\\d\\d\\/\\d\\d")) {3868c.set(Integer.valueOf(date.substring(0, 4)),3869Integer.valueOf(date.substring(5, 7))-1,3870Integer.valueOf(date.substring(8, 10)));3871} else {3872throw ioe;3873}3874}3875if (time != null) {3876if (time.matches("\\d\\d:\\d\\d:\\d\\d")) {3877c.set(Calendar.HOUR_OF_DAY, Integer.valueOf(time.substring(0, 2)));3878c.set(Calendar.MINUTE, Integer.valueOf(time.substring(0, 2)));3879c.set(Calendar.SECOND, Integer.valueOf(time.substring(0, 2)));3880c.set(Calendar.MILLISECOND, 0);3881} else {3882throw ioe;3883}3884}3885}3886}3887return c.getTime();3888}38893890/**3891* Match a command (may be abbreviated) with a command set.3892* @param s the command provided3893* @param list the legal command set. If there is a null, commands after it3894* are regarded experimental, which means they are supported but their3895* existence should not be revealed to user.3896* @return the position of a single match, or -1 if none matched3897* @throws Exception if s is ambiguous3898*/3899private static int oneOf(String s, String... list) throws Exception {3900int[] match = new int[list.length];3901int nmatch = 0;3902int experiment = Integer.MAX_VALUE;3903for (int i = 0; i<list.length; i++) {3904String one = list[i];3905if (one == null) {3906experiment = i;3907continue;3908}3909if (one.toLowerCase(Locale.ENGLISH)3910.startsWith(s.toLowerCase(Locale.ENGLISH))) {3911match[nmatch++] = i;3912} else {3913StringBuffer sb = new StringBuffer();3914boolean first = true;3915for (char c: one.toCharArray()) {3916if (first) {3917sb.append(c);3918first = false;3919} else {3920if (!Character.isLowerCase(c)) {3921sb.append(c);3922}3923}3924}3925if (sb.toString().equalsIgnoreCase(s)) {3926match[nmatch++] = i;3927}3928}3929}3930if (nmatch == 0) {3931return -1;3932} else if (nmatch == 1) {3933return match[0];3934} else {3935// If multiple matches is in experimental commands, ignore them3936if (match[1] > experiment) {3937return match[0];3938}3939StringBuffer sb = new StringBuffer();3940MessageFormat form = new MessageFormat(rb.getString3941("command.{0}.is.ambiguous."));3942Object[] source = {s};3943sb.append(form.format(source));3944sb.append("\n ");3945for (int i=0; i<nmatch && match[i]<experiment; i++) {3946sb.append(' ');3947sb.append(list[match[i]]);3948}3949throw new Exception(sb.toString());3950}3951}39523953/**3954* Create a GeneralName object from known types3955* @param t one of 5 known types3956* @param v value3957* @return which one3958*/3959private GeneralName createGeneralName(String t, String v)3960throws Exception {3961GeneralNameInterface gn;3962int p = oneOf(t, "EMAIL", "URI", "DNS", "IP", "OID");3963if (p < 0) {3964throw new Exception(rb.getString(3965"Unrecognized.GeneralName.type.") + t);3966}3967switch (p) {3968case 0: gn = new RFC822Name(v); break;3969case 1: gn = new URIName(v); break;3970case 2: gn = new DNSName(v); break;3971case 3: gn = new IPAddressName(v); break;3972default: gn = new OIDName(v); break; //43973}3974return new GeneralName(gn);3975}39763977private static final String[] extSupported = {3978"BasicConstraints",3979"KeyUsage",3980"ExtendedKeyUsage",3981"SubjectAlternativeName",3982"IssuerAlternativeName",3983"SubjectInfoAccess",3984"AuthorityInfoAccess",3985null,3986"CRLDistributionPoints",3987};39883989private ObjectIdentifier findOidForExtName(String type)3990throws Exception {3991switch (oneOf(type, extSupported)) {3992case 0: return PKIXExtensions.BasicConstraints_Id;3993case 1: return PKIXExtensions.KeyUsage_Id;3994case 2: return PKIXExtensions.ExtendedKeyUsage_Id;3995case 3: return PKIXExtensions.SubjectAlternativeName_Id;3996case 4: return PKIXExtensions.IssuerAlternativeName_Id;3997case 5: return PKIXExtensions.SubjectInfoAccess_Id;3998case 6: return PKIXExtensions.AuthInfoAccess_Id;3999case 8: return PKIXExtensions.CRLDistributionPoints_Id;4000default: return new ObjectIdentifier(type);4001}4002}40034004/**4005* Create X509v3 extensions from a string representation. Note that the4006* SubjectKeyIdentifierExtension will always be created non-critical besides4007* the extension requested in the <code>extstr</code> argument.4008*4009* @param reqex the requested extensions, can be null, used for -gencert4010* @param ext the original extensions, can be null, used for -selfcert4011* @param extstrs -ext values, Read keytool doc4012* @param pkey the public key for the certificate4013* @param akey the public key for the authority (issuer)4014* @return the created CertificateExtensions4015*/4016private CertificateExtensions createV3Extensions(4017CertificateExtensions reqex,4018CertificateExtensions ext,4019List <String> extstrs,4020PublicKey pkey,4021PublicKey akey) throws Exception {40224023if (ext != null && reqex != null) {4024// This should not happen4025throw new Exception("One of request and original should be null.");4026}4027if (ext == null) ext = new CertificateExtensions();4028try {4029// name{:critical}{=value}4030// Honoring requested extensions4031if (reqex != null) {4032for(String extstr: extstrs) {4033if (extstr.toLowerCase(Locale.ENGLISH).startsWith("honored=")) {4034List<String> list = Arrays.asList(4035extstr.toLowerCase(Locale.ENGLISH).substring(8).split(","));4036// First check existence of "all"4037if (list.contains("all")) {4038ext = reqex; // we know ext was null4039}4040// one by one for others4041for (String item: list) {4042if (item.equals("all")) continue;40434044// add or remove4045boolean add = true;4046// -1, unchanged, 0 crtical, 1 non-critical4047int action = -1;4048String type = null;4049if (item.startsWith("-")) {4050add = false;4051type = item.substring(1);4052} else {4053int colonpos = item.indexOf(':');4054if (colonpos >= 0) {4055type = item.substring(0, colonpos);4056action = oneOf(item.substring(colonpos+1),4057"critical", "non-critical");4058if (action == -1) {4059throw new Exception(rb.getString4060("Illegal.value.") + item);4061}4062}4063}4064String n = reqex.getNameByOid(findOidForExtName(type));4065if (add) {4066Extension e = reqex.get(n);4067if (!e.isCritical() && action == 04068|| e.isCritical() && action == 1) {4069e = Extension.newExtension(4070e.getExtensionId(),4071!e.isCritical(),4072e.getExtensionValue());4073ext.set(n, e);4074}4075} else {4076ext.delete(n);4077}4078}4079break;4080}4081}4082}4083for(String extstr: extstrs) {4084String name, value;4085boolean isCritical = false;40864087int eqpos = extstr.indexOf('=');4088if (eqpos >= 0) {4089name = extstr.substring(0, eqpos);4090value = extstr.substring(eqpos+1);4091} else {4092name = extstr;4093value = null;4094}40954096int colonpos = name.indexOf(':');4097if (colonpos >= 0) {4098if (oneOf(name.substring(colonpos+1), "critical") == 0) {4099isCritical = true;4100}4101name = name.substring(0, colonpos);4102}41034104if (name.equalsIgnoreCase("honored")) {4105continue;4106}4107int exttype = oneOf(name, extSupported);4108switch (exttype) {4109case 0: // BC4110int pathLen = -1;4111boolean isCA = false;4112if (value == null) {4113isCA = true;4114} else {4115try { // the abbr format4116pathLen = Integer.parseInt(value);4117isCA = true;4118} catch (NumberFormatException ufe) {4119// ca:true,pathlen:14120for (String part: value.split(",")) {4121String[] nv = part.split(":");4122if (nv.length != 2) {4123throw new Exception(rb.getString4124("Illegal.value.") + extstr);4125} else {4126if (nv[0].equalsIgnoreCase("ca")) {4127isCA = Boolean.parseBoolean(nv[1]);4128} else if (nv[0].equalsIgnoreCase("pathlen")) {4129pathLen = Integer.parseInt(nv[1]);4130} else {4131throw new Exception(rb.getString4132("Illegal.value.") + extstr);4133}4134}4135}4136}4137}4138ext.set(BasicConstraintsExtension.NAME,4139new BasicConstraintsExtension(isCritical, isCA,4140pathLen));4141break;4142case 1: // KU4143if(value != null) {4144boolean[] ok = new boolean[9];4145for (String s: value.split(",")) {4146int p = oneOf(s,4147"digitalSignature", // (0),4148"nonRepudiation", // (1)4149"keyEncipherment", // (2),4150"dataEncipherment", // (3),4151"keyAgreement", // (4),4152"keyCertSign", // (5),4153"cRLSign", // (6),4154"encipherOnly", // (7),4155"decipherOnly", // (8)4156"contentCommitment" // also (1)4157);4158if (p < 0) {4159throw new Exception(rb.getString("Unknown.keyUsage.type.") + s);4160}4161if (p == 9) p = 1;4162ok[p] = true;4163}4164KeyUsageExtension kue = new KeyUsageExtension(ok);4165// The above KeyUsageExtension constructor does not4166// allow isCritical value, so...4167ext.set(KeyUsageExtension.NAME, Extension.newExtension(4168kue.getExtensionId(),4169isCritical,4170kue.getExtensionValue()));4171} else {4172throw new Exception(rb.getString4173("Illegal.value.") + extstr);4174}4175break;4176case 2: // EKU4177if(value != null) {4178Vector<ObjectIdentifier> v = new Vector<>();4179for (String s: value.split(",")) {4180int p = oneOf(s,4181"anyExtendedKeyUsage",4182"serverAuth", //14183"clientAuth", //24184"codeSigning", //34185"emailProtection", //44186"", //54187"", //64188"", //74189"timeStamping", //84190"OCSPSigning" //94191);4192if (p < 0) {4193try {4194v.add(new ObjectIdentifier(s));4195} catch (Exception e) {4196throw new Exception(rb.getString(4197"Unknown.extendedkeyUsage.type.") + s);4198}4199} else if (p == 0) {4200v.add(new ObjectIdentifier("2.5.29.37.0"));4201} else {4202v.add(new ObjectIdentifier("1.3.6.1.5.5.7.3." + p));4203}4204}4205ext.set(ExtendedKeyUsageExtension.NAME,4206new ExtendedKeyUsageExtension(isCritical, v));4207} else {4208throw new Exception(rb.getString4209("Illegal.value.") + extstr);4210}4211break;4212case 3: // SAN4213case 4: // IAN4214if(value != null) {4215String[] ps = value.split(",");4216GeneralNames gnames = new GeneralNames();4217for(String item: ps) {4218colonpos = item.indexOf(':');4219if (colonpos < 0) {4220throw new Exception("Illegal item " + item + " in " + extstr);4221}4222String t = item.substring(0, colonpos);4223String v = item.substring(colonpos+1);4224gnames.add(createGeneralName(t, v));4225}4226if (exttype == 3) {4227ext.set(SubjectAlternativeNameExtension.NAME,4228new SubjectAlternativeNameExtension(4229isCritical, gnames));4230} else {4231ext.set(IssuerAlternativeNameExtension.NAME,4232new IssuerAlternativeNameExtension(4233isCritical, gnames));4234}4235} else {4236throw new Exception(rb.getString4237("Illegal.value.") + extstr);4238}4239break;4240case 5: // SIA, always non-critical4241case 6: // AIA, always non-critical4242if (isCritical) {4243throw new Exception(rb.getString(4244"This.extension.cannot.be.marked.as.critical.") + extstr);4245}4246if(value != null) {4247List<AccessDescription> accessDescriptions =4248new ArrayList<>();4249String[] ps = value.split(",");4250for(String item: ps) {4251colonpos = item.indexOf(':');4252int colonpos2 = item.indexOf(':', colonpos+1);4253if (colonpos < 0 || colonpos2 < 0) {4254throw new Exception(rb.getString4255("Illegal.value.") + extstr);4256}4257String m = item.substring(0, colonpos);4258String t = item.substring(colonpos+1, colonpos2);4259String v = item.substring(colonpos2+1);4260int p = oneOf(m,4261"",4262"ocsp", //14263"caIssuers", //24264"timeStamping", //34265"",4266"caRepository" //54267);4268ObjectIdentifier oid;4269if (p < 0) {4270try {4271oid = new ObjectIdentifier(m);4272} catch (Exception e) {4273throw new Exception(rb.getString(4274"Unknown.AccessDescription.type.") + m);4275}4276} else {4277oid = new ObjectIdentifier("1.3.6.1.5.5.7.48." + p);4278}4279accessDescriptions.add(new AccessDescription(4280oid, createGeneralName(t, v)));4281}4282if (exttype == 5) {4283ext.set(SubjectInfoAccessExtension.NAME,4284new SubjectInfoAccessExtension(accessDescriptions));4285} else {4286ext.set(AuthorityInfoAccessExtension.NAME,4287new AuthorityInfoAccessExtension(accessDescriptions));4288}4289} else {4290throw new Exception(rb.getString4291("Illegal.value.") + extstr);4292}4293break;4294case 8: // CRL, experimental, only support 1 distributionpoint4295if(value != null) {4296String[] ps = value.split(",");4297GeneralNames gnames = new GeneralNames();4298for(String item: ps) {4299colonpos = item.indexOf(':');4300if (colonpos < 0) {4301throw new Exception("Illegal item " + item + " in " + extstr);4302}4303String t = item.substring(0, colonpos);4304String v = item.substring(colonpos+1);4305gnames.add(createGeneralName(t, v));4306}4307ext.set(CRLDistributionPointsExtension.NAME,4308new CRLDistributionPointsExtension(4309isCritical, Collections.singletonList(4310new DistributionPoint(gnames, null, null))));4311} else {4312throw new Exception(rb.getString4313("Illegal.value.") + extstr);4314}4315break;4316case -1:4317ObjectIdentifier oid = new ObjectIdentifier(name);4318byte[] data = null;4319if (value != null) {4320data = new byte[value.length() / 2 + 1];4321int pos = 0;4322for (char c: value.toCharArray()) {4323int hex;4324if (c >= '0' && c <= '9') {4325hex = c - '0' ;4326} else if (c >= 'A' && c <= 'F') {4327hex = c - 'A' + 10;4328} else if (c >= 'a' && c <= 'f') {4329hex = c - 'a' + 10;4330} else {4331continue;4332}4333if (pos % 2 == 0) {4334data[pos/2] = (byte)(hex << 4);4335} else {4336data[pos/2] += hex;4337}4338pos++;4339}4340if (pos % 2 != 0) {4341throw new Exception(rb.getString(4342"Odd.number.of.hex.digits.found.") + extstr);4343}4344data = Arrays.copyOf(data, pos/2);4345} else {4346data = new byte[0];4347}4348ext.set(oid.toString(), new Extension(oid, isCritical,4349new DerValue(DerValue.tag_OctetString, data)4350.toByteArray()));4351break;4352default:4353throw new Exception(rb.getString(4354"Unknown.extension.type.") + extstr);4355}4356}4357// always non-critical4358ext.set(SubjectKeyIdentifierExtension.NAME,4359new SubjectKeyIdentifierExtension(4360new KeyIdentifier(pkey).getIdentifier()));4361if (akey != null && !pkey.equals(akey)) {4362ext.set(AuthorityKeyIdentifierExtension.NAME,4363new AuthorityKeyIdentifierExtension(4364new KeyIdentifier(akey), null, null));4365}4366} catch(IOException e) {4367throw new RuntimeException(e);4368}4369return ext;4370}43714372private boolean isTrustedCert(Certificate cert) throws KeyStoreException {4373if (caks != null && caks.getCertificateAlias(cert) != null) {4374return true;4375} else {4376String inKS = keyStore.getCertificateAlias(cert);4377return inKS != null && keyStore.isCertificateEntry(inKS);4378}4379}43804381private void checkWeak(String label, String sigAlg, Key key) {4382if (sigAlg != null) {4383if (!DISABLED_CHECK.permits(SIG_PRIMITIVE_SET, sigAlg, null)) {4384weakWarnings.add(String.format(4385rb.getString("whose.sigalg.disabled"), label, sigAlg));4386} else if (!LEGACY_CHECK.permits(SIG_PRIMITIVE_SET, sigAlg, null)) {4387weakWarnings.add(String.format(4388rb.getString("whose.sigalg.weak"), label, sigAlg));4389}4390}43914392if (key != null) {4393if (!DISABLED_CHECK.permits(SIG_PRIMITIVE_SET, key)) {4394weakWarnings.add(String.format(4395rb.getString("whose.key.disabled"), label,4396String.format(rb.getString("key.bit"),4397KeyUtil.getKeySize(key), fullDisplayAlgName(key))));4398} else if (!LEGACY_CHECK.permits(SIG_PRIMITIVE_SET, key)) {4399weakWarnings.add(String.format(4400rb.getString("whose.key.weak"), label,4401String.format(rb.getString("key.bit"),4402KeyUtil.getKeySize(key), fullDisplayAlgName(key))));4403}4404}4405}44064407private void checkWeak(String label, Certificate[] certs)4408throws KeyStoreException {4409for (int i = 0; i < certs.length; i++) {4410Certificate cert = certs[i];4411if (cert instanceof X509Certificate) {4412X509Certificate xc = (X509Certificate)cert;4413String fullLabel = label;4414if (certs.length > 1) {4415fullLabel = oneInMany(label, i, certs.length);4416}4417checkWeak(fullLabel, xc);4418}4419}4420}44214422private void checkWeak(String label, Certificate cert)4423throws KeyStoreException {4424if (cert instanceof X509Certificate) {4425X509Certificate xc = (X509Certificate)cert;4426// No need to check the sigalg of a trust anchor4427String sigAlg = isTrustedCert(cert) ? null : xc.getSigAlgName();4428checkWeak(label, sigAlg, xc.getPublicKey());4429}4430}44314432private void checkWeak(String label, PKCS10 p10) {4433checkWeak(label, p10.getSigAlg(), p10.getSubjectPublicKeyInfo());4434}44354436private void checkWeak(String label, CRL crl, Key key) {4437if (crl instanceof X509CRLImpl) {4438X509CRLImpl impl = (X509CRLImpl)crl;4439checkWeak(label, impl.getSigAlgName(), key);4440}4441}44424443private void printWeakWarnings(boolean newLine) {4444if (!weakWarnings.isEmpty() && !nowarn) {4445System.err.println("\nWarning:");4446for (String warning : weakWarnings) {4447System.err.println(warning);4448}4449if (newLine) {4450// When calling before a yes/no prompt, add a new line4451System.err.println();4452}4453}4454weakWarnings.clear();4455}44564457/**4458* Prints the usage of this tool.4459*/4460private void usage() {4461if (command != null) {4462System.err.println("keytool " + command +4463rb.getString(".OPTION."));4464System.err.println();4465System.err.println(rb.getString(command.description));4466System.err.println();4467System.err.println(rb.getString("Options."));4468System.err.println();44694470// Left and right sides of the options list4471String[] left = new String[command.options.length];4472String[] right = new String[command.options.length];44734474// Check if there's an unknown option4475boolean found = false;44764477// Length of left side of options list4478int lenLeft = 0;4479for (int j=0; j<left.length; j++) {4480Option opt = command.options[j];4481left[j] = opt.toString();4482if (opt.arg != null) left[j] += " " + opt.arg;4483if (left[j].length() > lenLeft) {4484lenLeft = left[j].length();4485}4486right[j] = rb.getString(opt.description);4487}4488for (int j=0; j<left.length; j++) {4489System.err.printf(" %-" + lenLeft + "s %s\n",4490left[j], right[j]);4491}4492System.err.println();4493System.err.println(rb.getString(4494"Use.keytool.help.for.all.available.commands"));4495} else {4496System.err.println(rb.getString(4497"Key.and.Certificate.Management.Tool"));4498System.err.println();4499System.err.println(rb.getString("Commands."));4500System.err.println();4501for (Command c: Command.values()) {4502if (c == KEYCLONE) break;4503System.err.printf(" %-20s%s\n", c, rb.getString(c.description));4504}4505System.err.println();4506System.err.println(rb.getString(4507"Use.keytool.command.name.help.for.usage.of.command.name"));4508}4509}45104511private void tinyHelp() {4512usage();4513if (debug) {4514throw new RuntimeException("NO BIG ERROR, SORRY");4515} else {4516System.exit(1);4517}4518}45194520private void errorNeedArgument(String flag) {4521Object[] source = {flag};4522System.err.println(new MessageFormat(4523rb.getString("Command.option.flag.needs.an.argument.")).format(source));4524tinyHelp();4525}45264527private char[] getPass(String modifier, String arg) {4528char[] output = KeyStoreUtil.getPassWithModifier(modifier, arg, rb);4529if (output != null) return output;4530tinyHelp();4531return null; // Useless, tinyHelp() already exits.4532}4533}45344535// This class is exactly the same as com.sun.tools.javac.util.Pair,4536// it's copied here since the original one is not included in JRE.4537class Pair<A, B> {45384539public final A fst;4540public final B snd;45414542public Pair(A fst, B snd) {4543this.fst = fst;4544this.snd = snd;4545}45464547public String toString() {4548return "Pair[" + fst + "," + snd + "]";4549}45504551public boolean equals(Object other) {4552return4553other instanceof Pair &&4554Objects.equals(fst, ((Pair)other).fst) &&4555Objects.equals(snd, ((Pair)other).snd);4556}45574558public int hashCode() {4559if (fst == null) return (snd == null) ? 0 : snd.hashCode() + 1;4560else if (snd == null) return fst.hashCode() + 2;4561else return fst.hashCode() * 17 + snd.hashCode();4562}45634564public static <A,B> Pair<A,B> of(A a, B b) {4565return new Pair<>(a,b);4566}4567}4568456945704571