Path: blob/master/src/java.base/share/classes/sun/security/tools/keytool/Main.java
67848 views
/*1* Copyright (c) 1997, 2022, 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.Path;30import java.security.*;31import java.security.cert.Certificate;32import java.security.cert.CertificateFactory;33import java.security.cert.CertStoreException;34import java.security.cert.CRL;35import java.security.cert.X509Certificate;36import java.security.cert.CertificateException;37import java.security.cert.URICertStoreParameters;383940import java.security.interfaces.ECKey;41import java.security.interfaces.EdECKey;42import java.security.spec.ECParameterSpec;43import java.text.Collator;44import java.text.MessageFormat;45import java.util.*;46import java.util.function.BiFunction;47import java.util.jar.JarEntry;48import java.util.jar.JarFile;49import java.math.BigInteger;50import java.net.URI;51import java.net.URL;52import java.net.URLClassLoader;53import java.security.cert.CertStore;5455import java.security.cert.X509CRL;56import java.security.cert.X509CRLEntry;57import java.security.cert.X509CRLSelector;58import javax.security.auth.x500.X500Principal;59import java.util.Base64;6061import sun.security.pkcs12.PKCS12KeyStore;62import sun.security.util.ECKeySizeParameterSpec;63import sun.security.util.KeyUtil;64import sun.security.util.NamedCurve;65import sun.security.util.ObjectIdentifier;66import sun.security.pkcs10.PKCS10;67import sun.security.pkcs10.PKCS10Attribute;68import sun.security.provider.X509Factory;69import sun.security.provider.certpath.ssl.SSLServerCertStore;70import sun.security.util.KnownOIDs;71import sun.security.util.Password;72import sun.security.util.SecurityProperties;73import sun.security.util.SecurityProviderConstants;74import sun.security.util.SignatureUtil;75import javax.crypto.KeyGenerator;76import javax.crypto.SecretKey;77import javax.crypto.SecretKeyFactory;78import javax.crypto.spec.PBEKeySpec;7980import sun.security.pkcs.PKCS9Attribute;81import sun.security.tools.KeyStoreUtil;82import sun.security.tools.PathList;83import sun.security.util.DerValue;84import sun.security.util.Pem;85import sun.security.x509.*;8687import static java.security.KeyStore.*;88import static sun.security.tools.keytool.Main.Command.*;89import static sun.security.tools.keytool.Main.Option.*;90import sun.security.util.DisabledAlgorithmConstraints;9192/**93* This tool manages keystores.94*95* @author Jan Luehe96*97*98* @see java.security.KeyStore99* @see sun.security.provider.KeyProtector100* @see sun.security.provider.JavaKeyStore101*102* @since 1.2103*/104public final class Main {105106private static final byte[] CRLF = new byte[] {'\r', '\n'};107108private boolean debug = false;109private Command command = null;110private String sigAlgName = null;111private String keyAlgName = null;112private boolean verbose = false;113private int keysize = -1;114private String groupName = null;115private boolean rfc = false;116private long validity = (long)90;117private String alias = null;118private String dname = null;119private String dest = null;120private String filename = null;121private String infilename = null;122private String outfilename = null;123private String srcksfname = null;124125// User-specified providers are added before any command is called.126// However, they are not removed before the end of the main() method.127// If you're calling KeyTool.main() directly in your own Java program,128// please programtically add any providers you need and do not specify129// them through the command line.130131private Set<Pair <String, String>> providers = null;132private Set<Pair <String, String>> providerClasses = null;133private String storetype = null;134private String srcProviderName = null;135private String providerName = null;136private String pathlist = null;137private char[] storePass = null;138private char[] storePassNew = null;139private char[] keyPass = null;140private char[] keyPassNew = null;141private char[] newPass = null;142private char[] destKeyPass = null;143private char[] srckeyPass = null;144private String ksfname = null;145private File ksfile = null;146private InputStream ksStream = null; // keystore stream147private String sslserver = null;148private String jarfile = null;149private KeyStore keyStore = null;150private boolean token = false;151private boolean nullStream = false;152private boolean kssave = false;153private boolean noprompt = false;154private boolean trustcacerts = false;155private boolean protectedPath = false;156private boolean srcprotectedPath = false;157private boolean cacerts = false;158private boolean nowarn = false;159private KeyStore caks = null; // "cacerts" keystore160private char[] srcstorePass = null;161private String srcstoretype = null;162private Set<char[]> passwords = new HashSet<>();163private String startDate = null;164private String signerAlias = null;165private char[] signerKeyPass = null;166167private boolean tlsInfo = false;168169private List<String> ids = new ArrayList<>(); // used in GENCRL170private List<String> v3ext = new ArrayList<>();171172// In-place importkeystore is special.173// A backup is needed, and no need to prompt for deststorepass.174private boolean inplaceImport = false;175private String inplaceBackupName = null;176177// Warnings on weak algorithms etc178private List<String> weakWarnings = new ArrayList<>();179180private static final DisabledAlgorithmConstraints DISABLED_CHECK =181new DisabledAlgorithmConstraints(182DisabledAlgorithmConstraints.PROPERTY_CERTPATH_DISABLED_ALGS);183184private static final DisabledAlgorithmConstraints LEGACY_CHECK =185new DisabledAlgorithmConstraints(186DisabledAlgorithmConstraints.PROPERTY_SECURITY_LEGACY_ALGS);187188private static final Set<CryptoPrimitive> SIG_PRIMITIVE_SET = Collections189.unmodifiableSet(EnumSet.of(CryptoPrimitive.SIGNATURE));190private boolean isPasswordlessKeyStore = false;191192enum Command {193CERTREQ("Generates.a.certificate.request",194ALIAS, SIGALG, FILEOUT, KEYPASS, KEYSTORE, DNAME,195EXT, STOREPASS, STORETYPE, PROVIDERNAME, ADDPROVIDER,196PROVIDERCLASS, PROVIDERPATH, V, PROTECTED),197CHANGEALIAS("Changes.an.entry.s.alias",198ALIAS, DESTALIAS, KEYPASS, KEYSTORE, CACERTS, STOREPASS,199STORETYPE, PROVIDERNAME, ADDPROVIDER, PROVIDERCLASS,200PROVIDERPATH, V, PROTECTED),201DELETE("Deletes.an.entry",202ALIAS, KEYSTORE, CACERTS, STOREPASS, STORETYPE,203PROVIDERNAME, ADDPROVIDER, PROVIDERCLASS,204PROVIDERPATH, V, PROTECTED),205EXPORTCERT("Exports.certificate",206RFC, ALIAS, FILEOUT, KEYSTORE, CACERTS, STOREPASS,207STORETYPE, PROVIDERNAME, ADDPROVIDER, PROVIDERCLASS,208PROVIDERPATH, V, PROTECTED),209GENKEYPAIR("Generates.a.key.pair",210ALIAS, KEYALG, KEYSIZE, CURVENAME, SIGALG, DNAME,211STARTDATE, EXT, VALIDITY, KEYPASS, KEYSTORE,212SIGNER, SIGNERKEYPASS,213STOREPASS, STORETYPE, PROVIDERNAME, ADDPROVIDER,214PROVIDERCLASS, PROVIDERPATH, V, PROTECTED),215GENSECKEY("Generates.a.secret.key",216ALIAS, KEYPASS, KEYALG, KEYSIZE, KEYSTORE,217STOREPASS, STORETYPE, PROVIDERNAME, ADDPROVIDER,218PROVIDERCLASS, PROVIDERPATH, V, PROTECTED),219GENCERT("Generates.certificate.from.a.certificate.request",220RFC, INFILE, OUTFILE, ALIAS, SIGALG, DNAME,221STARTDATE, EXT, VALIDITY, KEYPASS, KEYSTORE,222STOREPASS, STORETYPE, PROVIDERNAME, ADDPROVIDER,223PROVIDERCLASS, PROVIDERPATH, V, PROTECTED),224IMPORTCERT("Imports.a.certificate.or.a.certificate.chain",225NOPROMPT, TRUSTCACERTS, PROTECTED, ALIAS, FILEIN,226KEYPASS, KEYSTORE, CACERTS, STOREPASS, STORETYPE,227PROVIDERNAME, ADDPROVIDER, PROVIDERCLASS,228PROVIDERPATH, V),229IMPORTPASS("Imports.a.password",230ALIAS, KEYPASS, KEYALG, KEYSIZE, KEYSTORE,231STOREPASS, STORETYPE, PROVIDERNAME, ADDPROVIDER,232PROVIDERCLASS, PROVIDERPATH, V, PROTECTED),233IMPORTKEYSTORE("Imports.one.or.all.entries.from.another.keystore",234SRCKEYSTORE, DESTKEYSTORE, SRCSTORETYPE,235DESTSTORETYPE, SRCSTOREPASS, DESTSTOREPASS,236SRCPROTECTED, DESTPROTECTED, SRCPROVIDERNAME, DESTPROVIDERNAME,237SRCALIAS, DESTALIAS, SRCKEYPASS, DESTKEYPASS,238NOPROMPT, ADDPROVIDER, PROVIDERCLASS, PROVIDERPATH,239V),240KEYPASSWD("Changes.the.key.password.of.an.entry",241ALIAS, KEYPASS, NEW, KEYSTORE, STOREPASS,242STORETYPE, PROVIDERNAME, ADDPROVIDER, PROVIDERCLASS,243PROVIDERPATH, V),244LIST("Lists.entries.in.a.keystore",245RFC, ALIAS, KEYSTORE, CACERTS, STOREPASS, STORETYPE,246PROVIDERNAME, ADDPROVIDER, PROVIDERCLASS,247PROVIDERPATH, V, PROTECTED),248PRINTCERT("Prints.the.content.of.a.certificate",249RFC, FILEIN, SSLSERVER, JARFILE,250KEYSTORE, STOREPASS, STORETYPE, TRUSTCACERTS,251PROVIDERNAME, ADDPROVIDER, PROVIDERCLASS,252PROVIDERPATH, V, PROTECTED),253PRINTCERTREQ("Prints.the.content.of.a.certificate.request",254FILEIN, V),255PRINTCRL("Prints.the.content.of.a.CRL.file",256FILEIN, KEYSTORE, STOREPASS, STORETYPE, TRUSTCACERTS,257PROVIDERNAME, ADDPROVIDER, PROVIDERCLASS, PROVIDERPATH,258V, PROTECTED),259STOREPASSWD("Changes.the.store.password.of.a.keystore",260NEW, KEYSTORE, CACERTS, STOREPASS, STORETYPE, PROVIDERNAME,261ADDPROVIDER, PROVIDERCLASS, PROVIDERPATH, V),262SHOWINFO("showinfo.command.help",263TLS, V),264265// Undocumented start here, KEYCLONE is used a marker in -help;266267KEYCLONE("Clones.a.key.entry",268ALIAS, DESTALIAS, KEYPASS, NEW, STORETYPE,269KEYSTORE, STOREPASS, PROVIDERNAME, ADDPROVIDER,270PROVIDERCLASS, PROVIDERPATH, V),271SELFCERT("Generates.a.self.signed.certificate",272ALIAS, SIGALG, DNAME, STARTDATE, EXT, VALIDITY, KEYPASS,273STORETYPE, KEYSTORE, STOREPASS, PROVIDERNAME,274ADDPROVIDER, PROVIDERCLASS, PROVIDERPATH, V),275GENCRL("Generates.CRL",276RFC, FILEOUT, ID,277ALIAS, SIGALG, KEYPASS, KEYSTORE,278STOREPASS, STORETYPE, PROVIDERNAME, ADDPROVIDER,279PROVIDERCLASS, PROVIDERPATH, V, PROTECTED),280IDENTITYDB("Imports.entries.from.a.JDK.1.1.x.style.identity.database",281FILEIN, STORETYPE, KEYSTORE, STOREPASS, PROVIDERNAME,282ADDPROVIDER, PROVIDERCLASS, PROVIDERPATH, V);283284final String description;285final Option[] options;286final String name;287288String altName; // "genkey" is altName for "genkeypair"289290Command(String d, Option... o) {291description = d;292options = o;293name = "-" + name().toLowerCase(Locale.ENGLISH);294}295@Override296public String toString() {297return name;298}299public String getAltName() {300return altName;301}302public void setAltName(String altName) {303this.altName = altName;304}305public static Command getCommand(String cmd) {306for (Command c: Command.values()) {307if (collator.compare(cmd, c.name) == 0308|| (c.altName != null309&& collator.compare(cmd, c.altName) == 0)) {310return c;311}312}313return null;314}315};316317static {318Command.GENKEYPAIR.setAltName("-genkey");319Command.IMPORTCERT.setAltName("-import");320Command.EXPORTCERT.setAltName("-export");321Command.IMPORTPASS.setAltName("-importpassword");322}323324// If an option is allowed multiple times, remember to record it325// in the optionsSet.contains() block in parseArgs().326enum Option {327ALIAS("alias", "<alias>", "alias.name.of.the.entry.to.process"),328CURVENAME("groupname", "<name>", "groupname.option.help"),329DESTALIAS("destalias", "<alias>", "destination.alias"),330DESTKEYPASS("destkeypass", "<arg>", "destination.key.password"),331DESTKEYSTORE("destkeystore", "<keystore>", "destination.keystore.name"),332DESTPROTECTED("destprotected", null, "destination.keystore.password.protected"),333DESTPROVIDERNAME("destprovidername", "<name>", "destination.keystore.provider.name"),334DESTSTOREPASS("deststorepass", "<arg>", "destination.keystore.password"),335DESTSTORETYPE("deststoretype", "<type>", "destination.keystore.type"),336DNAME("dname", "<name>", "distinguished.name"),337EXT("ext", "<value>", "X.509.extension"),338FILEOUT("file", "<file>", "output.file.name"),339FILEIN("file", "<file>", "input.file.name"),340ID("id", "<id:reason>", "Serial.ID.of.cert.to.revoke"),341INFILE("infile", "<file>", "input.file.name"),342KEYALG("keyalg", "<alg>", "key.algorithm.name"),343KEYPASS("keypass", "<arg>", "key.password"),344KEYSIZE("keysize", "<size>", "key.bit.size"),345KEYSTORE("keystore", "<keystore>", "keystore.name"),346CACERTS("cacerts", null, "access.the.cacerts.keystore"),347NEW("new", "<arg>", "new.password"),348NOPROMPT("noprompt", null, "do.not.prompt"),349OUTFILE("outfile", "<file>", "output.file.name"),350PROTECTED("protected", null, "password.through.protected.mechanism"),351PROVIDERCLASS("providerclass", "<class>\n[-providerarg <arg>]", "provider.class.option"),352ADDPROVIDER("addprovider", "<name>\n[-providerarg <arg>]", "addprovider.option"),353PROVIDERNAME("providername", "<name>", "provider.name"),354PROVIDERPATH("providerpath", "<list>", "provider.classpath"),355RFC("rfc", null, "output.in.RFC.style"),356SIGALG("sigalg", "<alg>", "signature.algorithm.name"),357SIGNER("signer", "<alias>", "signer.alias"),358SIGNERKEYPASS("signerkeypass", "<arg>", "signer.key.password"),359SRCALIAS("srcalias", "<alias>", "source.alias"),360SRCKEYPASS("srckeypass", "<arg>", "source.key.password"),361SRCKEYSTORE("srckeystore", "<keystore>", "source.keystore.name"),362SRCPROTECTED("srcprotected", null, "source.keystore.password.protected"),363SRCPROVIDERNAME("srcprovidername", "<name>", "source.keystore.provider.name"),364SRCSTOREPASS("srcstorepass", "<arg>", "source.keystore.password"),365SRCSTORETYPE("srcstoretype", "<type>", "source.keystore.type"),366SSLSERVER("sslserver", "<server[:port]>", "SSL.server.host.and.port"),367JARFILE("jarfile", "<file>", "signed.jar.file"),368STARTDATE("startdate", "<date>", "certificate.validity.start.date.time"),369STOREPASS("storepass", "<arg>", "keystore.password"),370STORETYPE("storetype", "<type>", "keystore.type"),371TLS("tls", null, "tls.option.help"),372TRUSTCACERTS("trustcacerts", null, "trust.certificates.from.cacerts"),373V("v", null, "verbose.output"),374VALIDITY("validity", "<days>", "validity.number.of.days");375376final String name, arg, description;377Option(String name, String arg, String description) {378this.name = name;379this.arg = arg;380this.description = description;381}382@Override383public String toString() {384return "-" + name;385}386};387388private static final String NONE = "NONE";389private static final String P11KEYSTORE = "PKCS11";390private static final String P12KEYSTORE = "PKCS12";391private static final String keyAlias = "mykey";392393// for i18n394private static final java.util.ResourceBundle rb =395java.util.ResourceBundle.getBundle(396"sun.security.tools.keytool.Resources");397private static final Collator collator = Collator.getInstance();398static {399// this is for case insensitive string comparisons400collator.setStrength(Collator.PRIMARY);401};402403private Main() { }404405public static void main(String[] args) throws Exception {406Main kt = new Main();407kt.run(args, System.out);408}409410private void run(String[] args, PrintStream out) throws Exception {411try {412args = parseArgs(args);413if (command != null) {414doCommands(out);415}416} catch (Exception e) {417System.out.println(rb.getString("keytool.error.") + e);418if (verbose) {419e.printStackTrace(System.out);420}421if (!debug) {422System.exit(1);423} else {424throw e;425}426} finally {427printWeakWarnings(false);428for (char[] pass : passwords) {429if (pass != null) {430Arrays.fill(pass, ' ');431pass = null;432}433}434435if (ksStream != null) {436ksStream.close();437}438}439}440441/**442* Parse command line arguments.443*/444String[] parseArgs(String[] args) throws Exception {445446int i=0;447boolean help = args.length == 0;448449String confFile = null;450451// Records all commands and options set. Used to check dups.452Set<String> optionsSet = new HashSet<>();453454for (i=0; i < args.length; i++) {455String flags = args[i];456if (flags.startsWith("-")) {457String lowerFlags = flags.toLowerCase(Locale.ROOT);458if (optionsSet.contains(lowerFlags)) {459switch (lowerFlags) {460case "-ext":461case "-id":462case "-provider":463case "-addprovider":464case "-providerclass":465case "-providerarg":466// These options are allowed multiple times467break;468default:469weakWarnings.add(String.format(470rb.getString("option.1.set.twice"),471lowerFlags));472}473} else {474optionsSet.add(lowerFlags);475}476if (collator.compare(flags, "-conf") == 0) {477if (i == args.length - 1) {478errorNeedArgument(flags);479}480confFile = args[++i];481} else {482Command c = Command.getCommand(flags);483if (c != null) {484if (command == null) {485command = c;486} else {487throw new Exception(String.format(488rb.getString("multiple.commands.1.2"),489command.name, c.name));490}491}492}493}494}495496if (confFile != null && command != null) {497args = KeyStoreUtil.expandArgs("keytool", confFile,498command.toString(),499command.getAltName(), args);500}501502debug = Arrays.stream(args).anyMatch(503x -> collator.compare(x, "-debug") == 0);504505if (debug) {506// No need to localize debug output507System.out.println("Command line args: " +508Arrays.toString(args));509}510511for (i=0; (i < args.length) && args[i].startsWith("-"); i++) {512513String flags = args[i];514515// Check if the last option needs an arg516if (i == args.length - 1) {517for (Option option: Option.values()) {518// Only options with an arg need to be checked519if (collator.compare(flags, option.toString()) == 0) {520if (option.arg != null) errorNeedArgument(flags);521break;522}523}524}525526/*527* Check modifiers528*/529String modifier = null;530int pos = flags.indexOf(':');531if (pos > 0) {532modifier = flags.substring(pos+1);533flags = flags.substring(0, pos);534}535536/*537* command modes538*/539Command c = Command.getCommand(flags);540541if (c != null) {542command = c;543} else if (collator.compare(flags, "--help") == 0 ||544collator.compare(flags, "-h") == 0 ||545collator.compare(flags, "-?") == 0 ||546// -help: legacy.547collator.compare(flags, "-help") == 0) {548help = true;549} else if (collator.compare(flags, "-conf") == 0) {550i++;551} else if (collator.compare(flags, "-nowarn") == 0) {552nowarn = true;553} else if (collator.compare(flags, "-keystore") == 0) {554ksfname = args[++i];555if (new File(ksfname).getCanonicalPath().equals(556new File(KeyStoreUtil.getCacerts()).getCanonicalPath())) {557System.err.println(rb.getString("warning.cacerts.option"));558}559} else if (collator.compare(flags, "-destkeystore") == 0) {560ksfname = args[++i];561} else if (collator.compare(flags, "-cacerts") == 0) {562cacerts = true;563} else if (collator.compare(flags, "-storepass") == 0 ||564collator.compare(flags, "-deststorepass") == 0) {565storePass = getPass(modifier, args[++i]);566passwords.add(storePass);567} else if (collator.compare(flags, "-storetype") == 0 ||568collator.compare(flags, "-deststoretype") == 0) {569storetype = KeyStoreUtil.niceStoreTypeName(args[++i]);570} else if (collator.compare(flags, "-srcstorepass") == 0) {571srcstorePass = getPass(modifier, args[++i]);572passwords.add(srcstorePass);573} else if (collator.compare(flags, "-srcstoretype") == 0) {574srcstoretype = KeyStoreUtil.niceStoreTypeName(args[++i]);575} else if (collator.compare(flags, "-srckeypass") == 0) {576srckeyPass = getPass(modifier, args[++i]);577passwords.add(srckeyPass);578} else if (collator.compare(flags, "-srcprovidername") == 0) {579srcProviderName = args[++i];580} else if (collator.compare(flags, "-providername") == 0 ||581collator.compare(flags, "-destprovidername") == 0) {582providerName = args[++i];583} else if (collator.compare(flags, "-providerpath") == 0) {584pathlist = args[++i];585} else if (collator.compare(flags, "-keypass") == 0) {586keyPass = getPass(modifier, args[++i]);587passwords.add(keyPass);588} else if (collator.compare(flags, "-new") == 0) {589newPass = getPass(modifier, args[++i]);590passwords.add(newPass);591} else if (collator.compare(flags, "-destkeypass") == 0) {592destKeyPass = getPass(modifier, args[++i]);593passwords.add(destKeyPass);594} else if (collator.compare(flags, "-alias") == 0 ||595collator.compare(flags, "-srcalias") == 0) {596alias = args[++i];597} else if (collator.compare(flags, "-dest") == 0 ||598collator.compare(flags, "-destalias") == 0) {599dest = args[++i];600} else if (collator.compare(flags, "-dname") == 0) {601dname = args[++i];602} else if (collator.compare(flags, "-keysize") == 0) {603keysize = Integer.parseInt(args[++i]);604} else if (collator.compare(flags, "-groupname") == 0) {605groupName = args[++i];606} else if (collator.compare(flags, "-keyalg") == 0) {607keyAlgName = args[++i];608} else if (collator.compare(flags, "-sigalg") == 0) {609sigAlgName = args[++i];610} else if (collator.compare(flags, "-signer") == 0) {611signerAlias = args[++i];612} else if (collator.compare(flags, "-signerkeypass") == 0) {613signerKeyPass = getPass(modifier, args[++i]);614passwords.add(signerKeyPass);615} else if (collator.compare(flags, "-startdate") == 0) {616startDate = args[++i];617} else if (collator.compare(flags, "-validity") == 0) {618validity = Long.parseLong(args[++i]);619} else if (collator.compare(flags, "-ext") == 0) {620v3ext.add(args[++i]);621} else if (collator.compare(flags, "-id") == 0) {622ids.add(args[++i]);623} else if (collator.compare(flags, "-file") == 0) {624filename = args[++i];625} else if (collator.compare(flags, "-infile") == 0) {626infilename = args[++i];627} else if (collator.compare(flags, "-outfile") == 0) {628outfilename = args[++i];629} else if (collator.compare(flags, "-sslserver") == 0) {630sslserver = args[++i];631} else if (collator.compare(flags, "-jarfile") == 0) {632jarfile = args[++i];633} else if (collator.compare(flags, "-srckeystore") == 0) {634srcksfname = args[++i];635} else if (collator.compare(flags, "-provider") == 0 ||636collator.compare(flags, "-providerclass") == 0) {637if (providerClasses == null) {638providerClasses = new HashSet<Pair <String, String>> (3);639}640String providerClass = args[++i];641String providerArg = null;642643if (args.length > (i+1)) {644flags = args[i+1];645if (collator.compare(flags, "-providerarg") == 0) {646if (args.length == (i+2)) errorNeedArgument(flags);647providerArg = args[i+2];648i += 2;649}650}651providerClasses.add(652Pair.of(providerClass, providerArg));653} else if (collator.compare(flags, "-addprovider") == 0) {654if (providers == null) {655providers = new HashSet<Pair <String, String>> (3);656}657String provider = args[++i];658String providerArg = null;659660if (args.length > (i+1)) {661flags = args[i+1];662if (collator.compare(flags, "-providerarg") == 0) {663if (args.length == (i+2)) errorNeedArgument(flags);664providerArg = args[i+2];665i += 2;666}667}668providers.add(669Pair.of(provider, providerArg));670}671672/*673* options674*/675else if (collator.compare(flags, "-v") == 0) {676verbose = true;677} else if (collator.compare(flags, "-debug") == 0) {678// Already processed679} else if (collator.compare(flags, "-rfc") == 0) {680rfc = true;681} else if (collator.compare(flags, "-noprompt") == 0) {682noprompt = true;683} else if (collator.compare(flags, "-trustcacerts") == 0) {684trustcacerts = true;685} else if (collator.compare(flags, "-protected") == 0 ||686collator.compare(flags, "-destprotected") == 0) {687protectedPath = true;688} else if (collator.compare(flags, "-srcprotected") == 0) {689srcprotectedPath = true;690} else if (collator.compare(flags, "-tls") == 0) {691tlsInfo = true;692} else {693System.err.println(rb.getString("Illegal.option.") + flags);694tinyHelp();695}696}697698if (i<args.length) {699System.err.println(rb.getString("Illegal.option.") + args[i]);700tinyHelp();701}702703if (command == null) {704if (help) {705usage();706} else {707System.err.println(rb.getString("Usage.error.no.command.provided"));708tinyHelp();709}710} else if (help) {711usage();712command = null;713}714715return args;716}717718boolean isKeyStoreRelated(Command cmd) {719return cmd != PRINTCERTREQ && cmd != SHOWINFO;720}721722/**723* Execute the commands.724*/725void doCommands(PrintStream out) throws Exception {726727if (cacerts) {728if (ksfname != null || storetype != null) {729throw new IllegalArgumentException(rb.getString730("the.keystore.or.storetype.option.cannot.be.used.with.the.cacerts.option"));731}732ksfname = KeyStoreUtil.getCacerts();733}734735if (P11KEYSTORE.equalsIgnoreCase(storetype) ||736KeyStoreUtil.isWindowsKeyStore(storetype)) {737token = true;738if (ksfname == null) {739ksfname = NONE;740}741}742if (NONE.equals(ksfname)) {743nullStream = true;744}745746if (token && !nullStream) {747System.err.println(MessageFormat.format(rb.getString748(".keystore.must.be.NONE.if.storetype.is.{0}"), storetype));749System.err.println();750tinyHelp();751}752753if (token &&754(command == KEYPASSWD || command == STOREPASSWD)) {755throw new UnsupportedOperationException(MessageFormat.format(rb.getString756(".storepasswd.and.keypasswd.commands.not.supported.if.storetype.is.{0}"), storetype));757}758759if (token && (keyPass != null || newPass != null || destKeyPass != null)) {760throw new IllegalArgumentException(MessageFormat.format(rb.getString761(".keypass.and.new.can.not.be.specified.if.storetype.is.{0}"), storetype));762}763764if (protectedPath) {765if (storePass != null || keyPass != null ||766newPass != null || destKeyPass != null) {767throw new IllegalArgumentException(rb.getString768("if.protected.is.specified.then.storepass.keypass.and.new.must.not.be.specified"));769}770}771772if (srcprotectedPath) {773if (srcstorePass != null || srckeyPass != null) {774throw new IllegalArgumentException(rb.getString775("if.srcprotected.is.specified.then.srcstorepass.and.srckeypass.must.not.be.specified"));776}777}778779if (KeyStoreUtil.isWindowsKeyStore(storetype)) {780if (storePass != null || keyPass != null ||781newPass != null || destKeyPass != null) {782throw new IllegalArgumentException(rb.getString783("if.keystore.is.not.password.protected.then.storepass.keypass.and.new.must.not.be.specified"));784}785}786787if (KeyStoreUtil.isWindowsKeyStore(srcstoretype)) {788if (srcstorePass != null || srckeyPass != null) {789throw new IllegalArgumentException(rb.getString790("if.source.keystore.is.not.password.protected.then.srcstorepass.and.srckeypass.must.not.be.specified"));791}792}793794if (validity <= (long)0) {795throw new Exception796(rb.getString("Validity.must.be.greater.than.zero"));797}798799// Try to load and install specified provider800if (providers != null) {801for (Pair<String, String> provider : providers) {802try {803KeyStoreUtil.loadProviderByName(804provider.fst, provider.snd);805if (debug) {806System.out.println("loadProviderByName: " + provider.fst);807}808} catch (IllegalArgumentException e) {809throw new Exception(String.format(rb.getString(810"provider.name.not.found"), provider.fst));811}812}813}814if (providerClasses != null) {815ClassLoader cl = null;816if (pathlist != null) {817String path = null;818path = PathList.appendPath(819path, System.getProperty("java.class.path"));820path = PathList.appendPath(821path, System.getProperty("env.class.path"));822path = PathList.appendPath(path, pathlist);823824URL[] urls = PathList.pathToURLs(path);825cl = new URLClassLoader(urls);826} else {827cl = ClassLoader.getSystemClassLoader();828}829for (Pair<String, String> provider : providerClasses) {830try {831KeyStoreUtil.loadProviderByClass(832provider.fst, provider.snd, cl);833if (debug) {834System.out.println("loadProviderByClass: " + provider.fst);835}836} catch (ClassCastException cce) {837throw new Exception(String.format(rb.getString(838"provclass.not.a.provider"), provider.fst));839} catch (IllegalArgumentException e) {840throw new Exception(String.format(rb.getString(841"provider.class.not.found"), provider.fst), e.getCause());842}843}844}845846if (command == LIST && verbose && rfc) {847System.err.println(rb.getString848("Must.not.specify.both.v.and.rfc.with.list.command"));849tinyHelp();850}851852// Make sure provided passwords are at least 6 characters long853if (command == GENKEYPAIR && keyPass!=null && keyPass.length < 6) {854throw new Exception(rb.getString855("Key.password.must.be.at.least.6.characters"));856}857if (newPass != null && newPass.length < 6) {858throw new Exception(rb.getString859("New.password.must.be.at.least.6.characters"));860}861if (destKeyPass != null && destKeyPass.length < 6) {862throw new Exception(rb.getString863("New.password.must.be.at.least.6.characters"));864}865866// Set this before inplaceImport check so we can compare name.867if (ksfname == null) {868ksfname = System.getProperty("user.home") + File.separator869+ ".keystore";870}871872KeyStore srcKeyStore = null;873if (command == IMPORTKEYSTORE) {874inplaceImport = inplaceImportCheck();875if (inplaceImport) {876// We load srckeystore first so we have srcstorePass that877// can be assigned to storePass878srcKeyStore = loadSourceKeyStore();879if (storePass == null) {880storePass = srcstorePass;881}882}883}884885// Check if keystore exists.886// If no keystore has been specified at the command line, try to use887// the default, which is located in $HOME/.keystore.888// No need to check if isKeyStoreRelated(command) is false.889890// DO NOT open the existing keystore if this is an in-place import.891// The keystore should be created as brand new.892if (isKeyStoreRelated(command) && !nullStream && !inplaceImport) {893try {894ksfile = new File(ksfname);895// Check if keystore file is empty896if (ksfile.exists() && ksfile.length() == 0) {897throw new Exception(rb.getString898("Keystore.file.exists.but.is.empty.") + ksfname);899}900ksStream = new FileInputStream(ksfile);901} catch (FileNotFoundException e) {902// These commands do not need the keystore to be existing.903// Either it will create a new one or the keystore is904// optional (i.e. PRINTCRL and PRINTCERT).905if (command != GENKEYPAIR &&906command != GENSECKEY &&907command != IDENTITYDB &&908command != IMPORTCERT &&909command != IMPORTPASS &&910command != IMPORTKEYSTORE &&911command != PRINTCRL &&912command != PRINTCERT) {913throw new Exception(rb.getString914("Keystore.file.does.not.exist.") + ksfname);915}916}917}918919if ((command == KEYCLONE || command == CHANGEALIAS)920&& dest == null) {921dest = getAlias("destination");922if ("".equals(dest)) {923throw new Exception(rb.getString924("Must.specify.destination.alias"));925}926}927928if (command == DELETE && alias == null) {929alias = getAlias(null);930if ("".equals(alias)) {931throw new Exception(rb.getString("Must.specify.alias"));932}933}934935if (ksfile != null && ksStream != null && providerName == null &&936!inplaceImport) {937// existing keystore938if (storetype == null) {939// Probe for keystore type when filename is available940keyStore = KeyStore.getInstance(ksfile, storePass);941storetype = keyStore.getType();942} else {943keyStore = KeyStore.getInstance(storetype);944// storePass might be null here, will probably prompt later945keyStore.load(ksStream, storePass);946}947if (storetype.equalsIgnoreCase("pkcs12")) {948try {949isPasswordlessKeyStore = PKCS12KeyStore.isPasswordless(ksfile);950} catch (IOException ioe) {951// This must be a JKS keystore that's opened as a PKCS12952}953}954} else {955// Create new keystore956if (storetype == null) {957storetype = KeyStore.getDefaultType();958}959if (providerName == null) {960keyStore = KeyStore.getInstance(storetype);961} else {962keyStore = KeyStore.getInstance(storetype, providerName);963}964// When creating a new pkcs12 file, Do not prompt for storepass965// if certProtectionAlgorithm and macAlgorithm are both NONE.966if (storetype.equalsIgnoreCase("pkcs12")) {967isPasswordlessKeyStore =968"NONE".equals(SecurityProperties.privilegedGetOverridable(969"keystore.pkcs12.certProtectionAlgorithm"))970&& "NONE".equals(SecurityProperties.privilegedGetOverridable(971"keystore.pkcs12.macAlgorithm"));972}973974/*975* Load the keystore data.976*977* At this point, it's OK if no keystore password has been provided.978* We want to make sure that we can load the keystore data, i.e.,979* the keystore data has the right format. If we cannot load the980* keystore, why bother asking the user for his or her password?981* Only if we were able to load the keystore, and no keystore982* password has been provided, will we prompt the user for the983* keystore password to verify the keystore integrity.984* This means that the keystore is loaded twice: first load operation985* checks the keystore format, second load operation verifies the986* keystore integrity.987*988* If the keystore password has already been provided (at the989* command line), however, the keystore is loaded only once, and the990* keystore format and integrity are checked "at the same time".991*992* Null stream keystores are loaded later.993*/994if (!nullStream) {995if (inplaceImport) {996keyStore.load(null, storePass);997} else {998// both ksStream and storePass could be null999keyStore.load(ksStream, storePass);1000}1001}1002}10031004if (P12KEYSTORE.equalsIgnoreCase(storetype) && command == KEYPASSWD) {1005throw new UnsupportedOperationException(rb.getString1006(".keypasswd.commands.not.supported.if.storetype.is.PKCS12"));1007}10081009// All commands that create or modify the keystore require a keystore1010// password.10111012if (nullStream && storePass != null) {1013keyStore.load(null, storePass);1014} else if (!nullStream && storePass != null) {1015// If we are creating a new non nullStream-based keystore,1016// insist that the password be at least 6 characters1017if (ksStream == null && storePass.length < 6) {1018throw new Exception(rb.getString1019("Keystore.password.must.be.at.least.6.characters"));1020}1021} else if (storePass == null) {1022if (!protectedPath && !KeyStoreUtil.isWindowsKeyStore(storetype)1023&& isKeyStoreRelated(command)1024&& !isPasswordlessKeyStore) {1025if (command == CERTREQ ||1026command == DELETE ||1027command == GENKEYPAIR ||1028command == GENSECKEY ||1029command == IMPORTCERT ||1030command == IMPORTPASS ||1031command == IMPORTKEYSTORE ||1032command == KEYCLONE ||1033command == CHANGEALIAS ||1034command == SELFCERT ||1035command == STOREPASSWD ||1036command == KEYPASSWD ||1037command == IDENTITYDB) {1038int count = 0;1039do {1040if (command == IMPORTKEYSTORE) {1041System.err.print1042(rb.getString("Enter.destination.keystore.password."));1043} else {1044System.err.print1045(rb.getString("Enter.keystore.password."));1046}1047System.err.flush();1048storePass = Password.readPassword(System.in);1049passwords.add(storePass);10501051// If we are creating a new non nullStream-based keystore,1052// insist that the password be at least 6 characters1053if (!nullStream && (storePass == null || storePass.length < 6)) {1054System.err.println(rb.getString1055("Keystore.password.is.too.short.must.be.at.least.6.characters"));1056storePass = null;1057}10581059// If the keystore file does not exist and needs to be1060// created, the storepass should be prompted twice.1061if (storePass != null && !nullStream && ksStream == null) {1062System.err.print(rb.getString("Re.enter.new.password."));1063char[] storePassAgain = Password.readPassword(System.in);1064passwords.add(storePassAgain);1065if (!Arrays.equals(storePass, storePassAgain)) {1066System.err.println1067(rb.getString("They.don.t.match.Try.again"));1068storePass = null;1069}1070}10711072count++;1073} while ((storePass == null) && count < 3);107410751076if (storePass == null) {1077System.err.println1078(rb.getString("Too.many.failures.try.later"));1079return;1080}1081} else {1082// here we have EXPORTCERT and LIST (info valid until STOREPASSWD)1083if (command != PRINTCRL && command != PRINTCERT) {1084System.err.print(rb.getString("Enter.keystore.password."));1085System.err.flush();1086storePass = Password.readPassword(System.in);1087passwords.add(storePass);1088}1089}1090}10911092// Now load a nullStream-based keystore,1093// or verify the integrity of an input stream-based keystore1094if (nullStream) {1095keyStore.load(null, storePass);1096} else if (ksStream != null) {1097// Reload with user-provided password1098try (FileInputStream fis = new FileInputStream(ksfile)) {1099keyStore.load(fis, storePass);1100}1101}1102}11031104if (storePass != null && P12KEYSTORE.equalsIgnoreCase(storetype)) {1105MessageFormat form = new MessageFormat(rb.getString(1106"Warning.Different.store.and.key.passwords.not.supported.for.PKCS12.KeyStores.Ignoring.user.specified.command.value."));1107if (keyPass != null && !Arrays.equals(storePass, keyPass)) {1108Object[] source = {"-keypass"};1109System.err.println(form.format(source));1110keyPass = storePass;1111}1112if (destKeyPass != null && !Arrays.equals(storePass, destKeyPass)) {1113Object[] source = {"-destkeypass"};1114System.err.println(form.format(source));1115destKeyPass = storePass;1116}1117}11181119// -trustcacerts can be specified on -importcert, -printcert or -printcrl.1120// Reset it so that warnings on CA cert will remain for other command.1121if (command != IMPORTCERT && command != PRINTCERT1122&& command != PRINTCRL) {1123trustcacerts = false;1124}11251126if (trustcacerts) {1127caks = KeyStoreUtil.getCacertsKeyStore();1128}11291130// Perform the specified command1131if (command == CERTREQ) {1132if (filename != null) {1133try (PrintStream ps = new PrintStream(new FileOutputStream1134(filename))) {1135doCertReq(alias, sigAlgName, ps);1136}1137} else {1138doCertReq(alias, sigAlgName, out);1139}1140if (verbose && filename != null) {1141MessageFormat form = new MessageFormat(rb.getString1142("Certification.request.stored.in.file.filename."));1143Object[] source = {filename};1144System.err.println(form.format(source));1145System.err.println(rb.getString("Submit.this.to.your.CA"));1146}1147} else if (command == DELETE) {1148doDeleteEntry(alias);1149kssave = true;1150} else if (command == EXPORTCERT) {1151if (filename != null) {1152try (PrintStream ps = new PrintStream(new FileOutputStream1153(filename))) {1154doExportCert(alias, ps);1155}1156} else {1157doExportCert(alias, out);1158}1159if (filename != null) {1160MessageFormat form = new MessageFormat(rb.getString1161("Certificate.stored.in.file.filename."));1162Object[] source = {filename};1163System.err.println(form.format(source));1164}1165} else if (command == GENKEYPAIR) {1166if (keyAlgName == null) {1167throw new Exception(rb.getString(1168"keyalg.option.missing.error"));1169}1170doGenKeyPair(alias, dname, keyAlgName, keysize, groupName, sigAlgName,1171signerAlias);1172kssave = true;1173} else if (command == GENSECKEY) {1174if (keyAlgName == null) {1175throw new Exception(rb.getString(1176"keyalg.option.missing.error"));1177}1178doGenSecretKey(alias, keyAlgName, keysize);1179kssave = true;1180} else if (command == IMPORTPASS) {1181if (keyAlgName == null) {1182keyAlgName = "PBE";1183}1184// password is stored as a secret key1185doGenSecretKey(alias, keyAlgName, keysize);1186kssave = true;1187} else if (command == IDENTITYDB) {1188if (filename != null) {1189try (InputStream inStream = new FileInputStream(filename)) {1190doImportIdentityDatabase(inStream);1191}1192} else {1193doImportIdentityDatabase(System.in);1194}1195} else if (command == IMPORTCERT) {1196InputStream inStream = System.in;1197if (filename != null) {1198inStream = new FileInputStream(filename);1199}1200String importAlias = (alias!=null)?alias:keyAlias;1201try {1202if (keyStore.entryInstanceOf(1203importAlias, KeyStore.PrivateKeyEntry.class)) {1204kssave = installReply(importAlias, inStream);1205if (kssave) {1206System.err.println(rb.getString1207("Certificate.reply.was.installed.in.keystore"));1208} else {1209System.err.println(rb.getString1210("Certificate.reply.was.not.installed.in.keystore"));1211}1212} else if (!keyStore.containsAlias(importAlias) ||1213keyStore.entryInstanceOf(importAlias,1214KeyStore.TrustedCertificateEntry.class)) {1215kssave = addTrustedCert(importAlias, inStream);1216if (kssave) {1217System.err.println(rb.getString1218("Certificate.was.added.to.keystore"));1219} else {1220System.err.println(rb.getString1221("Certificate.was.not.added.to.keystore"));1222}1223}1224} finally {1225if (inStream != System.in) {1226inStream.close();1227}1228}1229} else if (command == IMPORTKEYSTORE) {1230// When not in-place import, srcKeyStore is not loaded yet.1231if (srcKeyStore == null) {1232srcKeyStore = loadSourceKeyStore();1233}1234doImportKeyStore(srcKeyStore);1235kssave = true;1236} else if (command == KEYCLONE) {1237keyPassNew = newPass;12381239// added to make sure only key can go thru1240if (alias == null) {1241alias = keyAlias;1242}1243if (keyStore.containsAlias(alias) == false) {1244MessageFormat form = new MessageFormat1245(rb.getString("Alias.alias.does.not.exist"));1246Object[] source = {alias};1247throw new Exception(form.format(source));1248}1249if (!keyStore.entryInstanceOf(alias, KeyStore.PrivateKeyEntry.class)) {1250MessageFormat form = new MessageFormat(rb.getString(1251"Alias.alias.references.an.entry.type.that.is.not.a.private.key.entry.The.keyclone.command.only.supports.cloning.of.private.key"));1252Object[] source = {alias};1253throw new Exception(form.format(source));1254}12551256doCloneEntry(alias, dest, true); // Now everything can be cloned1257kssave = true;1258} else if (command == CHANGEALIAS) {1259if (alias == null) {1260alias = keyAlias;1261}1262doCloneEntry(alias, dest, false);1263// in PKCS11, clone a PrivateKeyEntry will delete the old one1264if (keyStore.containsAlias(alias)) {1265doDeleteEntry(alias);1266}1267kssave = true;1268} else if (command == KEYPASSWD) {1269keyPassNew = newPass;1270doChangeKeyPasswd(alias);1271kssave = true;1272} else if (command == LIST) {1273if (storePass == null1274&& !KeyStoreUtil.isWindowsKeyStore(storetype)1275&& !isPasswordlessKeyStore) {1276printNoIntegrityWarning();1277}12781279if (alias != null) {1280doPrintEntry(rb.getString("the.certificate"), alias, out);1281} else {1282doPrintEntries(out);1283}1284} else if (command == PRINTCERT) {1285doPrintCert(out);1286} else if (command == SELFCERT) {1287doSelfCert(alias, dname, sigAlgName);1288kssave = true;1289} else if (command == STOREPASSWD) {1290doChangeStorePasswd();1291kssave = true;1292} else if (command == GENCERT) {1293if (alias == null) {1294alias = keyAlias;1295}1296InputStream inStream = System.in;1297if (infilename != null) {1298inStream = new FileInputStream(infilename);1299}1300PrintStream ps = null;1301if (outfilename != null) {1302ps = new PrintStream(new FileOutputStream(outfilename));1303out = ps;1304}1305try {1306doGenCert(alias, sigAlgName, inStream, out);1307} finally {1308if (inStream != System.in) {1309inStream.close();1310}1311if (ps != null) {1312ps.close();1313}1314}1315} else if (command == GENCRL) {1316if (alias == null) {1317alias = keyAlias;1318}1319if (filename != null) {1320try (PrintStream ps =1321new PrintStream(new FileOutputStream(filename))) {1322doGenCRL(ps);1323}1324} else {1325doGenCRL(out);1326}1327} else if (command == PRINTCERTREQ) {1328if (filename != null) {1329try (InputStream inStream = new FileInputStream(filename)) {1330doPrintCertReq(inStream, out);1331}1332} else {1333doPrintCertReq(System.in, out);1334}1335} else if (command == PRINTCRL) {1336doPrintCRL(filename, out);1337} else if (command == SHOWINFO) {1338doShowInfo();1339}13401341// If we need to save the keystore, do so.1342if (kssave) {1343if (verbose) {1344MessageFormat form = new MessageFormat1345(rb.getString(".Storing.ksfname."));1346Object[] source = {nullStream ? "keystore" : ksfname};1347System.err.println(form.format(source));1348}13491350if (token) {1351keyStore.store(null, null);1352} else {1353char[] pass = (storePassNew!=null) ? storePassNew : storePass;1354if (nullStream) {1355keyStore.store(null, pass);1356} else {1357ByteArrayOutputStream bout = new ByteArrayOutputStream();1358keyStore.store(bout, pass);1359try (FileOutputStream fout = new FileOutputStream(ksfname)) {1360fout.write(bout.toByteArray());1361}1362}1363}1364}13651366if (isKeyStoreRelated(command)1367&& !token && !nullStream && ksfname != null) {13681369// JKS storetype warning on the final result keystore1370File f = new File(ksfname);1371char[] pass = (storePassNew!=null) ? storePassNew : storePass;1372if (f.exists()) {1373// Probe for real type. A JKS can be loaded as PKCS12 because1374// DualFormat support, vice versa.1375String realType = storetype;1376try {1377keyStore = KeyStore.getInstance(f, pass);1378realType = keyStore.getType();1379if (realType.equalsIgnoreCase("JKS")1380|| realType.equalsIgnoreCase("JCEKS")) {1381boolean allCerts = true;1382for (String a : Collections.list(keyStore.aliases())) {1383if (!keyStore.entryInstanceOf(1384a, TrustedCertificateEntry.class)) {1385allCerts = false;1386break;1387}1388}1389// Don't warn for "cacerts" style keystore.1390if (!allCerts) {1391weakWarnings.add(String.format(1392rb.getString("jks.storetype.warning"),1393realType, ksfname));1394}1395}1396} catch (KeyStoreException e) {1397// Probing not supported, therefore cannot be JKS or JCEKS.1398// Skip the legacy type warning at all.1399}1400if (inplaceImport) {1401String realSourceStoreType = srcstoretype;1402try {1403realSourceStoreType = KeyStore.getInstance(1404new File(inplaceBackupName), srcstorePass).getType();1405} catch (KeyStoreException e) {1406// Probing not supported. Assuming srcstoretype.1407}1408String format =1409realType.equalsIgnoreCase(realSourceStoreType) ?1410rb.getString("backup.keystore.warning") :1411rb.getString("migrate.keystore.warning");1412weakWarnings.add(1413String.format(format,1414srcksfname,1415realSourceStoreType,1416inplaceBackupName,1417realType));1418}1419}1420}1421}14221423/**1424* Generate a certificate: Read PKCS10 request from in, and print1425* certificate to out. Use alias as CA, sigAlgName as the signature1426* type.1427*/1428private void doGenCert(String alias, String sigAlgName, InputStream in, PrintStream out)1429throws Exception {143014311432if (keyStore.containsAlias(alias) == false) {1433MessageFormat form = new MessageFormat1434(rb.getString("Alias.alias.does.not.exist"));1435Object[] source = {alias};1436throw new Exception(form.format(source));1437}1438Certificate signerCert = keyStore.getCertificate(alias);1439byte[] encoded = signerCert.getEncoded();1440X509CertImpl signerCertImpl = new X509CertImpl(encoded);1441X509CertInfo signerCertInfo = (X509CertInfo)signerCertImpl.get(1442X509CertImpl.NAME + "." + X509CertImpl.INFO);1443X500Name issuer = (X500Name)signerCertInfo.get(X509CertInfo.SUBJECT + "." +1444X509CertInfo.DN_NAME);14451446Date firstDate = getStartDate(startDate);1447Date lastDate = getLastDate(firstDate, validity);1448CertificateValidity interval = new CertificateValidity(firstDate,1449lastDate);14501451PrivateKey privateKey =1452(PrivateKey)recoverKey(alias, storePass, keyPass).fst;1453if (sigAlgName == null) {1454sigAlgName = getCompatibleSigAlgName(privateKey);1455}1456X509CertInfo info = new X509CertInfo();1457info.set(X509CertInfo.VALIDITY, interval);1458info.set(X509CertInfo.SERIAL_NUMBER,1459CertificateSerialNumber.newRandom64bit(new SecureRandom()));1460info.set(X509CertInfo.VERSION,1461new CertificateVersion(CertificateVersion.V3));1462info.set(X509CertInfo.ISSUER, issuer);14631464BufferedReader reader = new BufferedReader(new InputStreamReader(in));1465boolean canRead = false;1466StringBuilder sb = new StringBuilder();1467while (true) {1468String s = reader.readLine();1469if (s == null) break;1470// OpenSSL does not use NEW1471//if (s.startsWith("-----BEGIN NEW CERTIFICATE REQUEST-----")) {1472if (s.startsWith("-----BEGIN") && s.indexOf("REQUEST") >= 0) {1473canRead = true;1474//} else if (s.startsWith("-----END NEW CERTIFICATE REQUEST-----")) {1475} else if (s.startsWith("-----END") && s.indexOf("REQUEST") >= 0) {1476break;1477} else if (canRead) {1478sb.append(s);1479}1480}1481byte[] rawReq = Pem.decode(new String(sb));1482PKCS10 req = new PKCS10(rawReq);14831484checkWeak(rb.getString("the.certificate.request"), req);14851486info.set(X509CertInfo.KEY, new CertificateX509Key(req.getSubjectPublicKeyInfo()));1487info.set(X509CertInfo.SUBJECT,1488dname==null?req.getSubjectName():new X500Name(dname));1489CertificateExtensions reqex = null;1490Iterator<PKCS10Attribute> attrs = req.getAttributes().getAttributes().iterator();1491while (attrs.hasNext()) {1492PKCS10Attribute attr = attrs.next();1493if (attr.getAttributeId().equals(PKCS9Attribute.EXTENSION_REQUEST_OID)) {1494reqex = (CertificateExtensions)attr.getAttributeValue();1495}1496}14971498PublicKey subjectPubKey = req.getSubjectPublicKeyInfo();1499PublicKey issuerPubKey = signerCert.getPublicKey();15001501KeyIdentifier signerSubjectKeyId;1502if (Arrays.equals(subjectPubKey.getEncoded(), issuerPubKey.getEncoded())) {1503// No AKID for self-signed cert1504signerSubjectKeyId = null;1505} else {1506X509CertImpl certImpl;1507if (signerCert instanceof X509CertImpl) {1508certImpl = (X509CertImpl) signerCert;1509} else {1510certImpl = new X509CertImpl(signerCert.getEncoded());1511}15121513// To enforce compliance with RFC 5280 section 4.2.1.1: "Where a key1514// identifier has been previously established, the CA SHOULD use the1515// previously established identifier."1516// Use issuer's SKID to establish the AKID in createV3Extensions() method.1517signerSubjectKeyId = certImpl.getSubjectKeyId();15181519if (signerSubjectKeyId == null) {1520signerSubjectKeyId = new KeyIdentifier(issuerPubKey);1521}1522}15231524CertificateExtensions ext = createV3Extensions(1525reqex,1526null,1527v3ext,1528subjectPubKey,1529signerSubjectKeyId);1530info.set(X509CertInfo.EXTENSIONS, ext);1531X509CertImpl cert = new X509CertImpl(info);1532cert.sign(privateKey, sigAlgName);1533dumpCert(cert, out);1534for (Certificate ca: keyStore.getCertificateChain(alias)) {1535if (ca instanceof X509Certificate) {1536X509Certificate xca = (X509Certificate)ca;1537if (!KeyStoreUtil.isSelfSigned(xca)) {1538dumpCert(xca, out);1539}1540}1541}15421543checkWeak(rb.getString("the.issuer"), keyStore.getCertificateChain(alias));1544checkWeak(rb.getString("the.generated.certificate"), cert);1545}15461547private void doGenCRL(PrintStream out)1548throws Exception {1549if (ids == null) {1550throw new Exception("Must provide -id when -gencrl");1551}1552Certificate signerCert = keyStore.getCertificate(alias);1553byte[] encoded = signerCert.getEncoded();1554X509CertImpl signerCertImpl = new X509CertImpl(encoded);1555X509CertInfo signerCertInfo = (X509CertInfo)signerCertImpl.get(1556X509CertImpl.NAME + "." + X509CertImpl.INFO);1557X500Name owner = (X500Name)signerCertInfo.get(X509CertInfo.SUBJECT + "." +1558X509CertInfo.DN_NAME);15591560Date firstDate = getStartDate(startDate);1561Date lastDate = getLastDate(firstDate, validity);1562CertificateValidity interval = new CertificateValidity(firstDate,1563lastDate);15641565PrivateKey privateKey =1566(PrivateKey)recoverKey(alias, storePass, keyPass).fst;1567if (sigAlgName == null) {1568sigAlgName = getCompatibleSigAlgName(privateKey);1569}15701571X509CRLEntry[] badCerts = new X509CRLEntry[ids.size()];1572for (int i=0; i<ids.size(); i++) {1573String id = ids.get(i);1574int d = id.indexOf(':');1575if (d >= 0) {1576CRLExtensions ext = new CRLExtensions();1577ext.set("Reason", new CRLReasonCodeExtension(Integer.parseInt(id.substring(d+1))));1578badCerts[i] = new X509CRLEntryImpl(new BigInteger(id.substring(0, d)),1579firstDate, ext);1580} else {1581badCerts[i] = new X509CRLEntryImpl(new BigInteger(ids.get(i)), firstDate);1582}1583}1584X509CRLImpl crl = new X509CRLImpl(owner, firstDate, lastDate, badCerts);1585crl.sign(privateKey, sigAlgName);1586if (rfc) {1587out.println("-----BEGIN X509 CRL-----");1588out.println(Base64.getMimeEncoder(64, CRLF).encodeToString(crl.getEncodedInternal()));1589out.println("-----END X509 CRL-----");1590} else {1591out.write(crl.getEncodedInternal());1592}1593checkWeak(rb.getString("the.generated.crl"), crl, privateKey);1594}15951596/**1597* Creates a PKCS#10 cert signing request, corresponding to the1598* keys (and name) associated with a given alias.1599*/1600private void doCertReq(String alias, String sigAlgName, PrintStream out)1601throws Exception1602{1603if (alias == null) {1604alias = keyAlias;1605}16061607Pair<Key,char[]> objs = recoverKey(alias, storePass, keyPass);1608PrivateKey privKey = (PrivateKey)objs.fst;1609if (keyPass == null) {1610keyPass = objs.snd;1611}16121613Certificate cert = keyStore.getCertificate(alias);1614if (cert == null) {1615MessageFormat form = new MessageFormat1616(rb.getString("alias.has.no.public.key.certificate."));1617Object[] source = {alias};1618throw new Exception(form.format(source));1619}1620PKCS10 request = new PKCS10(cert.getPublicKey());1621CertificateExtensions ext = createV3Extensions(null, null, v3ext, cert.getPublicKey(), null);1622// Attribute name is not significant1623request.getAttributes().setAttribute(X509CertInfo.EXTENSIONS,1624new PKCS10Attribute(PKCS9Attribute.EXTENSION_REQUEST_OID, ext));16251626// Construct a Signature object, so that we can sign the request1627if (sigAlgName == null) {1628sigAlgName = getCompatibleSigAlgName(privKey);1629}16301631X500Name subject = dname == null?1632new X500Name(((X509Certificate)cert).getSubjectX500Principal().getEncoded()):1633new X500Name(dname);16341635// Sign the request and base-64 encode it1636request.encodeAndSign(subject, privKey, sigAlgName);1637request.print(out);16381639checkWeak(rb.getString("the.generated.certificate.request"), request);1640}16411642/**1643* Deletes an entry from the keystore.1644*/1645private void doDeleteEntry(String alias) throws Exception {1646if (keyStore.containsAlias(alias) == false) {1647MessageFormat form = new MessageFormat1648(rb.getString("Alias.alias.does.not.exist"));1649Object[] source = {alias};1650throw new Exception(form.format(source));1651}1652keyStore.deleteEntry(alias);1653}16541655/**1656* Exports a certificate from the keystore.1657*/1658private void doExportCert(String alias, PrintStream out)1659throws Exception1660{1661if (storePass == null1662&& !KeyStoreUtil.isWindowsKeyStore(storetype)1663&& !isPasswordlessKeyStore) {1664printNoIntegrityWarning();1665}1666if (alias == null) {1667alias = keyAlias;1668}1669if (keyStore.containsAlias(alias) == false) {1670MessageFormat form = new MessageFormat1671(rb.getString("Alias.alias.does.not.exist"));1672Object[] source = {alias};1673throw new Exception(form.format(source));1674}16751676X509Certificate cert = (X509Certificate)keyStore.getCertificate(alias);1677if (cert == null) {1678MessageFormat form = new MessageFormat1679(rb.getString("Alias.alias.has.no.certificate"));1680Object[] source = {alias};1681throw new Exception(form.format(source));1682}1683dumpCert(cert, out);1684checkWeak(rb.getString("the.certificate"), cert);1685}16861687/**1688* Prompt the user for a keypass when generating a key entry.1689* @param alias the entry we will set password for1690* @param orig the original entry of doing a dup, null if generate new1691* @param origPass the password to copy from if user press ENTER1692*/1693private char[] promptForKeyPass(String alias, String orig, char[] origPass) throws Exception{1694if (origPass != null && P12KEYSTORE.equalsIgnoreCase(storetype)) {1695return origPass;1696} else if (!token && !protectedPath) {1697// Prompt for key password1698int count;1699for (count = 0; count < 3; count++) {1700MessageFormat form = new MessageFormat(rb.getString1701("Enter.key.password.for.alias."));1702Object[] source = {alias};1703System.err.print(form.format(source));1704if (origPass != null) {1705System.err.println();1706if (orig == null) {1707System.err.print(rb.getString1708(".RETURN.if.same.as.keystore.password."));1709} else {1710form = new MessageFormat(rb.getString1711(".RETURN.if.same.as.for.otherAlias."));1712Object[] src = {orig};1713System.err.print(form.format(src));1714}1715}1716System.err.flush();1717char[] entered = Password.readPassword(System.in);1718passwords.add(entered);1719if (entered == null && origPass != null) {1720return origPass;1721} else if (entered != null && entered.length >= 6) {1722System.err.print(rb.getString("Re.enter.new.password."));1723char[] passAgain = Password.readPassword(System.in);1724passwords.add(passAgain);1725if (!Arrays.equals(entered, passAgain)) {1726System.err.println1727(rb.getString("They.don.t.match.Try.again"));1728continue;1729}1730return entered;1731} else {1732System.err.println(rb.getString1733("Key.password.is.too.short.must.be.at.least.6.characters"));1734}1735}1736if (count == 3) {1737if (command == KEYCLONE) {1738throw new Exception(rb.getString1739("Too.many.failures.Key.entry.not.cloned"));1740} else {1741throw new Exception(rb.getString1742("Too.many.failures.key.not.added.to.keystore"));1743}1744}1745}1746return null; // PKCS11, MSCAPI, or -protected1747}17481749/*1750* Prompt the user for the password credential to be stored.1751*/1752private char[] promptForCredential() throws Exception {1753// Handle password supplied via stdin1754if (System.console() == null) {1755char[] importPass = Password.readPassword(System.in);1756passwords.add(importPass);1757return importPass;1758}17591760int count;1761for (count = 0; count < 3; count++) {1762System.err.print(1763rb.getString("Enter.the.password.to.be.stored."));1764System.err.flush();1765char[] entered = Password.readPassword(System.in);1766passwords.add(entered);1767System.err.print(rb.getString("Re.enter.password."));1768char[] passAgain = Password.readPassword(System.in);1769passwords.add(passAgain);1770if (!Arrays.equals(entered, passAgain)) {1771System.err.println(rb.getString("They.don.t.match.Try.again"));1772continue;1773}1774return entered;1775}17761777if (count == 3) {1778throw new Exception(rb.getString1779("Too.many.failures.key.not.added.to.keystore"));1780}17811782return null;1783}17841785/**1786* Creates a new secret key.1787*/1788private void doGenSecretKey(String alias, String keyAlgName,1789int keysize)1790throws Exception1791{1792if (alias == null) {1793alias = keyAlias;1794}1795if (keyStore.containsAlias(alias)) {1796MessageFormat form = new MessageFormat(rb.getString1797("Secret.key.not.generated.alias.alias.already.exists"));1798Object[] source = {alias};1799throw new Exception(form.format(source));1800}18011802// Use the keystore's default PBE algorithm for entry protection1803boolean useDefaultPBEAlgorithm = true;1804SecretKey secKey = null;18051806if (keyAlgName.toUpperCase(Locale.ENGLISH).startsWith("PBE")) {1807SecretKeyFactory factory = SecretKeyFactory.getInstance("PBE");18081809// User is prompted for PBE credential1810secKey =1811factory.generateSecret(new PBEKeySpec(promptForCredential()));18121813// Check whether a specific PBE algorithm was specified1814if (!"PBE".equalsIgnoreCase(keyAlgName)) {1815useDefaultPBEAlgorithm = false;1816}18171818if (verbose) {1819MessageFormat form = new MessageFormat(rb.getString(1820"Generated.keyAlgName.secret.key"));1821Object[] source =1822{useDefaultPBEAlgorithm ? "PBE" : secKey.getAlgorithm()};1823System.err.println(form.format(source));1824}1825} else {1826KeyGenerator keygen = KeyGenerator.getInstance(keyAlgName);1827if (keysize == -1) {1828if ("DES".equalsIgnoreCase(keyAlgName)) {1829keysize = 56;1830} else if ("DESede".equalsIgnoreCase(keyAlgName)) {1831keysize = 168;1832} else {1833throw new Exception(rb.getString1834("Please.provide.keysize.for.secret.key.generation"));1835}1836}1837keygen.init(keysize);1838secKey = keygen.generateKey();18391840MessageFormat form = new MessageFormat(rb.getString1841("Generated.keysize.bit.keyAlgName.secret.key"));1842Object[] source = {keysize,1843secKey.getAlgorithm()};1844System.err.println(form.format(source));1845}18461847if (keyPass == null) {1848keyPass = promptForKeyPass(alias, null, storePass);1849}18501851if (useDefaultPBEAlgorithm) {1852keyStore.setKeyEntry(alias, secKey, keyPass, null);1853} else {1854keyStore.setEntry(alias, new KeyStore.SecretKeyEntry(secKey),1855new KeyStore.PasswordProtection(keyPass, keyAlgName, null));1856}1857}18581859/**1860* If no signature algorithm was specified at the command line,1861* we choose one that is compatible with the selected private key1862*/1863private static String getCompatibleSigAlgName(PrivateKey key)1864throws Exception {1865String result = SignatureUtil.getDefaultSigAlgForKey(key);1866if (result != null) {1867return result;1868} else {1869throw new Exception(rb.getString1870("Cannot.derive.signature.algorithm"));1871}1872}18731874/**1875* Creates a new key pair and self-signed certificate.1876*/1877private void doGenKeyPair(String alias, String dname, String keyAlgName,1878int keysize, String groupName, String sigAlgName,1879String signerAlias)1880throws Exception1881{1882if (groupName != null) {1883if (keysize != -1) {1884throw new Exception(rb.getString("groupname.keysize.coexist"));1885}1886} else {1887if (keysize == -1) {1888if ("EC".equalsIgnoreCase(keyAlgName)) {1889keysize = SecurityProviderConstants.DEF_EC_KEY_SIZE;1890} else if ("RSA".equalsIgnoreCase(keyAlgName)) {1891keysize = SecurityProviderConstants.DEF_RSA_KEY_SIZE;1892} else if ("DSA".equalsIgnoreCase(keyAlgName)) {1893keysize = SecurityProviderConstants.DEF_DSA_KEY_SIZE;1894} else if ("EdDSA".equalsIgnoreCase(keyAlgName)) {1895keysize = SecurityProviderConstants.DEF_ED_KEY_SIZE;1896} else if ("Ed25519".equalsIgnoreCase(keyAlgName)) {1897keysize = 255;1898} else if ("Ed448".equalsIgnoreCase(keyAlgName)) {1899keysize = 448;1900} else if ("XDH".equalsIgnoreCase(keyAlgName)) {1901keysize = SecurityProviderConstants.DEF_XEC_KEY_SIZE;1902} else if ("X25519".equalsIgnoreCase(keyAlgName)) {1903keysize = 255;1904} else if ("X448".equalsIgnoreCase(keyAlgName)) {1905keysize = 448;1906} else if ("DH".equalsIgnoreCase(keyAlgName)) {1907keysize = SecurityProviderConstants.DEF_DH_KEY_SIZE;1908}1909} else {1910if ("EC".equalsIgnoreCase(keyAlgName)) {1911weakWarnings.add(String.format(1912rb.getString("deprecate.keysize.for.ec"),1913ecGroupNameForSize(keysize)));1914}1915}1916}19171918if (alias == null) {1919alias = keyAlias;1920}19211922if (keyStore.containsAlias(alias)) {1923MessageFormat form = new MessageFormat(rb.getString1924("Key.pair.not.generated.alias.alias.already.exists"));1925Object[] source = {alias};1926throw new Exception(form.format(source));1927}19281929CertAndKeyGen keypair;1930KeyIdentifier signerSubjectKeyId = null;1931if (signerAlias != null) {1932PrivateKey signerPrivateKey =1933(PrivateKey)recoverKey(signerAlias, storePass, signerKeyPass).fst;1934Certificate signerCert = keyStore.getCertificate(signerAlias);19351936X509CertImpl signerCertImpl;1937if (signerCert instanceof X509CertImpl) {1938signerCertImpl = (X509CertImpl) signerCert;1939} else {1940signerCertImpl = new X509CertImpl(signerCert.getEncoded());1941}19421943X509CertInfo signerCertInfo = (X509CertInfo)signerCertImpl.get(1944X509CertImpl.NAME + "." + X509CertImpl.INFO);1945X500Name signerSubjectName = (X500Name)signerCertInfo.get(X509CertInfo.SUBJECT + "." +1946X509CertInfo.DN_NAME);19471948keypair = new CertAndKeyGen(keyAlgName, sigAlgName, providerName,1949signerPrivateKey, signerSubjectName);19501951signerSubjectKeyId = signerCertImpl.getSubjectKeyId();1952if (signerSubjectKeyId == null) {1953signerSubjectKeyId = new KeyIdentifier(signerCert.getPublicKey());1954}1955} else {1956keypair = new CertAndKeyGen(keyAlgName, sigAlgName, providerName);1957}19581959// If DN is provided, parse it. Otherwise, prompt the user for it.1960X500Name x500Name;1961if (dname == null) {1962printWeakWarnings(true);1963x500Name = getX500Name();1964} else {1965x500Name = new X500Name(dname);1966}19671968if (groupName != null) {1969keypair.generate(groupName);1970} else {1971// This covers keysize both specified and unspecified1972keypair.generate(keysize);1973}19741975CertificateExtensions ext = createV3Extensions(1976null,1977null,1978v3ext,1979keypair.getPublicKeyAnyway(),1980signerSubjectKeyId);19811982PrivateKey privKey = keypair.getPrivateKey();1983X509Certificate newCert = keypair.getSelfCertificate(1984x500Name, getStartDate(startDate), validity*24L*60L*60L, ext);19851986if (signerAlias != null) {1987MessageFormat form = new MessageFormat(rb.getString1988("Generating.keysize.bit.keyAlgName.key.pair.and.a.certificate.sigAlgName.issued.by.signerAlias.with.a.validity.of.validality.days.for"));1989Object[] source = {1990groupName == null ? keysize : KeyUtil.getKeySize(privKey),1991fullDisplayAlgName(privKey),1992newCert.getSigAlgName(),1993signerAlias,1994validity,1995x500Name};1996System.err.println(form.format(source));1997} else {1998MessageFormat form = new MessageFormat(rb.getString1999("Generating.keysize.bit.keyAlgName.key.pair.and.self.signed.certificate.sigAlgName.with.a.validity.of.validality.days.for"));2000Object[] source = {2001groupName == null ? keysize : KeyUtil.getKeySize(privKey),2002fullDisplayAlgName(privKey),2003newCert.getSigAlgName(),2004validity,2005x500Name};2006System.err.println(form.format(source));2007}20082009if (keyPass == null) {2010keyPass = promptForKeyPass(alias, null, storePass);2011}20122013Certificate[] finalChain;2014if (signerAlias != null) {2015Certificate[] signerChain = keyStore.getCertificateChain(signerAlias);2016finalChain = new X509Certificate[signerChain.length + 1];2017finalChain[0] = newCert;2018System.arraycopy(signerChain, 0, finalChain, 1, signerChain.length);2019} else {2020finalChain = new Certificate[] { newCert };2021}2022checkWeak(rb.getString("the.generated.certificate"), finalChain);2023keyStore.setKeyEntry(alias, privKey, keyPass, finalChain);2024}20252026private String ecGroupNameForSize(int size) throws Exception {2027AlgorithmParameters ap = AlgorithmParameters.getInstance("EC");2028ap.init(new ECKeySizeParameterSpec(size));2029// The following line assumes the toString value is "name (oid)"2030return ap.toString().split(" ")[0];2031}20322033/**2034* Clones an entry2035* @param orig original alias2036* @param dest destination alias2037* @changePassword if the password can be changed2038*/2039private void doCloneEntry(String orig, String dest, boolean changePassword)2040throws Exception2041{2042if (orig == null) {2043orig = keyAlias;2044}20452046if (keyStore.containsAlias(dest)) {2047MessageFormat form = new MessageFormat2048(rb.getString("Destination.alias.dest.already.exists"));2049Object[] source = {dest};2050throw new Exception(form.format(source));2051}20522053Pair<Entry,char[]> objs = recoverEntry(keyStore, orig, storePass, keyPass);2054Entry entry = objs.fst;2055keyPass = objs.snd;20562057PasswordProtection pp = null;20582059if (keyPass != null) { // protected2060if (!changePassword || P12KEYSTORE.equalsIgnoreCase(storetype)) {2061keyPassNew = keyPass;2062} else {2063if (keyPassNew == null) {2064keyPassNew = promptForKeyPass(dest, orig, keyPass);2065}2066}2067pp = new PasswordProtection(keyPassNew);2068}2069keyStore.setEntry(dest, entry, pp);2070}20712072/**2073* Changes a key password.2074*/2075private void doChangeKeyPasswd(String alias) throws Exception2076{20772078if (alias == null) {2079alias = keyAlias;2080}2081Pair<Key,char[]> objs = recoverKey(alias, storePass, keyPass);2082Key privKey = objs.fst;2083if (keyPass == null) {2084keyPass = objs.snd;2085}20862087if (keyPassNew == null) {2088MessageFormat form = new MessageFormat2089(rb.getString("key.password.for.alias."));2090Object[] source = {alias};2091keyPassNew = getNewPasswd(form.format(source), keyPass);2092}2093keyStore.setKeyEntry(alias, privKey, keyPassNew,2094keyStore.getCertificateChain(alias));2095}20962097/**2098* Imports a JDK 1.1-style identity database. We can only store one2099* certificate per identity, because we use the identity's name as the2100* alias (which references a keystore entry), and aliases must be unique.2101*/2102private void doImportIdentityDatabase(InputStream in)2103throws Exception2104{2105System.err.println(rb.getString2106("No.entries.from.identity.database.added"));2107}21082109/**2110* Prints a single keystore entry.2111*/2112private void doPrintEntry(String label, String alias, PrintStream out)2113throws Exception2114{2115if (keyStore.containsAlias(alias) == false) {2116MessageFormat form = new MessageFormat2117(rb.getString("Alias.alias.does.not.exist"));2118Object[] source = {alias};2119throw new Exception(form.format(source));2120}21212122if (verbose || rfc || debug) {2123MessageFormat form = new MessageFormat2124(rb.getString("Alias.name.alias"));2125Object[] source = {alias};2126out.println(form.format(source));21272128if (!token) {2129form = new MessageFormat(rb.getString2130("Creation.date.keyStore.getCreationDate.alias."));2131Object[] src = {keyStore.getCreationDate(alias)};2132out.println(form.format(src));2133}2134} else {2135if (!token) {2136MessageFormat form = new MessageFormat2137(rb.getString("alias.keyStore.getCreationDate.alias."));2138Object[] source = {alias, keyStore.getCreationDate(alias)};2139out.print(form.format(source));2140} else {2141MessageFormat form = new MessageFormat2142(rb.getString("alias."));2143Object[] source = {alias};2144out.print(form.format(source));2145}2146}21472148if (keyStore.entryInstanceOf(alias, KeyStore.SecretKeyEntry.class)) {2149if (verbose || rfc || debug) {2150Object[] source = {"SecretKeyEntry"};2151out.println(new MessageFormat(2152rb.getString("Entry.type.type.")).format(source));2153} else {2154out.println("SecretKeyEntry, ");2155}2156} else if (keyStore.entryInstanceOf(alias, KeyStore.PrivateKeyEntry.class)) {2157if (verbose || rfc || debug) {2158Object[] source = {"PrivateKeyEntry"};2159out.println(new MessageFormat(2160rb.getString("Entry.type.type.")).format(source));2161} else {2162out.println("PrivateKeyEntry, ");2163}21642165// Get the chain2166Certificate[] chain = keyStore.getCertificateChain(alias);2167if (chain != null) {2168if (verbose || rfc || debug) {2169out.println(rb.getString2170("Certificate.chain.length.") + chain.length);2171for (int i = 0; i < chain.length; i ++) {2172MessageFormat form = new MessageFormat2173(rb.getString("Certificate.i.1."));2174Object[] source = {(i + 1)};2175out.println(form.format(source));2176if (verbose && (chain[i] instanceof X509Certificate)) {2177printX509Cert((X509Certificate)(chain[i]), out);2178} else if (debug) {2179out.println(chain[i].toString());2180} else {2181dumpCert(chain[i], out);2182}2183checkWeak(label, chain[i]);2184}2185} else {2186// Print the digest of the user cert only2187out.println2188(rb.getString("Certificate.fingerprint.SHA.256.") +2189getCertFingerPrint("SHA-256", chain[0]));2190checkWeak(label, chain);2191}2192} else {2193out.println(rb.getString2194("Certificate.chain.length.") + 0);2195}2196} else if (keyStore.entryInstanceOf(alias,2197KeyStore.TrustedCertificateEntry.class)) {2198// We have a trusted certificate entry2199Certificate cert = keyStore.getCertificate(alias);2200Object[] source = {"trustedCertEntry"};2201String mf = new MessageFormat(2202rb.getString("Entry.type.type.")).format(source) + "\n";2203if (verbose && (cert instanceof X509Certificate)) {2204out.println(mf);2205printX509Cert((X509Certificate)cert, out);2206} else if (rfc) {2207out.println(mf);2208dumpCert(cert, out);2209} else if (debug) {2210for (var attr : keyStore.getEntry(alias, null).getAttributes()) {2211System.out.println("Attribute " + attr.getName() + ": " + attr.getValue());2212}2213out.println(cert.toString());2214} else {2215out.println("trustedCertEntry, ");2216out.println(rb.getString("Certificate.fingerprint.SHA.256.")2217+ getCertFingerPrint("SHA-256", cert));2218}2219checkWeak(label, cert);2220} else {2221out.println(rb.getString("Unknown.Entry.Type"));2222}2223}22242225boolean inplaceImportCheck() throws Exception {2226if (P11KEYSTORE.equalsIgnoreCase(srcstoretype) ||2227KeyStoreUtil.isWindowsKeyStore(srcstoretype)) {2228return false;2229}22302231if (srcksfname != null) {2232File srcksfile = new File(srcksfname);2233if (srcksfile.exists() && srcksfile.length() == 0) {2234throw new Exception(rb.getString2235("Source.keystore.file.exists.but.is.empty.") +2236srcksfname);2237}2238if (srcksfile.getCanonicalFile()2239.equals(new File(ksfname).getCanonicalFile())) {2240return true;2241} else {2242// Informational, especially if destkeystore is not2243// provided, which default to ~/.keystore.2244System.err.println(String.format(rb.getString(2245"importing.keystore.status"), srcksfname, ksfname));2246return false;2247}2248} else {2249throw new Exception(rb.getString2250("Please.specify.srckeystore"));2251}2252}22532254/**2255* Load the srckeystore from a stream, used in -importkeystore2256* @return the src KeyStore2257*/2258KeyStore loadSourceKeyStore() throws Exception {22592260InputStream is = null;2261File srcksfile = null;2262boolean srcIsPasswordless = false;22632264if (P11KEYSTORE.equalsIgnoreCase(srcstoretype) ||2265KeyStoreUtil.isWindowsKeyStore(srcstoretype)) {2266if (!NONE.equals(srcksfname)) {2267System.err.println(MessageFormat.format(rb.getString2268(".keystore.must.be.NONE.if.storetype.is.{0}"), srcstoretype));2269System.err.println();2270tinyHelp();2271}2272} else {2273srcksfile = new File(srcksfname);2274is = new FileInputStream(srcksfile);2275}22762277KeyStore store;2278try {2279// Probe for keystore type when filename is available2280if (srcksfile != null && is != null && srcProviderName == null &&2281srcstoretype == null) {2282store = KeyStore.getInstance(srcksfile, srcstorePass);2283srcstoretype = store.getType();2284if (srcstoretype.equalsIgnoreCase("pkcs12")) {2285srcIsPasswordless = PKCS12KeyStore.isPasswordless(srcksfile);2286}2287} else {2288if (srcstoretype == null) {2289srcstoretype = KeyStore.getDefaultType();2290}2291if (srcProviderName == null) {2292store = KeyStore.getInstance(srcstoretype);2293} else {2294store = KeyStore.getInstance(srcstoretype, srcProviderName);2295}2296}22972298if (srcstorePass == null2299&& !srcprotectedPath2300&& !KeyStoreUtil.isWindowsKeyStore(srcstoretype)2301&& !srcIsPasswordless) {2302System.err.print(rb.getString("Enter.source.keystore.password."));2303System.err.flush();2304srcstorePass = Password.readPassword(System.in);2305passwords.add(srcstorePass);2306}23072308// always let keypass be storepass when using pkcs122309if (P12KEYSTORE.equalsIgnoreCase(srcstoretype)) {2310if (srckeyPass != null && srcstorePass != null &&2311!Arrays.equals(srcstorePass, srckeyPass)) {2312MessageFormat form = new MessageFormat(rb.getString(2313"Warning.Different.store.and.key.passwords.not.supported.for.PKCS12.KeyStores.Ignoring.user.specified.command.value."));2314Object[] source = {"-srckeypass"};2315System.err.println(form.format(source));2316srckeyPass = srcstorePass;2317}2318}23192320store.load(is, srcstorePass); // "is" already null in PKCS112321} finally {2322if (is != null) {2323is.close();2324}2325}23262327if (srcstorePass == null2328&& !srcIsPasswordless2329&& !KeyStoreUtil.isWindowsKeyStore(srcstoretype)) {2330// anti refactoring, copied from printNoIntegrityWarning(),2331// but change 2 lines2332System.err.println();2333System.err.println(rb.getString2334(".WARNING.WARNING.WARNING."));2335System.err.println(rb.getString2336(".The.integrity.of.the.information.stored.in.the.srckeystore."));2337System.err.println(rb.getString2338(".WARNING.WARNING.WARNING."));2339System.err.println();2340}23412342return store;2343}23442345/**2346* import all keys and certs from importkeystore.2347* keep alias unchanged if no name conflict, otherwise, prompt.2348* keep keypass unchanged for keys2349*/2350private void doImportKeyStore(KeyStore srcKS) throws Exception {23512352if (alias != null) {2353doImportKeyStoreSingle(srcKS, alias);2354} else {2355if (dest != null || srckeyPass != null) {2356throw new Exception(rb.getString(2357"if.alias.not.specified.destalias.and.srckeypass.must.not.be.specified"));2358}2359doImportKeyStoreAll(srcKS);2360}23612362if (inplaceImport) {2363// Backup to file.old or file.old2...2364// The keystore is not rewritten yet now.2365for (int n = 1; /* forever */; n++) {2366inplaceBackupName = srcksfname + ".old" + (n == 1 ? "" : n);2367File bkFile = new File(inplaceBackupName);2368if (!bkFile.exists()) {2369Files.copy(Path.of(srcksfname), bkFile.toPath());2370break;2371}2372}23732374}23752376/*2377* Information display rule of -importkeystore2378* 1. inside single, shows failure2379* 2. inside all, shows sucess2380* 3. inside all where there is a failure, prompt for continue2381* 4. at the final of all, shows summary2382*/2383}23842385/**2386* Import a single entry named alias from srckeystore2387* @return 1 if the import action succeed2388* 0 if user choose to ignore an alias-dumplicated entry2389* 2 if setEntry throws Exception2390*/2391private int doImportKeyStoreSingle(KeyStore srckeystore, String alias)2392throws Exception {23932394String newAlias = (dest==null) ? alias : dest;23952396if (keyStore.containsAlias(newAlias)) {2397Object[] source = {alias};2398if (noprompt) {2399System.err.println(new MessageFormat(rb.getString(2400"Warning.Overwriting.existing.alias.alias.in.destination.keystore")).format(source));2401} else {2402String reply = getYesNoReply(new MessageFormat(rb.getString(2403"Existing.entry.alias.alias.exists.overwrite.no.")).format(source));2404if ("NO".equals(reply)) {2405newAlias = inputStringFromStdin(rb.getString2406("Enter.new.alias.name.RETURN.to.cancel.import.for.this.entry."));2407if ("".equals(newAlias)) {2408System.err.println(new MessageFormat(rb.getString(2409"Entry.for.alias.alias.not.imported.")).format(2410source));2411return 0;2412}2413}2414}2415}24162417Pair<Entry,char[]> objs = recoverEntry(srckeystore, alias, srcstorePass, srckeyPass);2418Entry entry = objs.fst;24192420PasswordProtection pp = null;24212422// According to keytool.html, "The destination entry will be protected2423// using destkeypass. If destkeypass is not provided, the destination2424// entry will be protected with the source entry password."2425// so always try to protect with destKeyPass.2426char[] newPass = null;2427if (destKeyPass != null) {2428newPass = destKeyPass;2429pp = new PasswordProtection(destKeyPass);2430} else if (objs.snd != null) {2431if (P12KEYSTORE.equalsIgnoreCase(storetype)) {2432if (isPasswordlessKeyStore) {2433newPass = objs.snd;2434} else {2435newPass = storePass;2436}2437} else {2438newPass = objs.snd;2439}2440pp = new PasswordProtection(newPass);2441}24422443try {2444Certificate c = srckeystore.getCertificate(alias);2445if (c != null) {2446checkWeak("<" + newAlias + ">", c);2447}2448keyStore.setEntry(newAlias, entry, pp);2449// Place the check so that only successful imports are blocked.2450// For example, we don't block a failed SecretEntry import.2451if (P12KEYSTORE.equalsIgnoreCase(storetype) && !isPasswordlessKeyStore) {2452if (newPass != null && !Arrays.equals(newPass, storePass)) {2453throw new Exception(rb.getString(2454"The.destination.pkcs12.keystore.has.different.storepass.and.keypass.Please.retry.with.destkeypass.specified."));2455}2456}2457return 1;2458} catch (KeyStoreException kse) {2459Object[] source2 = {alias, kse.toString()};2460MessageFormat form = new MessageFormat(rb.getString(2461"Problem.importing.entry.for.alias.alias.exception.Entry.for.alias.alias.not.imported."));2462System.err.println(form.format(source2));2463return 2;2464}2465}24662467private void doImportKeyStoreAll(KeyStore srckeystore) throws Exception {24682469int ok = 0;2470int count = srckeystore.size();2471for (Enumeration<String> e = srckeystore.aliases();2472e.hasMoreElements(); ) {2473String alias = e.nextElement();2474int result = doImportKeyStoreSingle(srckeystore, alias);2475if (result == 1) {2476ok++;2477Object[] source = {alias};2478MessageFormat form = new MessageFormat(rb.getString("Entry.for.alias.alias.successfully.imported."));2479System.err.println(form.format(source));2480} else if (result == 2) {2481if (!noprompt) {2482String reply = getYesNoReply("Do you want to quit the import process? [no]: ");2483if ("YES".equals(reply)) {2484break;2485}2486}2487}2488}2489Object[] source = {ok, count-ok};2490MessageFormat form = new MessageFormat(rb.getString(2491"Import.command.completed.ok.entries.successfully.imported.fail.entries.failed.or.cancelled"));2492System.err.println(form.format(source));2493}24942495/**2496* Prints all keystore entries.2497*/2498private void doPrintEntries(PrintStream out)2499throws Exception2500{2501out.println(rb.getString("Keystore.type.") + keyStore.getType());2502out.println(rb.getString("Keystore.provider.") +2503keyStore.getProvider().getName());2504out.println();25052506MessageFormat form;2507form = (keyStore.size() == 1) ?2508new MessageFormat(rb.getString2509("Your.keystore.contains.keyStore.size.entry")) :2510new MessageFormat(rb.getString2511("Your.keystore.contains.keyStore.size.entries"));2512Object[] source = {keyStore.size()};2513out.println(form.format(source));2514out.println();25152516List<String> aliases = Collections.list(keyStore.aliases());2517aliases.sort(String::compareTo);2518for (String alias : aliases) {2519doPrintEntry("<" + alias + ">", alias, out);2520if (verbose || rfc) {2521out.println(rb.getString("NEWLINE"));2522out.println(rb.getString2523("STAR"));2524out.println(rb.getString2525("STARNN"));2526}2527}2528}25292530/**2531* Loads CRLs from a source. This method is also called in JarSigner.2532* @param src the source, which means System.in if null, or a URI,2533* or a bare file path name2534*/2535public static Collection<? extends CRL> loadCRLs(String src) throws Exception {2536InputStream in = null;2537URI uri = null;2538if (src == null) {2539in = System.in;2540} else {2541try {2542uri = new URI(src);2543if (uri.getScheme().equals("ldap")) {2544// No input stream for LDAP2545} else {2546in = uri.toURL().openStream();2547}2548} catch (Exception e) {2549try {2550in = new FileInputStream(src);2551} catch (Exception e2) {2552if (uri == null || uri.getScheme() == null) {2553throw e2; // More likely a bare file path2554} else {2555throw e; // More likely a protocol or network problem2556}2557}2558}2559}2560if (in != null) {2561try {2562// Read the full stream before feeding to X509Factory,2563// otherwise, keytool -gencrl | keytool -printcrl2564// might not work properly, since -gencrl is slow2565// and there's no data in the pipe at the beginning.2566byte[] bytes = in.readAllBytes();2567return CertificateFactory.getInstance("X509").generateCRLs(2568new ByteArrayInputStream(bytes));2569} finally {2570if (in != System.in) {2571in.close();2572}2573}2574} else { // must be LDAP, and uri is not null2575URICertStoreParameters params =2576new URICertStoreParameters(uri);2577CertStore s = CertStore.getInstance("LDAP", params);2578return s.getCRLs(new X509CRLSelector());2579}2580}25812582/**2583* Returns CRLs described in a X509Certificate's CRLDistributionPoints2584* Extension. Only those containing a general name of type URI are read.2585*/2586public static List<CRL> readCRLsFromCert(X509Certificate cert)2587throws Exception {2588List<CRL> crls = new ArrayList<>();2589CRLDistributionPointsExtension ext =2590X509CertImpl.toImpl(cert).getCRLDistributionPointsExtension();2591if (ext == null) return crls;2592List<DistributionPoint> distPoints =2593ext.get(CRLDistributionPointsExtension.POINTS);2594for (DistributionPoint o: distPoints) {2595GeneralNames names = o.getFullName();2596if (names != null) {2597for (GeneralName name: names.names()) {2598if (name.getType() == GeneralNameInterface.NAME_URI) {2599URIName uriName = (URIName)name.getName();2600for (CRL crl: loadCRLs(uriName.getName())) {2601if (crl instanceof X509CRL) {2602crls.add((X509CRL)crl);2603}2604}2605break; // Different name should point to same CRL2606}2607}2608}2609}2610return crls;2611}26122613private static String verifyCRL(KeyStore ks, CRL crl)2614throws Exception {2615X509CRL xcrl = (X509CRL)crl;2616X500Principal issuer = xcrl.getIssuerX500Principal();2617for (String s: Collections.list(ks.aliases())) {2618Certificate cert = ks.getCertificate(s);2619if (cert instanceof X509Certificate) {2620X509Certificate xcert = (X509Certificate)cert;2621if (xcert.getSubjectX500Principal().equals(issuer)) {2622try {2623((X509CRL)crl).verify(cert.getPublicKey());2624return s;2625} catch (Exception e) {2626}2627}2628}2629}2630return null;2631}26322633private void doPrintCRL(String src, PrintStream out)2634throws Exception {2635for (CRL crl: loadCRLs(src)) {2636printCRL(crl, out);2637String issuer = null;2638Certificate signer = null;2639if (caks != null) {2640issuer = verifyCRL(caks, crl);2641if (issuer != null) {2642signer = caks.getCertificate(issuer);2643out.printf(rb.getString(2644"verified.by.s.in.s.weak"),2645issuer,2646"cacerts",2647withWeak(signer.getPublicKey()));2648out.println();2649}2650}2651if (issuer == null && keyStore != null) {2652issuer = verifyCRL(keyStore, crl);2653if (issuer != null) {2654signer = keyStore.getCertificate(issuer);2655out.printf(rb.getString(2656"verified.by.s.in.s.weak"),2657issuer,2658"keystore",2659withWeak(signer.getPublicKey()));2660out.println();2661}2662}2663if (issuer == null) {2664out.println(rb.getString2665("STAR"));2666if (trustcacerts) {2667out.println(rb.getString2668("warning.not.verified.make.sure.keystore.is.correct"));2669} else {2670out.println(rb.getString2671("warning.not.verified.make.sure.keystore.is.correct.or.specify.trustcacerts"));2672}2673out.println(rb.getString2674("STARNN"));2675}2676checkWeak(rb.getString("the.crl"), crl, signer == null ? null : signer.getPublicKey());2677}2678}26792680private void printCRL(CRL crl, PrintStream out)2681throws Exception {2682X509CRL xcrl = (X509CRL)crl;2683if (rfc) {2684out.println("-----BEGIN X509 CRL-----");2685out.println(Base64.getMimeEncoder(64, CRLF).encodeToString(xcrl.getEncoded()));2686out.println("-----END X509 CRL-----");2687} else {2688String s;2689if (crl instanceof X509CRLImpl) {2690X509CRLImpl x509crl = (X509CRLImpl) crl;2691s = x509crl.toStringWithAlgName(withWeak("" + x509crl.getSigAlgId()));2692} else {2693s = crl.toString();2694}2695out.println(s);2696}2697}26982699private void doPrintCertReq(InputStream in, PrintStream out)2700throws Exception {27012702BufferedReader reader = new BufferedReader(new InputStreamReader(in));2703StringBuilder sb = new StringBuilder();2704boolean started = false;2705while (true) {2706String s = reader.readLine();2707if (s == null) break;2708if (!started) {2709if (s.startsWith("-----")) {2710started = true;2711}2712} else {2713if (s.startsWith("-----")) {2714break;2715}2716sb.append(s);2717}2718}2719PKCS10 req = new PKCS10(Pem.decode(new String(sb)));27202721PublicKey pkey = req.getSubjectPublicKeyInfo();2722out.printf(rb.getString("PKCS.10.with.weak"),2723req.getSubjectName(),2724pkey.getFormat(),2725withWeak(pkey),2726withWeak(req.getSigAlg()));2727for (PKCS10Attribute attr: req.getAttributes().getAttributes()) {2728ObjectIdentifier oid = attr.getAttributeId();2729if (oid.equals(PKCS9Attribute.EXTENSION_REQUEST_OID)) {2730CertificateExtensions exts = (CertificateExtensions)attr.getAttributeValue();2731if (exts != null) {2732printExtensions(rb.getString("Extension.Request."), exts, out);2733}2734} else {2735out.println("Attribute: " + attr.getAttributeId());2736PKCS9Attribute pkcs9Attr =2737new PKCS9Attribute(attr.getAttributeId(),2738attr.getAttributeValue());2739out.print(pkcs9Attr.getName() + ": ");2740Object attrVal = attr.getAttributeValue();2741out.println(attrVal instanceof String[] ?2742Arrays.toString((String[]) attrVal) :2743attrVal);2744}2745}2746if (debug) {2747out.println(req); // Just to see more, say, public key length...2748}2749checkWeak(rb.getString("the.certificate.request"), req);2750}27512752/**2753* Reads a certificate (or certificate chain) and prints its contents in2754* a human readable format.2755*/2756private void printCertFromStream(InputStream in, PrintStream out)2757throws Exception2758{2759Collection<? extends Certificate> c = null;2760try {2761c = generateCertificates(in);2762} catch (CertificateException ce) {2763throw new Exception(rb.getString("Failed.to.parse.input"), ce);2764}2765if (c.isEmpty()) {2766throw new Exception(rb.getString("Empty.input"));2767}2768Certificate[] certs = c.toArray(new Certificate[c.size()]);2769for (int i=0; i<certs.length; i++) {2770X509Certificate x509Cert = null;2771try {2772x509Cert = (X509Certificate)certs[i];2773} catch (ClassCastException cce) {2774throw new Exception(rb.getString("Not.X.509.certificate"));2775}2776if (certs.length > 1) {2777MessageFormat form = new MessageFormat2778(rb.getString("Certificate.i.1."));2779Object[] source = {i + 1};2780out.println(form.format(source));2781}2782if (rfc)2783dumpCert(x509Cert, out);2784else2785printX509Cert(x509Cert, out);2786if (i < (certs.length-1)) {2787out.println();2788}2789checkWeak(oneInMany(rb.getString("the.certificate"), i, certs.length), x509Cert);2790}2791}27922793private void doShowInfo() throws Exception {2794if (tlsInfo) {2795ShowInfo.tls(verbose);2796} else {2797System.out.println(rb.getString("showinfo.no.option"));2798}2799}28002801private Collection<? extends Certificate> generateCertificates(InputStream in)2802throws CertificateException, IOException {2803byte[] data = in.readAllBytes();2804try {2805return CertificateFactory.getInstance("X.509")2806.generateCertificates(new ByteArrayInputStream(data));2807} catch (CertificateException e) {2808if (providerName != null) {2809try {2810return CertificateFactory.getInstance("X.509", providerName)2811.generateCertificates(new ByteArrayInputStream(data));2812} catch (Exception e2) {2813e.addSuppressed(e2);2814}2815}2816throw e;2817}2818}28192820private Certificate generateCertificate(InputStream in)2821throws CertificateException, IOException {2822byte[] data = in.readAllBytes();2823try {2824return CertificateFactory.getInstance("X.509")2825.generateCertificate(new ByteArrayInputStream(data));2826} catch (CertificateException e) {2827if (providerName != null) {2828try {2829return CertificateFactory.getInstance("X.509", providerName)2830.generateCertificate(new ByteArrayInputStream(data));2831} catch (Exception e2) {2832e.addSuppressed(e2);2833}2834}2835throw e;2836}2837}28382839private static String oneInMany(String label, int i, int num) {2840if (num == 1) {2841return label;2842} else {2843return String.format(rb.getString("one.in.many"), label, i+1, num);2844}2845}28462847private void doPrintCert(final PrintStream out) throws Exception {2848if (jarfile != null) {2849// reset "jdk.certpath.disabledAlgorithms" security property2850// to be able to read jars which were signed with weak algorithms2851Security.setProperty(DisabledAlgorithmConstraints.PROPERTY_JAR_DISABLED_ALGS, "");28522853JarFile jf = new JarFile(jarfile, true);2854Enumeration<JarEntry> entries = jf.entries();2855Set<CodeSigner> ss = new HashSet<>();2856byte[] buffer = new byte[8192];2857int pos = 0;2858while (entries.hasMoreElements()) {2859JarEntry je = entries.nextElement();2860try (InputStream is = jf.getInputStream(je)) {2861while (is.read(buffer) != -1) {2862// we just read. this will throw a SecurityException2863// if a signature/digest check fails. This also2864// populate the signers2865}2866}2867CodeSigner[] signers = je.getCodeSigners();2868if (signers != null) {2869for (CodeSigner signer: signers) {2870if (!ss.contains(signer)) {2871ss.add(signer);2872out.printf(rb.getString("Signer.d."), ++pos);2873out.println();2874out.println();2875out.println(rb.getString("Signature."));2876out.println();28772878List<? extends Certificate> certs2879= signer.getSignerCertPath().getCertificates();2880int cc = 0;2881for (Certificate cert: certs) {2882X509Certificate x = (X509Certificate)cert;2883if (rfc) {2884out.println(rb.getString("Certificate.owner.") + x.getSubjectX500Principal() + "\n");2885dumpCert(x, out);2886} else {2887printX509Cert(x, out);2888}2889out.println();2890checkWeak(oneInMany(rb.getString("the.certificate"), cc++, certs.size()), x);2891}2892Timestamp ts = signer.getTimestamp();2893if (ts != null) {2894out.println(rb.getString("Timestamp."));2895out.println();2896certs = ts.getSignerCertPath().getCertificates();2897cc = 0;2898for (Certificate cert: certs) {2899X509Certificate x = (X509Certificate)cert;2900if (rfc) {2901out.println(rb.getString("Certificate.owner.") + x.getSubjectX500Principal() + "\n");2902dumpCert(x, out);2903} else {2904printX509Cert(x, out);2905}2906out.println();2907checkWeak(oneInMany(rb.getString("the.tsa.certificate"), cc++, certs.size()), x);2908}2909}2910}2911}2912}2913}2914jf.close();2915if (ss.isEmpty()) {2916out.println(rb.getString("Not.a.signed.jar.file"));2917}2918} else if (sslserver != null) {2919CertStore cs = SSLServerCertStore.getInstance(new URI("https://" + sslserver));2920Collection<? extends Certificate> chain;2921try {2922chain = cs.getCertificates(null);2923if (chain.isEmpty()) {2924// If the certs are not retrieved, we consider it an error2925// even if the URL connection is successful.2926throw new Exception(rb.getString(2927"No.certificate.from.the.SSL.server"));2928}2929} catch (CertStoreException cse) {2930if (cse.getCause() instanceof IOException) {2931throw new Exception(rb.getString(2932"No.certificate.from.the.SSL.server"),2933cse.getCause());2934} else {2935throw cse;2936}2937}29382939int i = 0;2940for (Certificate cert : chain) {2941try {2942if (rfc) {2943dumpCert(cert, out);2944} else {2945out.println("Certificate #" + i);2946out.println("====================================");2947printX509Cert((X509Certificate)cert, out);2948out.println();2949}2950checkWeak(oneInMany(rb.getString("the.certificate"), i++, chain.size()), cert);2951} catch (Exception e) {2952if (debug) {2953e.printStackTrace();2954}2955}2956}2957} else {2958if (filename != null) {2959try (FileInputStream inStream = new FileInputStream(filename)) {2960printCertFromStream(inStream, out);2961}2962} else {2963printCertFromStream(System.in, out);2964}2965}2966}29672968private void doChangeStorePasswd() throws Exception {2969storePassNew = newPass;2970if (storePassNew == null) {2971storePassNew = getNewPasswd("keystore password", storePass);2972}2973if (P12KEYSTORE.equalsIgnoreCase(storetype)) {2974// When storetype is PKCS12, we need to change all keypass as well2975for (String alias : Collections.list(keyStore.aliases())) {2976if (!keyStore.isCertificateEntry(alias)) {2977// keyPass should be either null or same with storePass,2978// but keep it in case one day we want to "normalize"2979// a PKCS12 keystore having different passwords.2980Pair<Entry, char[]> objs2981= recoverEntry(keyStore, alias, storePass, keyPass);2982keyStore.setEntry(alias, objs.fst,2983new PasswordProtection(storePassNew));2984}2985}2986}2987}29882989/**2990* Creates a self-signed certificate, and stores it as a single-element2991* certificate chain.2992*/2993private void doSelfCert(String alias, String dname, String sigAlgName)2994throws Exception2995{2996if (alias == null) {2997alias = keyAlias;2998}29993000Pair<Key,char[]> objs = recoverKey(alias, storePass, keyPass);3001PrivateKey privKey = (PrivateKey)objs.fst;3002if (keyPass == null)3003keyPass = objs.snd;30043005// Determine the signature algorithm3006if (sigAlgName == null) {3007sigAlgName = getCompatibleSigAlgName(privKey);3008}30093010// Get the old certificate3011Certificate oldCert = keyStore.getCertificate(alias);3012if (oldCert == null) {3013MessageFormat form = new MessageFormat3014(rb.getString("alias.has.no.public.key"));3015Object[] source = {alias};3016throw new Exception(form.format(source));3017}3018if (!(oldCert instanceof X509Certificate)) {3019MessageFormat form = new MessageFormat3020(rb.getString("alias.has.no.X.509.certificate"));3021Object[] source = {alias};3022throw new Exception(form.format(source));3023}30243025// convert to X509CertImpl, so that we can modify selected fields3026// (no public APIs available yet)3027byte[] encoded = oldCert.getEncoded();3028X509CertImpl certImpl = new X509CertImpl(encoded);3029X509CertInfo certInfo = (X509CertInfo)certImpl.get(X509CertImpl.NAME3030+ "." +3031X509CertImpl.INFO);30323033// Extend its validity3034Date firstDate = getStartDate(startDate);3035Date lastDate = getLastDate(firstDate, validity);3036CertificateValidity interval = new CertificateValidity(firstDate,3037lastDate);3038certInfo.set(X509CertInfo.VALIDITY, interval);30393040// Make new serial number3041certInfo.set(X509CertInfo.SERIAL_NUMBER,3042CertificateSerialNumber.newRandom64bit(new SecureRandom()));30433044// Set owner and issuer fields3045X500Name owner;3046if (dname == null) {3047// Get the owner name from the certificate3048owner = (X500Name)certInfo.get(X509CertInfo.SUBJECT + "." +3049X509CertInfo.DN_NAME);3050} else {3051// Use the owner name specified at the command line3052owner = new X500Name(dname);3053certInfo.set(X509CertInfo.SUBJECT + "." +3054X509CertInfo.DN_NAME, owner);3055}3056// Make issuer same as owner (self-signed!)3057certInfo.set(X509CertInfo.ISSUER + "." +3058X509CertInfo.DN_NAME, owner);30593060certInfo.set(X509CertInfo.VERSION,3061new CertificateVersion(CertificateVersion.V3));30623063CertificateExtensions ext = createV3Extensions(3064null,3065(CertificateExtensions)certInfo.get(X509CertInfo.EXTENSIONS),3066v3ext,3067oldCert.getPublicKey(),3068null);3069certInfo.set(X509CertInfo.EXTENSIONS, ext);3070// Sign the new certificate3071X509CertImpl newCert = new X509CertImpl(certInfo);3072newCert.sign(privKey, sigAlgName);30733074// Store the new certificate as a single-element certificate chain3075keyStore.setKeyEntry(alias, privKey,3076(keyPass != null) ? keyPass : storePass,3077new Certificate[] { newCert } );30783079if (verbose) {3080System.err.println(rb.getString("New.certificate.self.signed."));3081System.err.print(newCert.toString());3082System.err.println();3083}3084}30853086/**3087* Processes a certificate reply from a certificate authority.3088*3089* <p>Builds a certificate chain on top of the certificate reply,3090* using trusted certificates from the keystore. The chain is complete3091* after a self-signed certificate has been encountered. The self-signed3092* certificate is considered a root certificate authority, and is stored3093* at the end of the chain.3094*3095* <p>The newly generated chain replaces the old chain associated with the3096* key entry.3097*3098* @return true if the certificate reply was installed, otherwise false.3099*/3100private boolean installReply(String alias, InputStream in)3101throws Exception3102{3103if (alias == null) {3104alias = keyAlias;3105}31063107Pair<Key,char[]> objs = recoverKey(alias, storePass, keyPass);3108PrivateKey privKey = (PrivateKey)objs.fst;3109if (keyPass == null) {3110keyPass = objs.snd;3111}31123113Certificate userCert = keyStore.getCertificate(alias);3114if (userCert == null) {3115MessageFormat form = new MessageFormat3116(rb.getString("alias.has.no.public.key.certificate."));3117Object[] source = {alias};3118throw new Exception(form.format(source));3119}31203121// Read the certificates in the reply3122Collection<? extends Certificate> c = generateCertificates(in);3123if (c.isEmpty()) {3124throw new Exception(rb.getString("Reply.has.no.certificates"));3125}3126Certificate[] replyCerts = c.toArray(new Certificate[c.size()]);3127Certificate[] newChain;3128if (replyCerts.length == 1) {3129// single-cert reply3130newChain = establishCertChain(userCert, replyCerts[0]);3131} else {3132// cert-chain reply (e.g., PKCS#7)3133newChain = validateReply(alias, userCert, replyCerts);3134}31353136// Now store the newly established chain in the keystore. The new3137// chain replaces the old one. The chain can be null if user chooses no.3138if (newChain != null) {3139keyStore.setKeyEntry(alias, privKey,3140(keyPass != null) ? keyPass : storePass,3141newChain);3142return true;3143} else {3144return false;3145}3146}31473148/**3149* Imports a certificate and adds it to the list of trusted certificates.3150*3151* @return true if the certificate was added, otherwise false.3152*/3153private boolean addTrustedCert(String alias, InputStream in)3154throws Exception3155{3156if (alias == null) {3157throw new Exception(rb.getString("Must.specify.alias"));3158}3159if (keyStore.containsAlias(alias)) {3160MessageFormat form = new MessageFormat(rb.getString3161("Certificate.not.imported.alias.alias.already.exists"));3162Object[] source = {alias};3163throw new Exception(form.format(source));3164}31653166// Read the certificate3167X509Certificate cert = null;3168try {3169cert = (X509Certificate)generateCertificate(in);3170} catch (ClassCastException | CertificateException ce) {3171throw new Exception(rb.getString("Input.not.an.X.509.certificate"));3172}31733174if (noprompt) {3175checkWeak(rb.getString("the.input"), cert);3176keyStore.setCertificateEntry(alias, cert);3177return true;3178}31793180// if certificate is self-signed, make sure it verifies3181boolean selfSigned = false;3182if (KeyStoreUtil.isSelfSigned(cert)) {3183cert.verify(cert.getPublicKey());3184selfSigned = true;3185}31863187// check if cert already exists in keystore3188String reply = null;3189String trustalias = keyStore.getCertificateAlias(cert);3190if (trustalias != null) {3191MessageFormat form = new MessageFormat(rb.getString3192("Certificate.already.exists.in.keystore.under.alias.trustalias."));3193Object[] source = {trustalias};3194System.err.println(form.format(source));3195checkWeak(rb.getString("the.input"), cert);3196printWeakWarnings(true);3197reply = getYesNoReply3198(rb.getString("Do.you.still.want.to.add.it.no."));3199} else if (selfSigned) {3200if (trustcacerts && (caks != null) &&3201((trustalias=caks.getCertificateAlias(cert)) != null)) {3202MessageFormat form = new MessageFormat(rb.getString3203("Certificate.already.exists.in.system.wide.CA.keystore.under.alias.trustalias."));3204Object[] source = {trustalias};3205System.err.println(form.format(source));3206checkWeak(rb.getString("the.input"), cert);3207printWeakWarnings(true);3208reply = getYesNoReply3209(rb.getString("Do.you.still.want.to.add.it.to.your.own.keystore.no."));3210}3211if (trustalias == null) {3212// Print the cert and ask user if they really want to add3213// it to their keystore3214printX509Cert(cert, System.out);3215checkWeak(rb.getString("the.input"), cert);3216printWeakWarnings(true);3217reply = getYesNoReply3218(rb.getString("Trust.this.certificate.no."));3219}3220}3221if (reply != null) {3222if ("YES".equals(reply)) {3223keyStore.setCertificateEntry(alias, cert);3224return true;3225} else {3226return false;3227}3228}32293230// Not found in this keystore and not self-signed3231// Try to establish trust chain3232try {3233Certificate[] chain = establishCertChain(null, cert);3234if (chain != null) {3235keyStore.setCertificateEntry(alias, cert);3236return true;3237}3238} catch (Exception e) {3239// Print the cert and ask user if they really want to add it to3240// their keystore3241printX509Cert(cert, System.out);3242checkWeak(rb.getString("the.input"), cert);3243printWeakWarnings(true);3244reply = getYesNoReply3245(rb.getString("Trust.this.certificate.no."));3246if ("YES".equals(reply)) {3247keyStore.setCertificateEntry(alias, cert);3248return true;3249} else {3250return false;3251}3252}32533254return false;3255}32563257/**3258* Prompts user for new password. New password must be different from3259* old one.3260*3261* @param prompt the message that gets prompted on the screen3262* @param oldPasswd the current (i.e., old) password3263*/3264private char[] getNewPasswd(String prompt, char[] oldPasswd)3265throws Exception3266{3267char[] entered = null;3268char[] reentered = null;32693270for (int count = 0; count < 3; count++) {3271MessageFormat form = new MessageFormat3272(rb.getString("New.prompt."));3273Object[] source = {prompt};3274System.err.print(form.format(source));3275entered = Password.readPassword(System.in);3276passwords.add(entered);3277if (entered == null || entered.length < 6) {3278System.err.println(rb.getString3279("Password.is.too.short.must.be.at.least.6.characters"));3280} else if (Arrays.equals(entered, oldPasswd)) {3281System.err.println(rb.getString("Passwords.must.differ"));3282} else {3283form = new MessageFormat3284(rb.getString("Re.enter.new.prompt."));3285Object[] src = {prompt};3286System.err.print(form.format(src));3287reentered = Password.readPassword(System.in);3288passwords.add(reentered);3289if (!Arrays.equals(entered, reentered)) {3290System.err.println3291(rb.getString("They.don.t.match.Try.again"));3292} else {3293Arrays.fill(reentered, ' ');3294return entered;3295}3296}3297if (entered != null) {3298Arrays.fill(entered, ' ');3299entered = null;3300}3301if (reentered != null) {3302Arrays.fill(reentered, ' ');3303reentered = null;3304}3305}3306throw new Exception(rb.getString("Too.many.failures.try.later"));3307}33083309/**3310* Prompts user for alias name.3311* @param prompt the {0} of "Enter {0} alias name: " in prompt line3312* @return the string entered by the user, without the \n at the end3313*/3314private String getAlias(String prompt) throws Exception {3315if (prompt != null) {3316MessageFormat form = new MessageFormat3317(rb.getString("Enter.prompt.alias.name."));3318Object[] source = {prompt};3319System.err.print(form.format(source));3320} else {3321System.err.print(rb.getString("Enter.alias.name."));3322}3323return (new BufferedReader(new InputStreamReader(3324System.in))).readLine();3325}33263327/**3328* Prompts user for an input string from the command line (System.in)3329* @prompt the prompt string printed3330* @return the string entered by the user, without the \n at the end3331*/3332private String inputStringFromStdin(String prompt) throws Exception {3333System.err.print(prompt);3334return (new BufferedReader(new InputStreamReader(3335System.in))).readLine();3336}33373338/**3339* Prompts user for key password. User may select to choose the same3340* password (<code>otherKeyPass</code>) as for <code>otherAlias</code>.3341*/3342private char[] getKeyPasswd(String alias, String otherAlias,3343char[] otherKeyPass)3344throws Exception3345{3346int count = 0;3347char[] keyPass = null;33483349do {3350if (otherKeyPass != null) {3351MessageFormat form = new MessageFormat(rb.getString3352("Enter.key.password.for.alias."));3353Object[] source = {alias};3354System.err.println(form.format(source));33553356form = new MessageFormat(rb.getString3357(".RETURN.if.same.as.for.otherAlias."));3358Object[] src = {otherAlias};3359System.err.print(form.format(src));3360} else {3361MessageFormat form = new MessageFormat(rb.getString3362("Enter.key.password.for.alias."));3363Object[] source = {alias};3364System.err.print(form.format(source));3365}3366System.err.flush();3367keyPass = Password.readPassword(System.in);3368passwords.add(keyPass);3369if (keyPass == null) {3370keyPass = otherKeyPass;3371}3372count++;3373} while ((keyPass == null) && count < 3);33743375if (keyPass == null) {3376throw new Exception(rb.getString("Too.many.failures.try.later"));3377}33783379return keyPass;3380}33813382private String withWeak(String alg) {3383if (DISABLED_CHECK.permits(SIG_PRIMITIVE_SET, alg, null)) {3384if (LEGACY_CHECK.permits(SIG_PRIMITIVE_SET, alg, null)) {3385return alg;3386} else {3387return String.format(rb.getString("with.weak"), alg);3388}3389} else {3390return String.format(rb.getString("with.disabled"), alg);3391}3392}33933394private String fullDisplayAlgName(Key key) {3395String result = key.getAlgorithm();3396if (key instanceof ECKey) {3397ECParameterSpec paramSpec = ((ECKey) key).getParams();3398if (paramSpec instanceof NamedCurve) {3399NamedCurve nc = (NamedCurve)paramSpec;3400result += " (" + nc.getNameAndAliases()[0] + ")";3401}3402} else if (key instanceof EdECKey) {3403result = ((EdECKey) key).getParams().getName();3404}3405return result;3406}34073408private String withWeak(Key key) {3409int kLen = KeyUtil.getKeySize(key);3410String displayAlg = fullDisplayAlgName(key);3411if (DISABLED_CHECK.permits(SIG_PRIMITIVE_SET, key)) {3412if (LEGACY_CHECK.permits(SIG_PRIMITIVE_SET, key)) {3413if (kLen >= 0) {3414return String.format(rb.getString("key.bit"), kLen, displayAlg);3415} else {3416return String.format(rb.getString("unknown.size.1"), displayAlg);3417}3418} else {3419return String.format(rb.getString("key.bit.weak"), kLen, displayAlg);3420}3421} else {3422return String.format(rb.getString("key.bit.disabled"), kLen, displayAlg);3423}3424}34253426/**3427* Prints a certificate in a human readable format.3428*/3429private void printX509Cert(X509Certificate cert, PrintStream out)3430throws Exception3431{34323433MessageFormat form = new MessageFormat3434(rb.getString(".PATTERN.printX509Cert.with.weak"));3435PublicKey pkey = cert.getPublicKey();3436String sigName = cert.getSigAlgName();3437// No need to warn about sigalg of a trust anchor3438if (!isTrustedCert(cert)) {3439sigName = withWeak(sigName);3440}3441Object[] source = {cert.getSubjectX500Principal().toString(),3442cert.getIssuerX500Principal().toString(),3443cert.getSerialNumber().toString(16),3444cert.getNotBefore().toString(),3445cert.getNotAfter().toString(),3446getCertFingerPrint("SHA-1", cert),3447getCertFingerPrint("SHA-256", cert),3448sigName,3449withWeak(pkey),3450cert.getVersion()3451};3452out.println(form.format(source));34533454if (cert instanceof X509CertImpl) {3455X509CertImpl impl = (X509CertImpl)cert;3456X509CertInfo certInfo = (X509CertInfo)impl.get(X509CertImpl.NAME3457+ "." +3458X509CertImpl.INFO);3459CertificateExtensions exts = (CertificateExtensions)3460certInfo.get(X509CertInfo.EXTENSIONS);3461if (exts != null) {3462printExtensions(rb.getString("Extensions."), exts, out);3463}3464}3465}34663467private static void printExtensions(String title, CertificateExtensions exts, PrintStream out)3468throws Exception {3469int extnum = 0;3470Iterator<Extension> i1 = exts.getAllExtensions().iterator();3471Iterator<Extension> i2 = exts.getUnparseableExtensions().values().iterator();3472while (i1.hasNext() || i2.hasNext()) {3473Extension ext = i1.hasNext()?i1.next():i2.next();3474if (extnum == 0) {3475out.println();3476out.println(title);3477out.println();3478}3479out.print("#"+(++extnum)+": "+ ext);3480if (ext.getClass() == Extension.class) {3481byte[] v = ext.getExtensionValue();3482if (v.length == 0) {3483out.println(rb.getString(".Empty.value."));3484} else {3485new sun.security.util.HexDumpEncoder().encodeBuffer(ext.getExtensionValue(), out);3486out.println();3487}3488}3489out.println();3490}3491}34923493/**3494* Locates a signer for a given certificate from a given keystore and3495* returns the signer's certificate.3496* @param cert the certificate whose signer is searched, not null3497* @param ks the keystore to search with, not null3498* @return <code>cert</code> itself if it's already inside <code>ks</code>,3499* or a certificate inside <code>ks</code> who signs <code>cert</code>,3500* or null otherwise. A label is added.3501*/3502private static Pair<String,Certificate>3503getSigner(Certificate cert, KeyStore ks) throws Exception {3504if (ks.getCertificateAlias(cert) != null) {3505return new Pair<>("", cert);3506}3507for (Enumeration<String> aliases = ks.aliases();3508aliases.hasMoreElements(); ) {3509String name = aliases.nextElement();3510Certificate trustedCert = ks.getCertificate(name);3511if (trustedCert != null) {3512try {3513cert.verify(trustedCert.getPublicKey());3514return new Pair<>(name, trustedCert);3515} catch (Exception e) {3516// Not verified, skip to the next one3517}3518}3519}3520return null;3521}35223523/**3524* Gets an X.500 name suitable for inclusion in a certification request.3525*/3526private X500Name getX500Name() throws IOException {3527BufferedReader in;3528in = new BufferedReader(new InputStreamReader(System.in));3529String commonName = "Unknown";3530String organizationalUnit = "Unknown";3531String organization = "Unknown";3532String city = "Unknown";3533String state = "Unknown";3534String country = "Unknown";3535X500Name name;3536String userInput = null;35373538int maxRetry = 20;3539do {3540if (maxRetry-- < 0) {3541throw new RuntimeException(rb.getString(3542"Too.many.retries.program.terminated"));3543}3544commonName = inputString(in,3545rb.getString("What.is.your.first.and.last.name."),3546commonName);3547organizationalUnit = inputString(in,3548rb.getString3549("What.is.the.name.of.your.organizational.unit."),3550organizationalUnit);3551organization = inputString(in,3552rb.getString("What.is.the.name.of.your.organization."),3553organization);3554city = inputString(in,3555rb.getString("What.is.the.name.of.your.City.or.Locality."),3556city);3557state = inputString(in,3558rb.getString("What.is.the.name.of.your.State.or.Province."),3559state);3560country = inputString(in,3561rb.getString3562("What.is.the.two.letter.country.code.for.this.unit."),3563country);3564name = new X500Name(commonName, organizationalUnit, organization,3565city, state, country);3566MessageFormat form = new MessageFormat3567(rb.getString("Is.name.correct."));3568Object[] source = {name};3569userInput = inputString3570(in, form.format(source), rb.getString("no"));3571} while (collator.compare(userInput, rb.getString("yes")) != 0 &&3572collator.compare(userInput, rb.getString("y")) != 0);35733574System.err.println();3575return name;3576}35773578private String inputString(BufferedReader in, String prompt,3579String defaultValue)3580throws IOException3581{3582System.err.println(prompt);3583MessageFormat form = new MessageFormat3584(rb.getString(".defaultValue."));3585Object[] source = {defaultValue};3586System.err.print(form.format(source));3587System.err.flush();35883589String value = in.readLine();3590if (value == null || collator.compare(value, "") == 0) {3591value = defaultValue;3592}3593return value;3594}35953596/**3597* Writes an X.509 certificate in base64 or binary encoding to an output3598* stream.3599*/3600private void dumpCert(Certificate cert, PrintStream out)3601throws IOException, CertificateException3602{3603if (rfc) {3604out.println(X509Factory.BEGIN_CERT);3605out.println(Base64.getMimeEncoder(64, CRLF).encodeToString(cert.getEncoded()));3606out.println(X509Factory.END_CERT);3607} else {3608out.write(cert.getEncoded()); // binary3609}3610}36113612/**3613* Recovers (private) key associated with given alias.3614*3615* @return an array of objects, where the 1st element in the array is the3616* recovered private key, and the 2nd element is the password used to3617* recover it.3618*/3619private Pair<Key,char[]> recoverKey(String alias, char[] storePass,3620char[] keyPass)3621throws Exception3622{3623Key key = null;36243625if (KeyStoreUtil.isWindowsKeyStore(storetype)) {3626key = keyStore.getKey(alias, null);3627return Pair.of(key, null);3628}36293630if (keyStore.containsAlias(alias) == false) {3631MessageFormat form = new MessageFormat3632(rb.getString("Alias.alias.does.not.exist"));3633Object[] source = {alias};3634throw new Exception(form.format(source));3635}3636if (!keyStore.entryInstanceOf(alias, KeyStore.PrivateKeyEntry.class) &&3637!keyStore.entryInstanceOf(alias, KeyStore.SecretKeyEntry.class)) {3638MessageFormat form = new MessageFormat3639(rb.getString("Alias.alias.has.no.key"));3640Object[] source = {alias};3641throw new Exception(form.format(source));3642}36433644if (keyPass == null) {3645// Try to recover the key using the keystore password3646if (storePass != null) {3647try {3648key = keyStore.getKey(alias, storePass);3649passwords.add(storePass);3650return Pair.of(key, storePass);3651} catch (UnrecoverableKeyException e) {3652if (token) {3653throw e;3654}3655}3656}3657// prompt user for key password3658keyPass = getKeyPasswd(alias, null, null);3659key = keyStore.getKey(alias, keyPass);3660return Pair.of(key, keyPass);3661} else {3662key = keyStore.getKey(alias, keyPass);3663return Pair.of(key, keyPass);3664}3665}36663667/**3668* Recovers entry associated with given alias.3669*3670* @return an array of objects, where the 1st element in the array is the3671* recovered entry, and the 2nd element is the password used to3672* recover it (null if no password).3673*/3674private Pair<Entry,char[]> recoverEntry(KeyStore ks,3675String alias,3676char[] pstore,3677char[] pkey) throws Exception {36783679if (!ks.containsAlias(alias)) {3680MessageFormat form = new MessageFormat(3681rb.getString("Alias.alias.does.not.exist"));3682Object[] source = {alias};3683throw new Exception(form.format(source));3684}36853686// Step 1: First attempt to access entry without key password3687// (PKCS11 entry or trusted certificate entry, for example).3688// If fail, go next.3689try {3690Entry entry = ks.getEntry(alias, null);3691return Pair.of(entry, null);3692} catch (UnrecoverableEntryException une) {3693if(P11KEYSTORE.equalsIgnoreCase(ks.getType()) ||3694KeyStoreUtil.isWindowsKeyStore(ks.getType())) {3695// should not happen, but a possibility3696throw une;3697}3698}36993700// entry is protected37013702// Step 2: try pkey if not null. If fail, fail.3703if (pkey != null) {3704PasswordProtection pp = new PasswordProtection(pkey);3705Entry entry = ks.getEntry(alias, pp);3706return Pair.of(entry, pkey);3707}37083709// Step 3: try pstore if not null. If fail, go next.3710if (pstore != null) {3711try {3712PasswordProtection pp = new PasswordProtection(pstore);3713Entry entry = ks.getEntry(alias, pp);3714return Pair.of(entry, pstore);3715} catch (UnrecoverableEntryException une) {3716if (P12KEYSTORE.equalsIgnoreCase(ks.getType())) {3717// P12 keystore currently does not support separate3718// store and entry passwords. We will not prompt for3719// entry password.3720throw une;3721}3722}3723}37243725// Step 4: prompt for entry password3726pkey = getKeyPasswd(alias, null, null);3727PasswordProtection pp = new PasswordProtection(pkey);3728Entry entry = ks.getEntry(alias, pp);3729return Pair.of(entry, pkey);3730}37313732/**3733* Gets the requested finger print of the certificate.3734*/3735private String getCertFingerPrint(String mdAlg, Certificate cert)3736throws Exception3737{3738byte[] encCertInfo = cert.getEncoded();3739MessageDigest md = MessageDigest.getInstance(mdAlg);3740byte[] digest = md.digest(encCertInfo);3741return HexFormat.ofDelimiter(":").withUpperCase().formatHex(digest);3742}37433744/**3745* Prints warning about missing integrity check.3746*/3747private void printNoIntegrityWarning() {3748System.err.println();3749System.err.println(rb.getString3750(".WARNING.WARNING.WARNING."));3751System.err.println(rb.getString3752(".The.integrity.of.the.information.stored.in.your.keystore."));3753System.err.println(rb.getString3754(".WARNING.WARNING.WARNING."));3755System.err.println();3756}37573758/**3759* Validates chain in certification reply, and returns the ordered3760* elements of the chain (with user certificate first, and root3761* certificate last in the array).3762*3763* @param alias the alias name3764* @param userCert the user certificate of the alias3765* @param replyCerts the chain provided in the reply3766*/3767private Certificate[] validateReply(String alias,3768Certificate userCert,3769Certificate[] replyCerts)3770throws Exception3771{37723773checkWeak(rb.getString("reply"), replyCerts);37743775// order the certs in the reply (bottom-up).3776// we know that all certs in the reply are of type X.509, because3777// we parsed them using an X.509 certificate factory3778int i;3779PublicKey userPubKey = userCert.getPublicKey();37803781// Remove duplicated certificates.3782HashSet<Certificate> nodup = new HashSet<>(Arrays.asList(replyCerts));3783replyCerts = nodup.toArray(new Certificate[nodup.size()]);37843785for (i=0; i<replyCerts.length; i++) {3786if (userPubKey.equals(replyCerts[i].getPublicKey())) {3787break;3788}3789}3790if (i == replyCerts.length) {3791MessageFormat form = new MessageFormat(rb.getString3792("Certificate.reply.does.not.contain.public.key.for.alias."));3793Object[] source = {alias};3794throw new Exception(form.format(source));3795}37963797Certificate tmpCert = replyCerts[0];3798replyCerts[0] = replyCerts[i];3799replyCerts[i] = tmpCert;38003801X509Certificate thisCert = (X509Certificate)replyCerts[0];38023803for (i=1; i < replyCerts.length-1; i++) {3804// find a cert in the reply who signs thisCert3805int j;3806for (j=i; j<replyCerts.length; j++) {3807if (KeyStoreUtil.signedBy(thisCert, (X509Certificate)replyCerts[j])) {3808tmpCert = replyCerts[i];3809replyCerts[i] = replyCerts[j];3810replyCerts[j] = tmpCert;3811thisCert = (X509Certificate)replyCerts[i];3812break;3813}3814}3815if (j == replyCerts.length) {3816throw new Exception3817(rb.getString("Incomplete.certificate.chain.in.reply"));3818}3819}38203821if (noprompt) {3822return replyCerts;3823}38243825// do we trust the cert at the top?3826Certificate topCert = replyCerts[replyCerts.length-1];3827boolean fromKeyStore = true;3828Pair<String,Certificate> root = getSigner(topCert, keyStore);3829if (root == null && trustcacerts && caks != null) {3830root = getSigner(topCert, caks);3831fromKeyStore = false;3832}3833if (root == null) {3834System.err.println();3835System.err.println3836(rb.getString("Top.level.certificate.in.reply."));3837printX509Cert((X509Certificate)topCert, System.out);3838System.err.println();3839System.err.print(rb.getString(".is.not.trusted."));3840printWeakWarnings(true);3841String reply = getYesNoReply3842(rb.getString("Install.reply.anyway.no."));3843if ("NO".equals(reply)) {3844return null;3845}3846} else {3847if (root.snd != topCert) {3848// append the root CA cert to the chain3849Certificate[] tmpCerts =3850new Certificate[replyCerts.length+1];3851System.arraycopy(replyCerts, 0, tmpCerts, 0,3852replyCerts.length);3853tmpCerts[tmpCerts.length-1] = root.snd;3854replyCerts = tmpCerts;3855checkWeak(String.format(fromKeyStore3856? rb.getString("alias.in.keystore")3857: rb.getString("alias.in.cacerts"),3858root.fst),3859root.snd);3860}3861}3862return replyCerts;3863}38643865/**3866* Establishes a certificate chain (using trusted certificates in the3867* keystore and cacerts), starting with the reply (certToVerify)3868* and ending at a self-signed certificate found in the keystore.3869*3870* @param userCert optional existing certificate, mostly likely be the3871* original self-signed cert created by -genkeypair.3872* It must have the same public key as certToVerify3873* but cannot be the same cert.3874* @param certToVerify the starting certificate to build the chain3875* @returns the established chain, might be null if user decides not3876*/3877private Certificate[] establishCertChain(Certificate userCert,3878Certificate certToVerify)3879throws Exception3880{3881if (userCert != null) {3882// Make sure that the public key of the certificate reply matches3883// the original public key in the keystore3884PublicKey origPubKey = userCert.getPublicKey();3885PublicKey replyPubKey = certToVerify.getPublicKey();3886if (!origPubKey.equals(replyPubKey)) {3887throw new Exception(rb.getString3888("Public.keys.in.reply.and.keystore.don.t.match"));3889}38903891// If the two certs are identical, we're done: no need to import3892// anything3893if (certToVerify.equals(userCert)) {3894throw new Exception(rb.getString3895("Certificate.reply.and.certificate.in.keystore.are.identical"));3896}3897}38983899// Build a hash table of all certificates in the keystore.3900// Use the subject distinguished name as the key into the hash table.3901// All certificates associated with the same subject distinguished3902// name are stored in the same hash table entry as a vector.3903Hashtable<Principal, Vector<Pair<String,X509Certificate>>> certs = null;3904if (keyStore.size() > 0) {3905certs = new Hashtable<>(11);3906keystorecerts2Hashtable(keyStore, certs);3907}3908if (trustcacerts) {3909if (caks!=null && caks.size()>0) {3910if (certs == null) {3911certs = new Hashtable<>(11);3912}3913keystorecerts2Hashtable(caks, certs);3914}3915}39163917// start building chain3918Vector<Pair<String,X509Certificate>> chain = new Vector<>(2);3919if (buildChain(3920new Pair<>(rb.getString("the.input"),3921(X509Certificate) certToVerify),3922chain, certs)) {3923for (Pair<String,X509Certificate> p : chain) {3924checkWeak(p.fst, p.snd);3925}3926Certificate[] newChain =3927new Certificate[chain.size()];3928// buildChain() returns chain with self-signed root-cert first and3929// user-cert last, so we need to invert the chain before we store3930// it3931int j=0;3932for (int i=chain.size()-1; i>=0; i--) {3933newChain[j] = chain.elementAt(i).snd;3934j++;3935}3936return newChain;3937} else {3938throw new Exception3939(rb.getString("Failed.to.establish.chain.from.reply"));3940}3941}39423943/**3944* Recursively tries to establish chain from pool of certs starting from3945* certToVerify until a self-signed cert is found, and fill the certs found3946* into chain. Each cert in the chain signs the next one.3947*3948* This method is able to recover from an error, say, if certToVerify3949* is signed by certA but certA has no issuer in certs and itself is not3950* self-signed, the method can try another certB that also signs3951* certToVerify and look for signer of certB, etc, etc.3952*3953* Each cert in chain comes with a label showing its origin. The label is3954* used in the warning message when the cert is considered a risk.3955*3956* @param certToVerify the cert that needs to be verified.3957* @param chain the chain that's being built.3958* @param certs the pool of trusted certs3959*3960* @return true if successful, false otherwise.3961*/3962private boolean buildChain(Pair<String,X509Certificate> certToVerify,3963Vector<Pair<String,X509Certificate>> chain,3964Hashtable<Principal, Vector<Pair<String,X509Certificate>>> certs) {3965if (KeyStoreUtil.isSelfSigned(certToVerify.snd)) {3966// reached self-signed root cert;3967// no verification needed because it's trusted.3968chain.addElement(certToVerify);3969return true;3970}39713972Principal issuer = certToVerify.snd.getIssuerX500Principal();39733974// Get the issuer's certificate(s)3975Vector<Pair<String,X509Certificate>> vec = certs.get(issuer);3976if (vec == null) {3977return false;3978}39793980// Try out each certificate in the vector, until we find one3981// whose public key verifies the signature of the certificate3982// in question.3983for (Enumeration<Pair<String,X509Certificate>> issuerCerts = vec.elements();3984issuerCerts.hasMoreElements(); ) {3985Pair<String,X509Certificate> issuerCert = issuerCerts.nextElement();3986PublicKey issuerPubKey = issuerCert.snd.getPublicKey();3987try {3988certToVerify.snd.verify(issuerPubKey);3989} catch (Exception e) {3990continue;3991}3992if (buildChain(issuerCert, chain, certs)) {3993chain.addElement(certToVerify);3994return true;3995}3996}3997return false;3998}39994000/**4001* Prompts user for yes/no decision.4002*4003* @return the user's decision, can only be "YES" or "NO"4004*/4005private String getYesNoReply(String prompt)4006throws IOException4007{4008String reply = null;4009int maxRetry = 20;4010do {4011if (maxRetry-- < 0) {4012throw new RuntimeException(rb.getString(4013"Too.many.retries.program.terminated"));4014}4015System.err.print(prompt);4016System.err.flush();4017reply = (new BufferedReader(new InputStreamReader4018(System.in))).readLine();4019if (reply == null ||4020collator.compare(reply, "") == 0 ||4021collator.compare(reply, rb.getString("n")) == 0 ||4022collator.compare(reply, rb.getString("no")) == 0) {4023reply = "NO";4024} else if (collator.compare(reply, rb.getString("y")) == 0 ||4025collator.compare(reply, rb.getString("yes")) == 0) {4026reply = "YES";4027} else {4028System.err.println(rb.getString("Wrong.answer.try.again"));4029reply = null;4030}4031} while (reply == null);4032return reply;4033}40344035/**4036* Stores the (leaf) certificates of a keystore in a hashtable.4037* All certs belonging to the same CA are stored in a vector that4038* in turn is stored in the hashtable, keyed by the CA's subject DN.4039* Each cert comes with a string label that shows its origin and alias.4040*/4041private void keystorecerts2Hashtable(KeyStore ks,4042Hashtable<Principal, Vector<Pair<String,X509Certificate>>> hash)4043throws Exception {40444045for (Enumeration<String> aliases = ks.aliases();4046aliases.hasMoreElements(); ) {4047String alias = aliases.nextElement();4048Certificate cert = ks.getCertificate(alias);4049if (cert != null) {4050Principal subjectDN = ((X509Certificate)cert).getSubjectX500Principal();4051Pair<String,X509Certificate> pair = new Pair<>(4052String.format(4053rb.getString(ks == caks ?4054"alias.in.cacerts" :4055"alias.in.keystore"),4056alias),4057(X509Certificate)cert);4058Vector<Pair<String,X509Certificate>> vec = hash.get(subjectDN);4059if (vec == null) {4060vec = new Vector<>();4061vec.addElement(pair);4062} else {4063if (!vec.contains(pair)) {4064vec.addElement(pair);4065}4066}4067hash.put(subjectDN, vec);4068}4069}4070}40714072/**4073* Returns the issue time that's specified the -startdate option4074* @param s the value of -startdate option4075*/4076private static Date getStartDate(String s) throws IOException {4077Calendar c = new GregorianCalendar();4078if (s != null) {4079IOException ioe = new IOException(4080rb.getString("Illegal.startdate.value"));4081int len = s.length();4082if (len == 0) {4083throw ioe;4084}4085if (s.charAt(0) == '-' || s.charAt(0) == '+') {4086// Form 1: ([+-]nnn[ymdHMS])+4087int start = 0;4088while (start < len) {4089int sign = 0;4090switch (s.charAt(start)) {4091case '+': sign = 1; break;4092case '-': sign = -1; break;4093default: throw ioe;4094}4095int i = start+1;4096for (; i<len; i++) {4097char ch = s.charAt(i);4098if (ch < '0' || ch > '9') break;4099}4100if (i == start+1) throw ioe;4101int number = Integer.parseInt(s.substring(start+1, i));4102if (i >= len) throw ioe;4103int unit = 0;4104switch (s.charAt(i)) {4105case 'y': unit = Calendar.YEAR; break;4106case 'm': unit = Calendar.MONTH; break;4107case 'd': unit = Calendar.DATE; break;4108case 'H': unit = Calendar.HOUR; break;4109case 'M': unit = Calendar.MINUTE; break;4110case 'S': unit = Calendar.SECOND; break;4111default: throw ioe;4112}4113c.add(unit, sign * number);4114start = i + 1;4115}4116} else {4117// Form 2: [yyyy/mm/dd] [HH:MM:SS]4118String date = null, time = null;4119if (len == 19) {4120date = s.substring(0, 10);4121time = s.substring(11);4122if (s.charAt(10) != ' ')4123throw ioe;4124} else if (len == 10) {4125date = s;4126} else if (len == 8) {4127time = s;4128} else {4129throw ioe;4130}4131if (date != null) {4132if (date.matches("\\d\\d\\d\\d\\/\\d\\d\\/\\d\\d")) {4133c.set(Integer.valueOf(date.substring(0, 4)),4134Integer.valueOf(date.substring(5, 7))-1,4135Integer.valueOf(date.substring(8, 10)));4136} else {4137throw ioe;4138}4139}4140if (time != null) {4141if (time.matches("\\d\\d:\\d\\d:\\d\\d")) {4142c.set(Calendar.HOUR_OF_DAY, Integer.valueOf(time.substring(0, 2)));4143c.set(Calendar.MINUTE, Integer.valueOf(time.substring(3, 5)));4144c.set(Calendar.SECOND, Integer.valueOf(time.substring(6, 8)));4145c.set(Calendar.MILLISECOND, 0);4146} else {4147throw ioe;4148}4149}4150}4151}4152return c.getTime();4153}41544155/**4156* Match a command with a command set. The match can be exact, or4157* partial, or case-insensitive.4158*4159* @param s the command provided by user4160* @param list the legal command set represented by KnownOIDs enums.4161* @return the position of a single match, or -1 if none matched4162* @throws Exception if s is ambiguous4163*/4164private static int oneOf(String s, KnownOIDs... list) throws Exception {4165String[] convertedList = new String[list.length];4166for (int i = 0; i < list.length; i++) {4167convertedList[i] = list[i].stdName();4168}4169return oneOf(s, convertedList);4170}41714172/**4173* Match a command with a command set. The match can be exact, or4174* partial, or case-insensitive.4175*4176* @param s the command provided by user4177* @param list the legal command set. If there is a null, commands after it4178* are regarded experimental, which means they are supported but their4179* existence should not be revealed to user.4180* @return the position of a single match, or -1 if none matched4181* @throws Exception if s is ambiguous4182*/4183private static int oneOf(String s, String... list) throws Exception {41844185// First, if there is an exact match, returns it.4186int res = oneOfMatch((a,b) -> a.equals(b), s, list);4187if (res >= 0) {4188return res;4189}41904191// Second, if there is one single camelCase or prefix match, returns it.4192// This regex substitution removes all lowercase letters not at the4193// beginning, so "keyCertSign" becomes "kCS".4194res = oneOfMatch((a,b) -> a.equals(b.replaceAll("(?<!^)[a-z]", ""))4195|| b.startsWith(a), s, list);4196if (res >= 0) {4197return res;4198}41994200// Finally, retry the 2nd step ignoring case4201return oneOfMatch((a,b) -> a.equalsIgnoreCase(b.replaceAll("(?<!^)[a-z]", ""))4202|| b.toUpperCase(Locale.ROOT).startsWith(a.toUpperCase(Locale.ROOT)),4203s, list);4204}42054206/**4207* Match a command with a command set.4208*4209* @param matcher a BiFunction which returns {@code true} if the 1st4210* argument (user input) matches the 2nd one (full command)4211* @param s the command provided by user4212* @param list the legal command set4213* @return the position of a single match, or -1 if none matched4214* @throws Exception if s is ambiguous4215*/4216private static int oneOfMatch(BiFunction<String,String,Boolean> matcher,4217String s, String... list) throws Exception {4218int[] match = new int[list.length];4219int nmatch = 0;4220int experiment = Integer.MAX_VALUE;4221for (int i = 0; i<list.length; i++) {4222String one = list[i];4223if (one == null) {4224experiment = i;4225continue;4226}4227if (matcher.apply(s, one)) {4228match[nmatch++] = i;4229}4230}4231if (nmatch == 0) {4232return -1;4233} else if (nmatch == 1) {4234return match[0];4235} else {4236// If multiple matches is in experimental commands, ignore them4237if (match[1] > experiment) {4238return match[0];4239}4240StringBuilder sb = new StringBuilder();4241MessageFormat form = new MessageFormat(rb.getString4242("command.{0}.is.ambiguous."));4243Object[] source = {s};4244sb.append(form.format(source));4245sb.append("\n ");4246for (int i=0; i<nmatch && match[i]<experiment; i++) {4247sb.append(' ');4248sb.append(list[match[i]]);4249}4250throw new Exception(sb.toString());4251}4252}42534254/**4255* Create a GeneralName object from known types4256* @param t one of 5 known types4257* @param v value4258* @param exttype X.509 extension type4259* @return which one4260*/4261private GeneralName createGeneralName(String t, String v, int exttype)4262throws Exception {4263GeneralNameInterface gn;4264int p = oneOf(t, "EMAIL", "URI", "DNS", "IP", "OID");4265if (p < 0) {4266throw new Exception(rb.getString(4267"Unrecognized.GeneralName.type.") + t);4268}4269switch (p) {4270case 0: gn = new RFC822Name(v); break;4271case 1: gn = new URIName(v); break;4272case 2:4273if (exttype == 3) {4274// Allow wildcard only for SAN extension4275gn = new DNSName(v, true);4276} else {4277gn = new DNSName(v);4278}4279break;4280case 3: gn = new IPAddressName(v); break;4281default: gn = new OIDName(v); break; //44282}4283return new GeneralName(gn);4284}42854286private static final String[] extSupported = {4287"BasicConstraints",4288"KeyUsage",4289"ExtendedKeyUsage",4290"SubjectAlternativeName",4291"IssuerAlternativeName",4292"SubjectInfoAccess",4293"AuthorityInfoAccess",4294null,4295"CRLDistributionPoints",4296};42974298private ObjectIdentifier findOidForExtName(String type)4299throws Exception {4300switch (oneOf(type, extSupported)) {4301case 0: return PKIXExtensions.BasicConstraints_Id;4302case 1: return PKIXExtensions.KeyUsage_Id;4303case 2: return PKIXExtensions.ExtendedKeyUsage_Id;4304case 3: return PKIXExtensions.SubjectAlternativeName_Id;4305case 4: return PKIXExtensions.IssuerAlternativeName_Id;4306case 5: return PKIXExtensions.SubjectInfoAccess_Id;4307case 6: return PKIXExtensions.AuthInfoAccess_Id;4308case 8: return PKIXExtensions.CRLDistributionPoints_Id;4309default: return ObjectIdentifier.of(type);4310}4311}43124313// Add an extension into a CertificateExtensions, always using OID as key4314private static void setExt(CertificateExtensions result, Extension ex)4315throws IOException {4316result.set(ex.getId(), ex);4317}43184319/**4320* Create X509v3 extensions from a string representation. Note that the4321* SubjectKeyIdentifierExtension will always be created non-critical besides4322* the extension requested in the <code>extstr</code> argument.4323*4324* @param requestedEx the requested extensions, can be null, used for -gencert4325* @param existingEx the original extensions, can be null, used for -selfcert4326* @param extstrs -ext values, Read keytool doc4327* @param pkey the public key for the certificate4328* @param aSubjectKeyId the subject key identifier for the authority (issuer)4329* @return the created CertificateExtensions4330*/4331private CertificateExtensions createV3Extensions(4332CertificateExtensions requestedEx,4333CertificateExtensions existingEx,4334List <String> extstrs,4335PublicKey pkey,4336KeyIdentifier aSubjectKeyId) throws Exception {43374338// By design, inside a CertificateExtensions object, all known4339// extensions uses name (say, "BasicConstraints") as key and4340// a child Extension type (say, "BasicConstraintsExtension")4341// as value, unknown extensions uses OID as key and bare4342// Extension object as value. This works fine inside JDK.4343//4344// However, in keytool, there is no way to prevent people4345// using OID in -ext, either as a new extension, or in a4346// honored value. Thus here we (ab)use CertificateExtensions4347// by always using OID as key and value can be of any type.43484349if (existingEx != null && requestedEx != null) {4350// This should not happen4351throw new Exception("One of request and original should be null.");4352}4353// A new extensions always using OID as key4354CertificateExtensions result = new CertificateExtensions();4355if (existingEx != null) {4356for (Extension ex: existingEx.getAllExtensions()) {4357setExt(result, ex);4358}4359}4360try {4361// always non-critical4362setExt(result, new SubjectKeyIdentifierExtension(4363new KeyIdentifier(pkey).getIdentifier()));4364if (aSubjectKeyId != null) {4365setExt(result, new AuthorityKeyIdentifierExtension(aSubjectKeyId,4366null, null));4367}43684369// name{:critical}{=value}4370// Honoring requested extensions4371if (requestedEx != null) {4372// The existing requestedEx might use names as keys,4373// translate to all-OID first.4374CertificateExtensions request2 = new CertificateExtensions();4375for (sun.security.x509.Extension ex: requestedEx.getAllExtensions()) {4376request2.set(ex.getId(), ex);4377}4378for(String extstr: extstrs) {4379if (extstr.toLowerCase(Locale.ENGLISH).startsWith("honored=")) {4380List<String> list = Arrays.asList(4381extstr.toLowerCase(Locale.ENGLISH).substring(8).split(","));4382// First check existence of "all"4383if (list.contains("all")) {4384for (Extension ex: request2.getAllExtensions()) {4385setExt(result, ex);4386}4387}4388// one by one for others4389for (String item: list) {4390if (item.equals("all")) continue;43914392// add or remove4393boolean add;4394// -1, unchanged, 0 critical, 1 non-critical4395int action = -1;4396String type = null;4397if (item.startsWith("-")) {4398add = false;4399type = item.substring(1);4400} else {4401add = true;4402int colonpos = item.indexOf(':');4403if (colonpos >= 0) {4404type = item.substring(0, colonpos);4405action = oneOf(item.substring(colonpos+1),4406"critical", "non-critical");4407if (action == -1) {4408throw new Exception(rb.getString4409("Illegal.value.") + item);4410}4411} else {4412type = item;4413}4414}4415String n = findOidForExtName(type).toString();4416if (add) {4417Extension e = request2.get(n);4418if (!e.isCritical() && action == 04419|| e.isCritical() && action == 1) {4420e = Extension.newExtension(4421e.getExtensionId(),4422!e.isCritical(),4423e.getExtensionValue());4424}4425setExt(result, e);4426} else {4427result.delete(n);4428}4429}4430break;4431}4432}4433}4434for(String extstr: extstrs) {4435String name, value;4436boolean isCritical = false;44374438int eqpos = extstr.indexOf('=');4439if (eqpos >= 0) {4440name = extstr.substring(0, eqpos);4441value = extstr.substring(eqpos+1);4442} else {4443name = extstr;4444value = null;4445}44464447int colonpos = name.indexOf(':');4448if (colonpos >= 0) {4449if (oneOf(name.substring(colonpos+1), "critical") == 0) {4450isCritical = true;4451}4452name = name.substring(0, colonpos);4453}44544455if (name.equalsIgnoreCase("honored")) {4456continue;4457}4458int exttype = oneOf(name, extSupported);4459switch (exttype) {4460case 0: // BC4461int pathLen = -1;4462boolean isCA = false;4463if (value == null) {4464isCA = true;4465} else {4466try { // the abbr format4467pathLen = Integer.parseInt(value);4468isCA = true;4469} catch (NumberFormatException ufe) {4470// ca:true,pathlen:14471for (String part: value.split(",")) {4472String[] nv = part.split(":");4473if (nv.length != 2) {4474throw new Exception(rb.getString4475("Illegal.value.") + extstr);4476} else {4477if (nv[0].equalsIgnoreCase("ca")) {4478isCA = Boolean.parseBoolean(nv[1]);4479} else if (nv[0].equalsIgnoreCase("pathlen")) {4480pathLen = Integer.parseInt(nv[1]);4481} else {4482throw new Exception(rb.getString4483("Illegal.value.") + extstr);4484}4485}4486}4487}4488}4489setExt(result, new BasicConstraintsExtension(isCritical, isCA,4490pathLen));4491break;4492case 1: // KU4493if(value != null) {4494boolean[] ok = new boolean[9];4495for (String s: value.split(",")) {4496int p = oneOf(s,4497"digitalSignature", // (0),4498"nonRepudiation", // (1)4499"keyEncipherment", // (2),4500"dataEncipherment", // (3),4501"keyAgreement", // (4),4502"keyCertSign", // (5),4503"cRLSign", // (6),4504"encipherOnly", // (7),4505"decipherOnly", // (8)4506"contentCommitment" // also (1)4507);4508if (p < 0) {4509throw new Exception(rb.getString("Unknown.keyUsage.type.") + s);4510}4511if (p == 9) p = 1;4512ok[p] = true;4513}4514KeyUsageExtension kue = new KeyUsageExtension(ok);4515// The above KeyUsageExtension constructor does not4516// allow isCritical value, so...4517setExt(result, Extension.newExtension(4518kue.getExtensionId(),4519isCritical,4520kue.getExtensionValue()));4521} else {4522throw new Exception(rb.getString4523("Illegal.value.") + extstr);4524}4525break;4526case 2: // EKU4527if(value != null) {4528Vector<ObjectIdentifier> v = new Vector<>();4529KnownOIDs[] choices = {4530KnownOIDs.anyExtendedKeyUsage,4531KnownOIDs.serverAuth,4532KnownOIDs.clientAuth,4533KnownOIDs.codeSigning,4534KnownOIDs.emailProtection,4535KnownOIDs.KP_TimeStamping,4536KnownOIDs.OCSPSigning4537};4538for (String s: value.split(",")) {4539int p = oneOf(s, choices);4540String o = s;4541if (p >= 0) {4542o = choices[p].value();4543}4544try {4545v.add(ObjectIdentifier.of(o));4546} catch (Exception e) {4547throw new Exception(rb.getString(4548"Unknown.extendedkeyUsage.type.") + s);4549}4550}4551setExt(result, new ExtendedKeyUsageExtension(isCritical, v));4552} else {4553throw new Exception(rb.getString4554("Illegal.value.") + extstr);4555}4556break;4557case 3: // SAN4558case 4: // IAN4559if(value != null) {4560String[] ps = value.split(",");4561GeneralNames gnames = new GeneralNames();4562for(String item: ps) {4563colonpos = item.indexOf(':');4564if (colonpos < 0) {4565throw new Exception("Illegal item " + item + " in " + extstr);4566}4567String t = item.substring(0, colonpos);4568String v = item.substring(colonpos+1);4569gnames.add(createGeneralName(t, v, exttype));4570}4571if (exttype == 3) {4572setExt(result, new SubjectAlternativeNameExtension(4573isCritical, gnames));4574} else {4575setExt(result, new IssuerAlternativeNameExtension(4576isCritical, gnames));4577}4578} else {4579throw new Exception(rb.getString4580("Illegal.value.") + extstr);4581}4582break;4583case 5: // SIA, always non-critical4584case 6: // AIA, always non-critical4585if (isCritical) {4586throw new Exception(rb.getString(4587"This.extension.cannot.be.marked.as.critical.") + extstr);4588}4589if(value != null) {4590List<AccessDescription> accessDescriptions =4591new ArrayList<>();4592String[] ps = value.split(",");4593for(String item: ps) {4594colonpos = item.indexOf(':');4595int colonpos2 = item.indexOf(':', colonpos+1);4596if (colonpos < 0 || colonpos2 < 0) {4597throw new Exception(rb.getString4598("Illegal.value.") + extstr);4599}4600String m = item.substring(0, colonpos);4601String t = item.substring(colonpos+1, colonpos2);4602String v = item.substring(colonpos2+1);4603KnownOIDs[] choices = {4604KnownOIDs.OCSP,4605KnownOIDs.caIssuers,4606KnownOIDs.AD_TimeStamping,4607KnownOIDs.caRepository4608};4609int p = oneOf(m, choices);4610ObjectIdentifier oid;4611if (p >= 0) {4612oid = ObjectIdentifier.of(choices[p]);4613} else {4614try {4615oid = ObjectIdentifier.of(m);4616} catch (Exception e) {4617throw new Exception(rb.getString(4618"Unknown.AccessDescription.type.") + m);4619}4620}4621accessDescriptions.add(new AccessDescription(4622oid, createGeneralName(t, v, exttype)));4623}4624if (exttype == 5) {4625setExt(result, new SubjectInfoAccessExtension(accessDescriptions));4626} else {4627setExt(result, new AuthorityInfoAccessExtension(accessDescriptions));4628}4629} else {4630throw new Exception(rb.getString4631("Illegal.value.") + extstr);4632}4633break;4634case 8: // CRL, experimental, only support 1 distributionpoint4635if(value != null) {4636String[] ps = value.split(",");4637GeneralNames gnames = new GeneralNames();4638for(String item: ps) {4639colonpos = item.indexOf(':');4640if (colonpos < 0) {4641throw new Exception("Illegal item " + item + " in " + extstr);4642}4643String t = item.substring(0, colonpos);4644String v = item.substring(colonpos+1);4645gnames.add(createGeneralName(t, v, exttype));4646}4647setExt(result, new CRLDistributionPointsExtension(4648isCritical, Collections.singletonList(4649new DistributionPoint(gnames, null, null))));4650} else {4651throw new Exception(rb.getString4652("Illegal.value.") + extstr);4653}4654break;4655case -1:4656ObjectIdentifier oid = ObjectIdentifier.of(name);4657byte[] data = null;4658if (value != null) {4659data = new byte[value.length() / 2 + 1];4660int pos = 0;4661for (char c: value.toCharArray()) {4662if (!HexFormat.isHexDigit(c)) {4663continue;4664}4665int hex = HexFormat.fromHexDigit(c);4666if (pos % 2 == 0) {4667data[pos/2] = (byte)(hex << 4);4668} else {4669data[pos/2] += hex;4670}4671pos++;4672}4673if (pos % 2 != 0) {4674throw new Exception(rb.getString(4675"Odd.number.of.hex.digits.found.") + extstr);4676}4677data = Arrays.copyOf(data, pos/2);4678} else {4679data = new byte[0];4680}4681setExt(result, new Extension(oid, isCritical,4682new DerValue(DerValue.tag_OctetString, data)4683.toByteArray()));4684break;4685default:4686throw new Exception(rb.getString(4687"Unknown.extension.type.") + extstr);4688}4689}4690} catch(IOException e) {4691throw new RuntimeException(e);4692}4693return result;4694}46954696private Date getLastDate(Date firstDate, long validity)4697throws Exception {4698Date lastDate = new Date();4699lastDate.setTime(firstDate.getTime() + validity*1000L*24L*60L*60L);47004701Calendar c = new GregorianCalendar(TimeZone.getTimeZone("UTC"));4702c.setTime(lastDate);4703if (c.get(Calendar.YEAR) > 9999) {4704throw new Exception("Validity period ends at calendar year " +4705c.get(Calendar.YEAR) + " which is greater than 9999");4706}47074708return lastDate;4709}47104711private boolean isTrustedCert(Certificate cert) throws KeyStoreException {4712if (caks != null && caks.getCertificateAlias(cert) != null) {4713return true;4714} else {4715String inKS = keyStore.getCertificateAlias(cert);4716return inKS != null && keyStore.isCertificateEntry(inKS);4717}4718}47194720private void checkWeak(String label, String sigAlg, Key key) {4721if (sigAlg != null) {4722if (!DISABLED_CHECK.permits(SIG_PRIMITIVE_SET, sigAlg, null)) {4723weakWarnings.add(String.format(4724rb.getString("whose.sigalg.disabled"), label, sigAlg));4725} else if (!LEGACY_CHECK.permits(SIG_PRIMITIVE_SET, sigAlg, null)) {4726weakWarnings.add(String.format(4727rb.getString("whose.sigalg.weak"), label, sigAlg));4728}4729}47304731if (key != null) {4732if (!DISABLED_CHECK.permits(SIG_PRIMITIVE_SET, key)) {4733weakWarnings.add(String.format(4734rb.getString("whose.key.disabled"), label,4735String.format(rb.getString("key.bit"),4736KeyUtil.getKeySize(key), fullDisplayAlgName(key))));4737} else if (!LEGACY_CHECK.permits(SIG_PRIMITIVE_SET, key)) {4738weakWarnings.add(String.format(4739rb.getString("whose.key.weak"), label,4740String.format(rb.getString("key.bit"),4741KeyUtil.getKeySize(key), fullDisplayAlgName(key))));4742}4743}4744}47454746private void checkWeak(String label, Certificate[] certs)4747throws KeyStoreException {4748for (int i = 0; i < certs.length; i++) {4749Certificate cert = certs[i];4750if (cert instanceof X509Certificate) {4751X509Certificate xc = (X509Certificate)cert;4752String fullLabel = label;4753if (certs.length > 1) {4754fullLabel = oneInMany(label, i, certs.length);4755}4756checkWeak(fullLabel, xc);4757}4758}4759}47604761private void checkWeak(String label, Certificate cert)4762throws KeyStoreException {4763if (cert instanceof X509Certificate) {4764X509Certificate xc = (X509Certificate)cert;4765// No need to check the sigalg of a trust anchor4766String sigAlg = isTrustedCert(cert) ? null : xc.getSigAlgName();4767checkWeak(label, sigAlg, xc.getPublicKey());4768}4769}47704771private void checkWeak(String label, PKCS10 p10) {4772checkWeak(label, p10.getSigAlg(), p10.getSubjectPublicKeyInfo());4773}47744775private void checkWeak(String label, CRL crl, Key key) {4776if (crl instanceof X509CRLImpl) {4777X509CRLImpl impl = (X509CRLImpl)crl;4778checkWeak(label, impl.getSigAlgName(), key);4779}4780}47814782private void printWeakWarnings(boolean newLine) {4783if (!weakWarnings.isEmpty() && !nowarn) {4784System.err.println("\nWarning:");4785for (String warning : weakWarnings) {4786System.err.println(warning);4787}4788if (newLine) {4789// When calling before a yes/no prompt, add a new line4790System.err.println();4791}4792}4793weakWarnings.clear();4794}47954796/**4797* Prints the usage of this tool.4798*/4799private void usage() {4800if (command != null) {4801System.err.println("keytool " + command +4802rb.getString(".OPTION."));4803System.err.println();4804System.err.println(rb.getString(command.description));4805System.err.println();4806System.err.println(rb.getString("Options."));4807System.err.println();48084809// Left and right sides of the options list. Both might4810// contain "\n" and span multiple lines4811String[] left = new String[command.options.length];4812String[] right = new String[command.options.length];48134814// Length of left side of options list4815int lenLeft = 0;48164817for (int j = 0; j < command.options.length; j++) {4818Option opt = command.options[j];4819left[j] = opt.toString();4820if (opt.arg != null) {4821left[j] += " " + opt.arg;4822}4823String[] lefts = left[j].split("\n");4824for (String s : lefts) {4825if (s.length() > lenLeft) {4826lenLeft = s.length();4827}4828}4829right[j] = rb.getString(opt.description);4830}4831for (int j = 0; j < left.length; j++) {4832String[] lefts = left[j].split("\n");4833String[] rights = right[j].split("\n");4834for (int i = 0; i < lefts.length && i < rights.length; i++) {4835String s1 = i < lefts.length ? lefts[i] : "";4836String s2 = i < rights.length ? rights[i] : "";4837if (i == 0) {4838System.err.printf(" %-" + lenLeft + "s %s\n", s1, s2);4839} else {4840System.err.printf(" %-" + lenLeft + "s %s\n", s1, s2);4841}4842}4843}4844System.err.println();4845System.err.println(rb.getString(4846"Use.keytool.help.for.all.available.commands"));4847} else {4848System.err.println(rb.getString(4849"Key.and.Certificate.Management.Tool"));4850System.err.println();4851System.err.println(rb.getString("Commands."));4852System.err.println();4853for (Command c: Command.values()) {4854if (c == KEYCLONE) break;4855System.err.printf(" %-20s%s\n", c, rb.getString(c.description));4856}4857System.err.println();4858System.err.println(rb.getString(4859"Use.keytool.help.for.all.available.commands"));4860System.err.println(rb.getString(4861"Use.keytool.command.name.help.for.usage.of.command.name"));4862}4863}48644865private void tinyHelp() {4866usage();4867if (debug) {4868throw new RuntimeException("NO BIG ERROR, SORRY");4869} else {4870System.exit(1);4871}4872}48734874private void errorNeedArgument(String flag) {4875Object[] source = {flag};4876System.err.println(new MessageFormat(4877rb.getString("Command.option.flag.needs.an.argument.")).format(source));4878tinyHelp();4879}48804881private char[] getPass(String modifier, String arg) {4882char[] output =4883KeyStoreUtil.getPassWithModifier(modifier, arg, rb, collator);4884if (output != null) return output;4885tinyHelp();4886return null; // Useless, tinyHelp() already exits.4887}4888}48894890// This class is exactly the same as com.sun.tools.javac.util.Pair,4891// it's copied here since the original one is not included in JRE.4892class Pair<A, B> {48934894public final A fst;4895public final B snd;48964897public Pair(A fst, B snd) {4898this.fst = fst;4899this.snd = snd;4900}49014902public String toString() {4903return "Pair[" + fst + "," + snd + "]";4904}49054906public boolean equals(Object other) {4907return4908other instanceof Pair &&4909Objects.equals(fst, ((Pair)other).fst) &&4910Objects.equals(snd, ((Pair)other).snd);4911}49124913public int hashCode() {4914if (fst == null) return (snd == null) ? 0 : snd.hashCode() + 1;4915else if (snd == null) return fst.hashCode() + 2;4916else return fst.hashCode() * 17 + snd.hashCode();4917}49184919public static <A,B> Pair<A,B> of(A a, B b) {4920return new Pair<>(a,b);4921}4922}4923492449254926