Path: blob/master/src/java.base/share/classes/sun/security/tools/keytool/Main.java
41161 views
/*1* Copyright (c) 1997, 2021, 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 = new Date();1448lastDate.setTime(firstDate.getTime() + validity*1000L*24L*60L*60L);1449CertificateValidity interval = new CertificateValidity(firstDate,1450lastDate);14511452PrivateKey privateKey =1453(PrivateKey)recoverKey(alias, storePass, keyPass).fst;1454if (sigAlgName == null) {1455sigAlgName = getCompatibleSigAlgName(privateKey);1456}1457X509CertInfo info = new X509CertInfo();1458info.set(X509CertInfo.VALIDITY, interval);1459info.set(X509CertInfo.SERIAL_NUMBER,1460CertificateSerialNumber.newRandom64bit(new SecureRandom()));1461info.set(X509CertInfo.VERSION,1462new CertificateVersion(CertificateVersion.V3));1463info.set(X509CertInfo.ISSUER, issuer);14641465BufferedReader reader = new BufferedReader(new InputStreamReader(in));1466boolean canRead = false;1467StringBuilder sb = new StringBuilder();1468while (true) {1469String s = reader.readLine();1470if (s == null) break;1471// OpenSSL does not use NEW1472//if (s.startsWith("-----BEGIN NEW CERTIFICATE REQUEST-----")) {1473if (s.startsWith("-----BEGIN") && s.indexOf("REQUEST") >= 0) {1474canRead = true;1475//} else if (s.startsWith("-----END NEW CERTIFICATE REQUEST-----")) {1476} else if (s.startsWith("-----END") && s.indexOf("REQUEST") >= 0) {1477break;1478} else if (canRead) {1479sb.append(s);1480}1481}1482byte[] rawReq = Pem.decode(new String(sb));1483PKCS10 req = new PKCS10(rawReq);14841485checkWeak(rb.getString("the.certificate.request"), req);14861487info.set(X509CertInfo.KEY, new CertificateX509Key(req.getSubjectPublicKeyInfo()));1488info.set(X509CertInfo.SUBJECT,1489dname==null?req.getSubjectName():new X500Name(dname));1490CertificateExtensions reqex = null;1491Iterator<PKCS10Attribute> attrs = req.getAttributes().getAttributes().iterator();1492while (attrs.hasNext()) {1493PKCS10Attribute attr = attrs.next();1494if (attr.getAttributeId().equals(PKCS9Attribute.EXTENSION_REQUEST_OID)) {1495reqex = (CertificateExtensions)attr.getAttributeValue();1496}1497}14981499PublicKey subjectPubKey = req.getSubjectPublicKeyInfo();1500PublicKey issuerPubKey = signerCert.getPublicKey();15011502KeyIdentifier signerSubjectKeyId;1503if (Arrays.equals(subjectPubKey.getEncoded(), issuerPubKey.getEncoded())) {1504// No AKID for self-signed cert1505signerSubjectKeyId = null;1506} else {1507X509CertImpl certImpl;1508if (signerCert instanceof X509CertImpl) {1509certImpl = (X509CertImpl) signerCert;1510} else {1511certImpl = new X509CertImpl(signerCert.getEncoded());1512}15131514// To enforce compliance with RFC 5280 section 4.2.1.1: "Where a key1515// identifier has been previously established, the CA SHOULD use the1516// previously established identifier."1517// Use issuer's SKID to establish the AKID in createV3Extensions() method.1518signerSubjectKeyId = certImpl.getSubjectKeyId();15191520if (signerSubjectKeyId == null) {1521signerSubjectKeyId = new KeyIdentifier(issuerPubKey);1522}1523}15241525CertificateExtensions ext = createV3Extensions(1526reqex,1527null,1528v3ext,1529subjectPubKey,1530signerSubjectKeyId);1531info.set(X509CertInfo.EXTENSIONS, ext);1532X509CertImpl cert = new X509CertImpl(info);1533cert.sign(privateKey, sigAlgName);1534dumpCert(cert, out);1535for (Certificate ca: keyStore.getCertificateChain(alias)) {1536if (ca instanceof X509Certificate) {1537X509Certificate xca = (X509Certificate)ca;1538if (!KeyStoreUtil.isSelfSigned(xca)) {1539dumpCert(xca, out);1540}1541}1542}15431544checkWeak(rb.getString("the.issuer"), keyStore.getCertificateChain(alias));1545checkWeak(rb.getString("the.generated.certificate"), cert);1546}15471548private void doGenCRL(PrintStream out)1549throws Exception {1550if (ids == null) {1551throw new Exception("Must provide -id when -gencrl");1552}1553Certificate signerCert = keyStore.getCertificate(alias);1554byte[] encoded = signerCert.getEncoded();1555X509CertImpl signerCertImpl = new X509CertImpl(encoded);1556X509CertInfo signerCertInfo = (X509CertInfo)signerCertImpl.get(1557X509CertImpl.NAME + "." + X509CertImpl.INFO);1558X500Name owner = (X500Name)signerCertInfo.get(X509CertInfo.SUBJECT + "." +1559X509CertInfo.DN_NAME);15601561Date firstDate = getStartDate(startDate);1562Date lastDate = (Date) firstDate.clone();1563lastDate.setTime(lastDate.getTime() + validity*1000*24*60*60);1564CertificateValidity interval = new CertificateValidity(firstDate,1565lastDate);156615671568PrivateKey privateKey =1569(PrivateKey)recoverKey(alias, storePass, keyPass).fst;1570if (sigAlgName == null) {1571sigAlgName = getCompatibleSigAlgName(privateKey);1572}15731574X509CRLEntry[] badCerts = new X509CRLEntry[ids.size()];1575for (int i=0; i<ids.size(); i++) {1576String id = ids.get(i);1577int d = id.indexOf(':');1578if (d >= 0) {1579CRLExtensions ext = new CRLExtensions();1580ext.set("Reason", new CRLReasonCodeExtension(Integer.parseInt(id.substring(d+1))));1581badCerts[i] = new X509CRLEntryImpl(new BigInteger(id.substring(0, d)),1582firstDate, ext);1583} else {1584badCerts[i] = new X509CRLEntryImpl(new BigInteger(ids.get(i)), firstDate);1585}1586}1587X509CRLImpl crl = new X509CRLImpl(owner, firstDate, lastDate, badCerts);1588crl.sign(privateKey, sigAlgName);1589if (rfc) {1590out.println("-----BEGIN X509 CRL-----");1591out.println(Base64.getMimeEncoder(64, CRLF).encodeToString(crl.getEncodedInternal()));1592out.println("-----END X509 CRL-----");1593} else {1594out.write(crl.getEncodedInternal());1595}1596checkWeak(rb.getString("the.generated.crl"), crl, privateKey);1597}15981599/**1600* Creates a PKCS#10 cert signing request, corresponding to the1601* keys (and name) associated with a given alias.1602*/1603private void doCertReq(String alias, String sigAlgName, PrintStream out)1604throws Exception1605{1606if (alias == null) {1607alias = keyAlias;1608}16091610Pair<Key,char[]> objs = recoverKey(alias, storePass, keyPass);1611PrivateKey privKey = (PrivateKey)objs.fst;1612if (keyPass == null) {1613keyPass = objs.snd;1614}16151616Certificate cert = keyStore.getCertificate(alias);1617if (cert == null) {1618MessageFormat form = new MessageFormat1619(rb.getString("alias.has.no.public.key.certificate."));1620Object[] source = {alias};1621throw new Exception(form.format(source));1622}1623PKCS10 request = new PKCS10(cert.getPublicKey());1624CertificateExtensions ext = createV3Extensions(null, null, v3ext, cert.getPublicKey(), null);1625// Attribute name is not significant1626request.getAttributes().setAttribute(X509CertInfo.EXTENSIONS,1627new PKCS10Attribute(PKCS9Attribute.EXTENSION_REQUEST_OID, ext));16281629// Construct a Signature object, so that we can sign the request1630if (sigAlgName == null) {1631sigAlgName = getCompatibleSigAlgName(privKey);1632}16331634X500Name subject = dname == null?1635new X500Name(((X509Certificate)cert).getSubjectX500Principal().getEncoded()):1636new X500Name(dname);16371638// Sign the request and base-64 encode it1639request.encodeAndSign(subject, privKey, sigAlgName);1640request.print(out);16411642checkWeak(rb.getString("the.generated.certificate.request"), request);1643}16441645/**1646* Deletes an entry from the keystore.1647*/1648private void doDeleteEntry(String alias) throws Exception {1649if (keyStore.containsAlias(alias) == false) {1650MessageFormat form = new MessageFormat1651(rb.getString("Alias.alias.does.not.exist"));1652Object[] source = {alias};1653throw new Exception(form.format(source));1654}1655keyStore.deleteEntry(alias);1656}16571658/**1659* Exports a certificate from the keystore.1660*/1661private void doExportCert(String alias, PrintStream out)1662throws Exception1663{1664if (storePass == null1665&& !KeyStoreUtil.isWindowsKeyStore(storetype)1666&& !isPasswordlessKeyStore) {1667printNoIntegrityWarning();1668}1669if (alias == null) {1670alias = keyAlias;1671}1672if (keyStore.containsAlias(alias) == false) {1673MessageFormat form = new MessageFormat1674(rb.getString("Alias.alias.does.not.exist"));1675Object[] source = {alias};1676throw new Exception(form.format(source));1677}16781679X509Certificate cert = (X509Certificate)keyStore.getCertificate(alias);1680if (cert == null) {1681MessageFormat form = new MessageFormat1682(rb.getString("Alias.alias.has.no.certificate"));1683Object[] source = {alias};1684throw new Exception(form.format(source));1685}1686dumpCert(cert, out);1687checkWeak(rb.getString("the.certificate"), cert);1688}16891690/**1691* Prompt the user for a keypass when generating a key entry.1692* @param alias the entry we will set password for1693* @param orig the original entry of doing a dup, null if generate new1694* @param origPass the password to copy from if user press ENTER1695*/1696private char[] promptForKeyPass(String alias, String orig, char[] origPass) throws Exception{1697if (origPass != null && P12KEYSTORE.equalsIgnoreCase(storetype)) {1698return origPass;1699} else if (!token && !protectedPath) {1700// Prompt for key password1701int count;1702for (count = 0; count < 3; count++) {1703MessageFormat form = new MessageFormat(rb.getString1704("Enter.key.password.for.alias."));1705Object[] source = {alias};1706System.err.print(form.format(source));1707if (origPass != null) {1708System.err.println();1709if (orig == null) {1710System.err.print(rb.getString1711(".RETURN.if.same.as.keystore.password."));1712} else {1713form = new MessageFormat(rb.getString1714(".RETURN.if.same.as.for.otherAlias."));1715Object[] src = {orig};1716System.err.print(form.format(src));1717}1718}1719System.err.flush();1720char[] entered = Password.readPassword(System.in);1721passwords.add(entered);1722if (entered == null && origPass != null) {1723return origPass;1724} else if (entered != null && entered.length >= 6) {1725System.err.print(rb.getString("Re.enter.new.password."));1726char[] passAgain = Password.readPassword(System.in);1727passwords.add(passAgain);1728if (!Arrays.equals(entered, passAgain)) {1729System.err.println1730(rb.getString("They.don.t.match.Try.again"));1731continue;1732}1733return entered;1734} else {1735System.err.println(rb.getString1736("Key.password.is.too.short.must.be.at.least.6.characters"));1737}1738}1739if (count == 3) {1740if (command == KEYCLONE) {1741throw new Exception(rb.getString1742("Too.many.failures.Key.entry.not.cloned"));1743} else {1744throw new Exception(rb.getString1745("Too.many.failures.key.not.added.to.keystore"));1746}1747}1748}1749return null; // PKCS11, MSCAPI, or -protected1750}17511752/*1753* Prompt the user for the password credential to be stored.1754*/1755private char[] promptForCredential() throws Exception {1756// Handle password supplied via stdin1757if (System.console() == null) {1758char[] importPass = Password.readPassword(System.in);1759passwords.add(importPass);1760return importPass;1761}17621763int count;1764for (count = 0; count < 3; count++) {1765System.err.print(1766rb.getString("Enter.the.password.to.be.stored."));1767System.err.flush();1768char[] entered = Password.readPassword(System.in);1769passwords.add(entered);1770System.err.print(rb.getString("Re.enter.password."));1771char[] passAgain = Password.readPassword(System.in);1772passwords.add(passAgain);1773if (!Arrays.equals(entered, passAgain)) {1774System.err.println(rb.getString("They.don.t.match.Try.again"));1775continue;1776}1777return entered;1778}17791780if (count == 3) {1781throw new Exception(rb.getString1782("Too.many.failures.key.not.added.to.keystore"));1783}17841785return null;1786}17871788/**1789* Creates a new secret key.1790*/1791private void doGenSecretKey(String alias, String keyAlgName,1792int keysize)1793throws Exception1794{1795if (alias == null) {1796alias = keyAlias;1797}1798if (keyStore.containsAlias(alias)) {1799MessageFormat form = new MessageFormat(rb.getString1800("Secret.key.not.generated.alias.alias.already.exists"));1801Object[] source = {alias};1802throw new Exception(form.format(source));1803}18041805// Use the keystore's default PBE algorithm for entry protection1806boolean useDefaultPBEAlgorithm = true;1807SecretKey secKey = null;18081809if (keyAlgName.toUpperCase(Locale.ENGLISH).startsWith("PBE")) {1810SecretKeyFactory factory = SecretKeyFactory.getInstance("PBE");18111812// User is prompted for PBE credential1813secKey =1814factory.generateSecret(new PBEKeySpec(promptForCredential()));18151816// Check whether a specific PBE algorithm was specified1817if (!"PBE".equalsIgnoreCase(keyAlgName)) {1818useDefaultPBEAlgorithm = false;1819}18201821if (verbose) {1822MessageFormat form = new MessageFormat(rb.getString(1823"Generated.keyAlgName.secret.key"));1824Object[] source =1825{useDefaultPBEAlgorithm ? "PBE" : secKey.getAlgorithm()};1826System.err.println(form.format(source));1827}1828} else {1829KeyGenerator keygen = KeyGenerator.getInstance(keyAlgName);1830if (keysize == -1) {1831if ("DES".equalsIgnoreCase(keyAlgName)) {1832keysize = 56;1833} else if ("DESede".equalsIgnoreCase(keyAlgName)) {1834keysize = 168;1835} else {1836throw new Exception(rb.getString1837("Please.provide.keysize.for.secret.key.generation"));1838}1839}1840keygen.init(keysize);1841secKey = keygen.generateKey();18421843MessageFormat form = new MessageFormat(rb.getString1844("Generated.keysize.bit.keyAlgName.secret.key"));1845Object[] source = {keysize,1846secKey.getAlgorithm()};1847System.err.println(form.format(source));1848}18491850if (keyPass == null) {1851keyPass = promptForKeyPass(alias, null, storePass);1852}18531854if (useDefaultPBEAlgorithm) {1855keyStore.setKeyEntry(alias, secKey, keyPass, null);1856} else {1857keyStore.setEntry(alias, new KeyStore.SecretKeyEntry(secKey),1858new KeyStore.PasswordProtection(keyPass, keyAlgName, null));1859}1860}18611862/**1863* If no signature algorithm was specified at the command line,1864* we choose one that is compatible with the selected private key1865*/1866private static String getCompatibleSigAlgName(PrivateKey key)1867throws Exception {1868String result = SignatureUtil.getDefaultSigAlgForKey(key);1869if (result != null) {1870return result;1871} else {1872throw new Exception(rb.getString1873("Cannot.derive.signature.algorithm"));1874}1875}18761877/**1878* Creates a new key pair and self-signed certificate.1879*/1880private void doGenKeyPair(String alias, String dname, String keyAlgName,1881int keysize, String groupName, String sigAlgName,1882String signerAlias)1883throws Exception1884{1885if (groupName != null) {1886if (keysize != -1) {1887throw new Exception(rb.getString("groupname.keysize.coexist"));1888}1889} else {1890if (keysize == -1) {1891if ("EC".equalsIgnoreCase(keyAlgName)) {1892keysize = SecurityProviderConstants.DEF_EC_KEY_SIZE;1893} else if ("RSA".equalsIgnoreCase(keyAlgName)) {1894keysize = SecurityProviderConstants.DEF_RSA_KEY_SIZE;1895} else if ("DSA".equalsIgnoreCase(keyAlgName)) {1896keysize = SecurityProviderConstants.DEF_DSA_KEY_SIZE;1897} else if ("EdDSA".equalsIgnoreCase(keyAlgName)) {1898keysize = SecurityProviderConstants.DEF_ED_KEY_SIZE;1899} else if ("Ed25519".equalsIgnoreCase(keyAlgName)) {1900keysize = 255;1901} else if ("Ed448".equalsIgnoreCase(keyAlgName)) {1902keysize = 448;1903} else if ("XDH".equalsIgnoreCase(keyAlgName)) {1904keysize = SecurityProviderConstants.DEF_XEC_KEY_SIZE;1905} else if ("X25519".equalsIgnoreCase(keyAlgName)) {1906keysize = 255;1907} else if ("X448".equalsIgnoreCase(keyAlgName)) {1908keysize = 448;1909} else if ("DH".equalsIgnoreCase(keyAlgName)) {1910keysize = SecurityProviderConstants.DEF_DH_KEY_SIZE;1911}1912} else {1913if ("EC".equalsIgnoreCase(keyAlgName)) {1914weakWarnings.add(String.format(1915rb.getString("deprecate.keysize.for.ec"),1916ecGroupNameForSize(keysize)));1917}1918}1919}19201921if (alias == null) {1922alias = keyAlias;1923}19241925if (keyStore.containsAlias(alias)) {1926MessageFormat form = new MessageFormat(rb.getString1927("Key.pair.not.generated.alias.alias.already.exists"));1928Object[] source = {alias};1929throw new Exception(form.format(source));1930}19311932CertAndKeyGen keypair;1933KeyIdentifier signerSubjectKeyId = null;1934if (signerAlias != null) {1935PrivateKey signerPrivateKey =1936(PrivateKey)recoverKey(signerAlias, storePass, signerKeyPass).fst;1937Certificate signerCert = keyStore.getCertificate(signerAlias);19381939X509CertImpl signerCertImpl;1940if (signerCert instanceof X509CertImpl) {1941signerCertImpl = (X509CertImpl) signerCert;1942} else {1943signerCertImpl = new X509CertImpl(signerCert.getEncoded());1944}19451946X509CertInfo signerCertInfo = (X509CertInfo)signerCertImpl.get(1947X509CertImpl.NAME + "." + X509CertImpl.INFO);1948X500Name signerSubjectName = (X500Name)signerCertInfo.get(X509CertInfo.SUBJECT + "." +1949X509CertInfo.DN_NAME);19501951keypair = new CertAndKeyGen(keyAlgName, sigAlgName, providerName,1952signerPrivateKey, signerSubjectName);19531954signerSubjectKeyId = signerCertImpl.getSubjectKeyId();1955if (signerSubjectKeyId == null) {1956signerSubjectKeyId = new KeyIdentifier(signerCert.getPublicKey());1957}1958} else {1959keypair = new CertAndKeyGen(keyAlgName, sigAlgName, providerName);1960}19611962// If DN is provided, parse it. Otherwise, prompt the user for it.1963X500Name x500Name;1964if (dname == null) {1965printWeakWarnings(true);1966x500Name = getX500Name();1967} else {1968x500Name = new X500Name(dname);1969}19701971if (groupName != null) {1972keypair.generate(groupName);1973} else {1974// This covers keysize both specified and unspecified1975keypair.generate(keysize);1976}19771978CertificateExtensions ext = createV3Extensions(1979null,1980null,1981v3ext,1982keypair.getPublicKeyAnyway(),1983signerSubjectKeyId);19841985PrivateKey privKey = keypair.getPrivateKey();1986X509Certificate newCert = keypair.getSelfCertificate(1987x500Name, getStartDate(startDate), validity*24L*60L*60L, ext);19881989if (signerAlias != null) {1990MessageFormat form = new MessageFormat(rb.getString1991("Generating.keysize.bit.keyAlgName.key.pair.and.a.certificate.sigAlgName.issued.by.signerAlias.with.a.validity.of.validality.days.for"));1992Object[] source = {1993groupName == null ? keysize : KeyUtil.getKeySize(privKey),1994fullDisplayAlgName(privKey),1995newCert.getSigAlgName(),1996signerAlias,1997validity,1998x500Name};1999System.err.println(form.format(source));2000} else {2001MessageFormat form = new MessageFormat(rb.getString2002("Generating.keysize.bit.keyAlgName.key.pair.and.self.signed.certificate.sigAlgName.with.a.validity.of.validality.days.for"));2003Object[] source = {2004groupName == null ? keysize : KeyUtil.getKeySize(privKey),2005fullDisplayAlgName(privKey),2006newCert.getSigAlgName(),2007validity,2008x500Name};2009System.err.println(form.format(source));2010}20112012if (keyPass == null) {2013keyPass = promptForKeyPass(alias, null, storePass);2014}20152016Certificate[] finalChain;2017if (signerAlias != null) {2018Certificate[] signerChain = keyStore.getCertificateChain(signerAlias);2019finalChain = new X509Certificate[signerChain.length + 1];2020finalChain[0] = newCert;2021System.arraycopy(signerChain, 0, finalChain, 1, signerChain.length);2022} else {2023finalChain = new Certificate[] { newCert };2024}2025checkWeak(rb.getString("the.generated.certificate"), finalChain);2026keyStore.setKeyEntry(alias, privKey, keyPass, finalChain);2027}20282029private String ecGroupNameForSize(int size) throws Exception {2030AlgorithmParameters ap = AlgorithmParameters.getInstance("EC");2031ap.init(new ECKeySizeParameterSpec(size));2032// The following line assumes the toString value is "name (oid)"2033return ap.toString().split(" ")[0];2034}20352036/**2037* Clones an entry2038* @param orig original alias2039* @param dest destination alias2040* @changePassword if the password can be changed2041*/2042private void doCloneEntry(String orig, String dest, boolean changePassword)2043throws Exception2044{2045if (orig == null) {2046orig = keyAlias;2047}20482049if (keyStore.containsAlias(dest)) {2050MessageFormat form = new MessageFormat2051(rb.getString("Destination.alias.dest.already.exists"));2052Object[] source = {dest};2053throw new Exception(form.format(source));2054}20552056Pair<Entry,char[]> objs = recoverEntry(keyStore, orig, storePass, keyPass);2057Entry entry = objs.fst;2058keyPass = objs.snd;20592060PasswordProtection pp = null;20612062if (keyPass != null) { // protected2063if (!changePassword || P12KEYSTORE.equalsIgnoreCase(storetype)) {2064keyPassNew = keyPass;2065} else {2066if (keyPassNew == null) {2067keyPassNew = promptForKeyPass(dest, orig, keyPass);2068}2069}2070pp = new PasswordProtection(keyPassNew);2071}2072keyStore.setEntry(dest, entry, pp);2073}20742075/**2076* Changes a key password.2077*/2078private void doChangeKeyPasswd(String alias) throws Exception2079{20802081if (alias == null) {2082alias = keyAlias;2083}2084Pair<Key,char[]> objs = recoverKey(alias, storePass, keyPass);2085Key privKey = objs.fst;2086if (keyPass == null) {2087keyPass = objs.snd;2088}20892090if (keyPassNew == null) {2091MessageFormat form = new MessageFormat2092(rb.getString("key.password.for.alias."));2093Object[] source = {alias};2094keyPassNew = getNewPasswd(form.format(source), keyPass);2095}2096keyStore.setKeyEntry(alias, privKey, keyPassNew,2097keyStore.getCertificateChain(alias));2098}20992100/**2101* Imports a JDK 1.1-style identity database. We can only store one2102* certificate per identity, because we use the identity's name as the2103* alias (which references a keystore entry), and aliases must be unique.2104*/2105private void doImportIdentityDatabase(InputStream in)2106throws Exception2107{2108System.err.println(rb.getString2109("No.entries.from.identity.database.added"));2110}21112112/**2113* Prints a single keystore entry.2114*/2115private void doPrintEntry(String label, String alias, PrintStream out)2116throws Exception2117{2118if (keyStore.containsAlias(alias) == false) {2119MessageFormat form = new MessageFormat2120(rb.getString("Alias.alias.does.not.exist"));2121Object[] source = {alias};2122throw new Exception(form.format(source));2123}21242125if (verbose || rfc || debug) {2126MessageFormat form = new MessageFormat2127(rb.getString("Alias.name.alias"));2128Object[] source = {alias};2129out.println(form.format(source));21302131if (!token) {2132form = new MessageFormat(rb.getString2133("Creation.date.keyStore.getCreationDate.alias."));2134Object[] src = {keyStore.getCreationDate(alias)};2135out.println(form.format(src));2136}2137} else {2138if (!token) {2139MessageFormat form = new MessageFormat2140(rb.getString("alias.keyStore.getCreationDate.alias."));2141Object[] source = {alias, keyStore.getCreationDate(alias)};2142out.print(form.format(source));2143} else {2144MessageFormat form = new MessageFormat2145(rb.getString("alias."));2146Object[] source = {alias};2147out.print(form.format(source));2148}2149}21502151if (keyStore.entryInstanceOf(alias, KeyStore.SecretKeyEntry.class)) {2152if (verbose || rfc || debug) {2153Object[] source = {"SecretKeyEntry"};2154out.println(new MessageFormat(2155rb.getString("Entry.type.type.")).format(source));2156} else {2157out.println("SecretKeyEntry, ");2158}2159} else if (keyStore.entryInstanceOf(alias, KeyStore.PrivateKeyEntry.class)) {2160if (verbose || rfc || debug) {2161Object[] source = {"PrivateKeyEntry"};2162out.println(new MessageFormat(2163rb.getString("Entry.type.type.")).format(source));2164} else {2165out.println("PrivateKeyEntry, ");2166}21672168// Get the chain2169Certificate[] chain = keyStore.getCertificateChain(alias);2170if (chain != null) {2171if (verbose || rfc || debug) {2172out.println(rb.getString2173("Certificate.chain.length.") + chain.length);2174for (int i = 0; i < chain.length; i ++) {2175MessageFormat form = new MessageFormat2176(rb.getString("Certificate.i.1."));2177Object[] source = {(i + 1)};2178out.println(form.format(source));2179if (verbose && (chain[i] instanceof X509Certificate)) {2180printX509Cert((X509Certificate)(chain[i]), out);2181} else if (debug) {2182out.println(chain[i].toString());2183} else {2184dumpCert(chain[i], out);2185}2186checkWeak(label, chain[i]);2187}2188} else {2189// Print the digest of the user cert only2190out.println2191(rb.getString("Certificate.fingerprint.SHA.256.") +2192getCertFingerPrint("SHA-256", chain[0]));2193checkWeak(label, chain);2194}2195} else {2196out.println(rb.getString2197("Certificate.chain.length.") + 0);2198}2199} else if (keyStore.entryInstanceOf(alias,2200KeyStore.TrustedCertificateEntry.class)) {2201// We have a trusted certificate entry2202Certificate cert = keyStore.getCertificate(alias);2203Object[] source = {"trustedCertEntry"};2204String mf = new MessageFormat(2205rb.getString("Entry.type.type.")).format(source) + "\n";2206if (verbose && (cert instanceof X509Certificate)) {2207out.println(mf);2208printX509Cert((X509Certificate)cert, out);2209} else if (rfc) {2210out.println(mf);2211dumpCert(cert, out);2212} else if (debug) {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 = new Date();3036lastDate.setTime(firstDate.getTime() + validity*1000L*24L*60L*60L);3037CertificateValidity interval = new CertificateValidity(firstDate,3038lastDate);3039certInfo.set(X509CertInfo.VALIDITY, interval);30403041// Make new serial number3042certInfo.set(X509CertInfo.SERIAL_NUMBER,3043CertificateSerialNumber.newRandom64bit(new SecureRandom()));30443045// Set owner and issuer fields3046X500Name owner;3047if (dname == null) {3048// Get the owner name from the certificate3049owner = (X500Name)certInfo.get(X509CertInfo.SUBJECT + "." +3050X509CertInfo.DN_NAME);3051} else {3052// Use the owner name specified at the command line3053owner = new X500Name(dname);3054certInfo.set(X509CertInfo.SUBJECT + "." +3055X509CertInfo.DN_NAME, owner);3056}3057// Make issuer same as owner (self-signed!)3058certInfo.set(X509CertInfo.ISSUER + "." +3059X509CertInfo.DN_NAME, owner);30603061certInfo.set(X509CertInfo.VERSION,3062new CertificateVersion(CertificateVersion.V3));30633064CertificateExtensions ext = createV3Extensions(3065null,3066(CertificateExtensions)certInfo.get(X509CertInfo.EXTENSIONS),3067v3ext,3068oldCert.getPublicKey(),3069null);3070certInfo.set(X509CertInfo.EXTENSIONS, ext);3071// Sign the new certificate3072X509CertImpl newCert = new X509CertImpl(certInfo);3073newCert.sign(privKey, sigAlgName);30743075// Store the new certificate as a single-element certificate chain3076keyStore.setKeyEntry(alias, privKey,3077(keyPass != null) ? keyPass : storePass,3078new Certificate[] { newCert } );30793080if (verbose) {3081System.err.println(rb.getString("New.certificate.self.signed."));3082System.err.print(newCert.toString());3083System.err.println();3084}3085}30863087/**3088* Processes a certificate reply from a certificate authority.3089*3090* <p>Builds a certificate chain on top of the certificate reply,3091* using trusted certificates from the keystore. The chain is complete3092* after a self-signed certificate has been encountered. The self-signed3093* certificate is considered a root certificate authority, and is stored3094* at the end of the chain.3095*3096* <p>The newly generated chain replaces the old chain associated with the3097* key entry.3098*3099* @return true if the certificate reply was installed, otherwise false.3100*/3101private boolean installReply(String alias, InputStream in)3102throws Exception3103{3104if (alias == null) {3105alias = keyAlias;3106}31073108Pair<Key,char[]> objs = recoverKey(alias, storePass, keyPass);3109PrivateKey privKey = (PrivateKey)objs.fst;3110if (keyPass == null) {3111keyPass = objs.snd;3112}31133114Certificate userCert = keyStore.getCertificate(alias);3115if (userCert == null) {3116MessageFormat form = new MessageFormat3117(rb.getString("alias.has.no.public.key.certificate."));3118Object[] source = {alias};3119throw new Exception(form.format(source));3120}31213122// Read the certificates in the reply3123Collection<? extends Certificate> c = generateCertificates(in);3124if (c.isEmpty()) {3125throw new Exception(rb.getString("Reply.has.no.certificates"));3126}3127Certificate[] replyCerts = c.toArray(new Certificate[c.size()]);3128Certificate[] newChain;3129if (replyCerts.length == 1) {3130// single-cert reply3131newChain = establishCertChain(userCert, replyCerts[0]);3132} else {3133// cert-chain reply (e.g., PKCS#7)3134newChain = validateReply(alias, userCert, replyCerts);3135}31363137// Now store the newly established chain in the keystore. The new3138// chain replaces the old one. The chain can be null if user chooses no.3139if (newChain != null) {3140keyStore.setKeyEntry(alias, privKey,3141(keyPass != null) ? keyPass : storePass,3142newChain);3143return true;3144} else {3145return false;3146}3147}31483149/**3150* Imports a certificate and adds it to the list of trusted certificates.3151*3152* @return true if the certificate was added, otherwise false.3153*/3154private boolean addTrustedCert(String alias, InputStream in)3155throws Exception3156{3157if (alias == null) {3158throw new Exception(rb.getString("Must.specify.alias"));3159}3160if (keyStore.containsAlias(alias)) {3161MessageFormat form = new MessageFormat(rb.getString3162("Certificate.not.imported.alias.alias.already.exists"));3163Object[] source = {alias};3164throw new Exception(form.format(source));3165}31663167// Read the certificate3168X509Certificate cert = null;3169try {3170cert = (X509Certificate)generateCertificate(in);3171} catch (ClassCastException | CertificateException ce) {3172throw new Exception(rb.getString("Input.not.an.X.509.certificate"));3173}31743175if (noprompt) {3176checkWeak(rb.getString("the.input"), cert);3177keyStore.setCertificateEntry(alias, cert);3178return true;3179}31803181// if certificate is self-signed, make sure it verifies3182boolean selfSigned = false;3183if (KeyStoreUtil.isSelfSigned(cert)) {3184cert.verify(cert.getPublicKey());3185selfSigned = true;3186}31873188// check if cert already exists in keystore3189String reply = null;3190String trustalias = keyStore.getCertificateAlias(cert);3191if (trustalias != null) {3192MessageFormat form = new MessageFormat(rb.getString3193("Certificate.already.exists.in.keystore.under.alias.trustalias."));3194Object[] source = {trustalias};3195System.err.println(form.format(source));3196checkWeak(rb.getString("the.input"), cert);3197printWeakWarnings(true);3198reply = getYesNoReply3199(rb.getString("Do.you.still.want.to.add.it.no."));3200} else if (selfSigned) {3201if (trustcacerts && (caks != null) &&3202((trustalias=caks.getCertificateAlias(cert)) != null)) {3203MessageFormat form = new MessageFormat(rb.getString3204("Certificate.already.exists.in.system.wide.CA.keystore.under.alias.trustalias."));3205Object[] source = {trustalias};3206System.err.println(form.format(source));3207checkWeak(rb.getString("the.input"), cert);3208printWeakWarnings(true);3209reply = getYesNoReply3210(rb.getString("Do.you.still.want.to.add.it.to.your.own.keystore.no."));3211}3212if (trustalias == null) {3213// Print the cert and ask user if they really want to add3214// it to their keystore3215printX509Cert(cert, System.out);3216checkWeak(rb.getString("the.input"), cert);3217printWeakWarnings(true);3218reply = getYesNoReply3219(rb.getString("Trust.this.certificate.no."));3220}3221}3222if (reply != null) {3223if ("YES".equals(reply)) {3224keyStore.setCertificateEntry(alias, cert);3225return true;3226} else {3227return false;3228}3229}32303231// Not found in this keystore and not self-signed3232// Try to establish trust chain3233try {3234Certificate[] chain = establishCertChain(null, cert);3235if (chain != null) {3236keyStore.setCertificateEntry(alias, cert);3237return true;3238}3239} catch (Exception e) {3240// Print the cert and ask user if they really want to add it to3241// their keystore3242printX509Cert(cert, System.out);3243checkWeak(rb.getString("the.input"), cert);3244printWeakWarnings(true);3245reply = getYesNoReply3246(rb.getString("Trust.this.certificate.no."));3247if ("YES".equals(reply)) {3248keyStore.setCertificateEntry(alias, cert);3249return true;3250} else {3251return false;3252}3253}32543255return false;3256}32573258/**3259* Prompts user for new password. New password must be different from3260* old one.3261*3262* @param prompt the message that gets prompted on the screen3263* @param oldPasswd the current (i.e., old) password3264*/3265private char[] getNewPasswd(String prompt, char[] oldPasswd)3266throws Exception3267{3268char[] entered = null;3269char[] reentered = null;32703271for (int count = 0; count < 3; count++) {3272MessageFormat form = new MessageFormat3273(rb.getString("New.prompt."));3274Object[] source = {prompt};3275System.err.print(form.format(source));3276entered = Password.readPassword(System.in);3277passwords.add(entered);3278if (entered == null || entered.length < 6) {3279System.err.println(rb.getString3280("Password.is.too.short.must.be.at.least.6.characters"));3281} else if (Arrays.equals(entered, oldPasswd)) {3282System.err.println(rb.getString("Passwords.must.differ"));3283} else {3284form = new MessageFormat3285(rb.getString("Re.enter.new.prompt."));3286Object[] src = {prompt};3287System.err.print(form.format(src));3288reentered = Password.readPassword(System.in);3289passwords.add(reentered);3290if (!Arrays.equals(entered, reentered)) {3291System.err.println3292(rb.getString("They.don.t.match.Try.again"));3293} else {3294Arrays.fill(reentered, ' ');3295return entered;3296}3297}3298if (entered != null) {3299Arrays.fill(entered, ' ');3300entered = null;3301}3302if (reentered != null) {3303Arrays.fill(reentered, ' ');3304reentered = null;3305}3306}3307throw new Exception(rb.getString("Too.many.failures.try.later"));3308}33093310/**3311* Prompts user for alias name.3312* @param prompt the {0} of "Enter {0} alias name: " in prompt line3313* @return the string entered by the user, without the \n at the end3314*/3315private String getAlias(String prompt) throws Exception {3316if (prompt != null) {3317MessageFormat form = new MessageFormat3318(rb.getString("Enter.prompt.alias.name."));3319Object[] source = {prompt};3320System.err.print(form.format(source));3321} else {3322System.err.print(rb.getString("Enter.alias.name."));3323}3324return (new BufferedReader(new InputStreamReader(3325System.in))).readLine();3326}33273328/**3329* Prompts user for an input string from the command line (System.in)3330* @prompt the prompt string printed3331* @return the string entered by the user, without the \n at the end3332*/3333private String inputStringFromStdin(String prompt) throws Exception {3334System.err.print(prompt);3335return (new BufferedReader(new InputStreamReader(3336System.in))).readLine();3337}33383339/**3340* Prompts user for key password. User may select to choose the same3341* password (<code>otherKeyPass</code>) as for <code>otherAlias</code>.3342*/3343private char[] getKeyPasswd(String alias, String otherAlias,3344char[] otherKeyPass)3345throws Exception3346{3347int count = 0;3348char[] keyPass = null;33493350do {3351if (otherKeyPass != null) {3352MessageFormat form = new MessageFormat(rb.getString3353("Enter.key.password.for.alias."));3354Object[] source = {alias};3355System.err.println(form.format(source));33563357form = new MessageFormat(rb.getString3358(".RETURN.if.same.as.for.otherAlias."));3359Object[] src = {otherAlias};3360System.err.print(form.format(src));3361} else {3362MessageFormat form = new MessageFormat(rb.getString3363("Enter.key.password.for.alias."));3364Object[] source = {alias};3365System.err.print(form.format(source));3366}3367System.err.flush();3368keyPass = Password.readPassword(System.in);3369passwords.add(keyPass);3370if (keyPass == null) {3371keyPass = otherKeyPass;3372}3373count++;3374} while ((keyPass == null) && count < 3);33753376if (keyPass == null) {3377throw new Exception(rb.getString("Too.many.failures.try.later"));3378}33793380return keyPass;3381}33823383private String withWeak(String alg) {3384if (DISABLED_CHECK.permits(SIG_PRIMITIVE_SET, alg, null)) {3385if (LEGACY_CHECK.permits(SIG_PRIMITIVE_SET, alg, null)) {3386return alg;3387} else {3388return String.format(rb.getString("with.weak"), alg);3389}3390} else {3391return String.format(rb.getString("with.disabled"), alg);3392}3393}33943395private String fullDisplayAlgName(Key key) {3396String result = key.getAlgorithm();3397if (key instanceof ECKey) {3398ECParameterSpec paramSpec = ((ECKey) key).getParams();3399if (paramSpec instanceof NamedCurve) {3400NamedCurve nc = (NamedCurve)paramSpec;3401result += " (" + nc.getNameAndAliases()[0] + ")";3402}3403} else if (key instanceof EdECKey) {3404result = ((EdECKey) key).getParams().getName();3405}3406return result;3407}34083409private String withWeak(Key key) {3410int kLen = KeyUtil.getKeySize(key);3411String displayAlg = fullDisplayAlgName(key);3412if (DISABLED_CHECK.permits(SIG_PRIMITIVE_SET, key)) {3413if (LEGACY_CHECK.permits(SIG_PRIMITIVE_SET, key)) {3414if (kLen >= 0) {3415return String.format(rb.getString("key.bit"), kLen, displayAlg);3416} else {3417return String.format(rb.getString("unknown.size.1"), displayAlg);3418}3419} else {3420return String.format(rb.getString("key.bit.weak"), kLen, displayAlg);3421}3422} else {3423return String.format(rb.getString("key.bit.disabled"), kLen, displayAlg);3424}3425}34263427/**3428* Prints a certificate in a human readable format.3429*/3430private void printX509Cert(X509Certificate cert, PrintStream out)3431throws Exception3432{34333434MessageFormat form = new MessageFormat3435(rb.getString(".PATTERN.printX509Cert.with.weak"));3436PublicKey pkey = cert.getPublicKey();3437String sigName = cert.getSigAlgName();3438// No need to warn about sigalg of a trust anchor3439if (!isTrustedCert(cert)) {3440sigName = withWeak(sigName);3441}3442Object[] source = {cert.getSubjectX500Principal().toString(),3443cert.getIssuerX500Principal().toString(),3444cert.getSerialNumber().toString(16),3445cert.getNotBefore().toString(),3446cert.getNotAfter().toString(),3447getCertFingerPrint("SHA-1", cert),3448getCertFingerPrint("SHA-256", cert),3449sigName,3450withWeak(pkey),3451cert.getVersion()3452};3453out.println(form.format(source));34543455if (cert instanceof X509CertImpl) {3456X509CertImpl impl = (X509CertImpl)cert;3457X509CertInfo certInfo = (X509CertInfo)impl.get(X509CertImpl.NAME3458+ "." +3459X509CertImpl.INFO);3460CertificateExtensions exts = (CertificateExtensions)3461certInfo.get(X509CertInfo.EXTENSIONS);3462if (exts != null) {3463printExtensions(rb.getString("Extensions."), exts, out);3464}3465}3466}34673468private static void printExtensions(String title, CertificateExtensions exts, PrintStream out)3469throws Exception {3470int extnum = 0;3471Iterator<Extension> i1 = exts.getAllExtensions().iterator();3472Iterator<Extension> i2 = exts.getUnparseableExtensions().values().iterator();3473while (i1.hasNext() || i2.hasNext()) {3474Extension ext = i1.hasNext()?i1.next():i2.next();3475if (extnum == 0) {3476out.println();3477out.println(title);3478out.println();3479}3480out.print("#"+(++extnum)+": "+ ext);3481if (ext.getClass() == Extension.class) {3482byte[] v = ext.getExtensionValue();3483if (v.length == 0) {3484out.println(rb.getString(".Empty.value."));3485} else {3486new sun.security.util.HexDumpEncoder().encodeBuffer(ext.getExtensionValue(), out);3487out.println();3488}3489}3490out.println();3491}3492}34933494/**3495* Locates a signer for a given certificate from a given keystore and3496* returns the signer's certificate.3497* @param cert the certificate whose signer is searched, not null3498* @param ks the keystore to search with, not null3499* @return <code>cert</code> itself if it's already inside <code>ks</code>,3500* or a certificate inside <code>ks</code> who signs <code>cert</code>,3501* or null otherwise. A label is added.3502*/3503private static Pair<String,Certificate>3504getSigner(Certificate cert, KeyStore ks) throws Exception {3505if (ks.getCertificateAlias(cert) != null) {3506return new Pair<>("", cert);3507}3508for (Enumeration<String> aliases = ks.aliases();3509aliases.hasMoreElements(); ) {3510String name = aliases.nextElement();3511Certificate trustedCert = ks.getCertificate(name);3512if (trustedCert != null) {3513try {3514cert.verify(trustedCert.getPublicKey());3515return new Pair<>(name, trustedCert);3516} catch (Exception e) {3517// Not verified, skip to the next one3518}3519}3520}3521return null;3522}35233524/**3525* Gets an X.500 name suitable for inclusion in a certification request.3526*/3527private X500Name getX500Name() throws IOException {3528BufferedReader in;3529in = new BufferedReader(new InputStreamReader(System.in));3530String commonName = "Unknown";3531String organizationalUnit = "Unknown";3532String organization = "Unknown";3533String city = "Unknown";3534String state = "Unknown";3535String country = "Unknown";3536X500Name name;3537String userInput = null;35383539int maxRetry = 20;3540do {3541if (maxRetry-- < 0) {3542throw new RuntimeException(rb.getString(3543"Too.many.retries.program.terminated"));3544}3545commonName = inputString(in,3546rb.getString("What.is.your.first.and.last.name."),3547commonName);3548organizationalUnit = inputString(in,3549rb.getString3550("What.is.the.name.of.your.organizational.unit."),3551organizationalUnit);3552organization = inputString(in,3553rb.getString("What.is.the.name.of.your.organization."),3554organization);3555city = inputString(in,3556rb.getString("What.is.the.name.of.your.City.or.Locality."),3557city);3558state = inputString(in,3559rb.getString("What.is.the.name.of.your.State.or.Province."),3560state);3561country = inputString(in,3562rb.getString3563("What.is.the.two.letter.country.code.for.this.unit."),3564country);3565name = new X500Name(commonName, organizationalUnit, organization,3566city, state, country);3567MessageFormat form = new MessageFormat3568(rb.getString("Is.name.correct."));3569Object[] source = {name};3570userInput = inputString3571(in, form.format(source), rb.getString("no"));3572} while (collator.compare(userInput, rb.getString("yes")) != 0 &&3573collator.compare(userInput, rb.getString("y")) != 0);35743575System.err.println();3576return name;3577}35783579private String inputString(BufferedReader in, String prompt,3580String defaultValue)3581throws IOException3582{3583System.err.println(prompt);3584MessageFormat form = new MessageFormat3585(rb.getString(".defaultValue."));3586Object[] source = {defaultValue};3587System.err.print(form.format(source));3588System.err.flush();35893590String value = in.readLine();3591if (value == null || collator.compare(value, "") == 0) {3592value = defaultValue;3593}3594return value;3595}35963597/**3598* Writes an X.509 certificate in base64 or binary encoding to an output3599* stream.3600*/3601private void dumpCert(Certificate cert, PrintStream out)3602throws IOException, CertificateException3603{3604if (rfc) {3605out.println(X509Factory.BEGIN_CERT);3606out.println(Base64.getMimeEncoder(64, CRLF).encodeToString(cert.getEncoded()));3607out.println(X509Factory.END_CERT);3608} else {3609out.write(cert.getEncoded()); // binary3610}3611}36123613/**3614* Recovers (private) key associated with given alias.3615*3616* @return an array of objects, where the 1st element in the array is the3617* recovered private key, and the 2nd element is the password used to3618* recover it.3619*/3620private Pair<Key,char[]> recoverKey(String alias, char[] storePass,3621char[] keyPass)3622throws Exception3623{3624Key key = null;36253626if (KeyStoreUtil.isWindowsKeyStore(storetype)) {3627key = keyStore.getKey(alias, null);3628return Pair.of(key, null);3629}36303631if (keyStore.containsAlias(alias) == false) {3632MessageFormat form = new MessageFormat3633(rb.getString("Alias.alias.does.not.exist"));3634Object[] source = {alias};3635throw new Exception(form.format(source));3636}3637if (!keyStore.entryInstanceOf(alias, KeyStore.PrivateKeyEntry.class) &&3638!keyStore.entryInstanceOf(alias, KeyStore.SecretKeyEntry.class)) {3639MessageFormat form = new MessageFormat3640(rb.getString("Alias.alias.has.no.key"));3641Object[] source = {alias};3642throw new Exception(form.format(source));3643}36443645if (keyPass == null) {3646// Try to recover the key using the keystore password3647if (storePass != null) {3648try {3649key = keyStore.getKey(alias, storePass);3650passwords.add(storePass);3651return Pair.of(key, storePass);3652} catch (UnrecoverableKeyException e) {3653if (token) {3654throw e;3655}3656}3657}3658// prompt user for key password3659keyPass = getKeyPasswd(alias, null, null);3660key = keyStore.getKey(alias, keyPass);3661return Pair.of(key, keyPass);3662} else {3663key = keyStore.getKey(alias, keyPass);3664return Pair.of(key, keyPass);3665}3666}36673668/**3669* Recovers entry associated with given alias.3670*3671* @return an array of objects, where the 1st element in the array is the3672* recovered entry, and the 2nd element is the password used to3673* recover it (null if no password).3674*/3675private Pair<Entry,char[]> recoverEntry(KeyStore ks,3676String alias,3677char[] pstore,3678char[] pkey) throws Exception {36793680if (!ks.containsAlias(alias)) {3681MessageFormat form = new MessageFormat(3682rb.getString("Alias.alias.does.not.exist"));3683Object[] source = {alias};3684throw new Exception(form.format(source));3685}36863687// Step 1: First attempt to access entry without key password3688// (PKCS11 entry or trusted certificate entry, for example).3689// If fail, go next.3690try {3691Entry entry = ks.getEntry(alias, null);3692return Pair.of(entry, null);3693} catch (UnrecoverableEntryException une) {3694if(P11KEYSTORE.equalsIgnoreCase(ks.getType()) ||3695KeyStoreUtil.isWindowsKeyStore(ks.getType())) {3696// should not happen, but a possibility3697throw une;3698}3699}37003701// entry is protected37023703// Step 2: try pkey if not null. If fail, fail.3704if (pkey != null) {3705PasswordProtection pp = new PasswordProtection(pkey);3706Entry entry = ks.getEntry(alias, pp);3707return Pair.of(entry, pkey);3708}37093710// Step 3: try pstore if not null. If fail, go next.3711if (pstore != null) {3712try {3713PasswordProtection pp = new PasswordProtection(pstore);3714Entry entry = ks.getEntry(alias, pp);3715return Pair.of(entry, pstore);3716} catch (UnrecoverableEntryException une) {3717if (P12KEYSTORE.equalsIgnoreCase(ks.getType())) {3718// P12 keystore currently does not support separate3719// store and entry passwords. We will not prompt for3720// entry password.3721throw une;3722}3723}3724}37253726// Step 4: prompt for entry password3727pkey = getKeyPasswd(alias, null, null);3728PasswordProtection pp = new PasswordProtection(pkey);3729Entry entry = ks.getEntry(alias, pp);3730return Pair.of(entry, pkey);3731}37323733/**3734* Gets the requested finger print of the certificate.3735*/3736private String getCertFingerPrint(String mdAlg, Certificate cert)3737throws Exception3738{3739byte[] encCertInfo = cert.getEncoded();3740MessageDigest md = MessageDigest.getInstance(mdAlg);3741byte[] digest = md.digest(encCertInfo);3742return HexFormat.ofDelimiter(":").withUpperCase().formatHex(digest);3743}37443745/**3746* Prints warning about missing integrity check.3747*/3748private void printNoIntegrityWarning() {3749System.err.println();3750System.err.println(rb.getString3751(".WARNING.WARNING.WARNING."));3752System.err.println(rb.getString3753(".The.integrity.of.the.information.stored.in.your.keystore."));3754System.err.println(rb.getString3755(".WARNING.WARNING.WARNING."));3756System.err.println();3757}37583759/**3760* Validates chain in certification reply, and returns the ordered3761* elements of the chain (with user certificate first, and root3762* certificate last in the array).3763*3764* @param alias the alias name3765* @param userCert the user certificate of the alias3766* @param replyCerts the chain provided in the reply3767*/3768private Certificate[] validateReply(String alias,3769Certificate userCert,3770Certificate[] replyCerts)3771throws Exception3772{37733774checkWeak(rb.getString("reply"), replyCerts);37753776// order the certs in the reply (bottom-up).3777// we know that all certs in the reply are of type X.509, because3778// we parsed them using an X.509 certificate factory3779int i;3780PublicKey userPubKey = userCert.getPublicKey();37813782// Remove duplicated certificates.3783HashSet<Certificate> nodup = new HashSet<>(Arrays.asList(replyCerts));3784replyCerts = nodup.toArray(new Certificate[nodup.size()]);37853786for (i=0; i<replyCerts.length; i++) {3787if (userPubKey.equals(replyCerts[i].getPublicKey())) {3788break;3789}3790}3791if (i == replyCerts.length) {3792MessageFormat form = new MessageFormat(rb.getString3793("Certificate.reply.does.not.contain.public.key.for.alias."));3794Object[] source = {alias};3795throw new Exception(form.format(source));3796}37973798Certificate tmpCert = replyCerts[0];3799replyCerts[0] = replyCerts[i];3800replyCerts[i] = tmpCert;38013802X509Certificate thisCert = (X509Certificate)replyCerts[0];38033804for (i=1; i < replyCerts.length-1; i++) {3805// find a cert in the reply who signs thisCert3806int j;3807for (j=i; j<replyCerts.length; j++) {3808if (KeyStoreUtil.signedBy(thisCert, (X509Certificate)replyCerts[j])) {3809tmpCert = replyCerts[i];3810replyCerts[i] = replyCerts[j];3811replyCerts[j] = tmpCert;3812thisCert = (X509Certificate)replyCerts[i];3813break;3814}3815}3816if (j == replyCerts.length) {3817throw new Exception3818(rb.getString("Incomplete.certificate.chain.in.reply"));3819}3820}38213822if (noprompt) {3823return replyCerts;3824}38253826// do we trust the cert at the top?3827Certificate topCert = replyCerts[replyCerts.length-1];3828boolean fromKeyStore = true;3829Pair<String,Certificate> root = getSigner(topCert, keyStore);3830if (root == null && trustcacerts && caks != null) {3831root = getSigner(topCert, caks);3832fromKeyStore = false;3833}3834if (root == null) {3835System.err.println();3836System.err.println3837(rb.getString("Top.level.certificate.in.reply."));3838printX509Cert((X509Certificate)topCert, System.out);3839System.err.println();3840System.err.print(rb.getString(".is.not.trusted."));3841printWeakWarnings(true);3842String reply = getYesNoReply3843(rb.getString("Install.reply.anyway.no."));3844if ("NO".equals(reply)) {3845return null;3846}3847} else {3848if (root.snd != topCert) {3849// append the root CA cert to the chain3850Certificate[] tmpCerts =3851new Certificate[replyCerts.length+1];3852System.arraycopy(replyCerts, 0, tmpCerts, 0,3853replyCerts.length);3854tmpCerts[tmpCerts.length-1] = root.snd;3855replyCerts = tmpCerts;3856checkWeak(String.format(fromKeyStore3857? rb.getString("alias.in.keystore")3858: rb.getString("alias.in.cacerts"),3859root.fst),3860root.snd);3861}3862}3863return replyCerts;3864}38653866/**3867* Establishes a certificate chain (using trusted certificates in the3868* keystore and cacerts), starting with the reply (certToVerify)3869* and ending at a self-signed certificate found in the keystore.3870*3871* @param userCert optional existing certificate, mostly likely be the3872* original self-signed cert created by -genkeypair.3873* It must have the same public key as certToVerify3874* but cannot be the same cert.3875* @param certToVerify the starting certificate to build the chain3876* @returns the established chain, might be null if user decides not3877*/3878private Certificate[] establishCertChain(Certificate userCert,3879Certificate certToVerify)3880throws Exception3881{3882if (userCert != null) {3883// Make sure that the public key of the certificate reply matches3884// the original public key in the keystore3885PublicKey origPubKey = userCert.getPublicKey();3886PublicKey replyPubKey = certToVerify.getPublicKey();3887if (!origPubKey.equals(replyPubKey)) {3888throw new Exception(rb.getString3889("Public.keys.in.reply.and.keystore.don.t.match"));3890}38913892// If the two certs are identical, we're done: no need to import3893// anything3894if (certToVerify.equals(userCert)) {3895throw new Exception(rb.getString3896("Certificate.reply.and.certificate.in.keystore.are.identical"));3897}3898}38993900// Build a hash table of all certificates in the keystore.3901// Use the subject distinguished name as the key into the hash table.3902// All certificates associated with the same subject distinguished3903// name are stored in the same hash table entry as a vector.3904Hashtable<Principal, Vector<Pair<String,X509Certificate>>> certs = null;3905if (keyStore.size() > 0) {3906certs = new Hashtable<>(11);3907keystorecerts2Hashtable(keyStore, certs);3908}3909if (trustcacerts) {3910if (caks!=null && caks.size()>0) {3911if (certs == null) {3912certs = new Hashtable<>(11);3913}3914keystorecerts2Hashtable(caks, certs);3915}3916}39173918// start building chain3919Vector<Pair<String,X509Certificate>> chain = new Vector<>(2);3920if (buildChain(3921new Pair<>(rb.getString("the.input"),3922(X509Certificate) certToVerify),3923chain, certs)) {3924for (Pair<String,X509Certificate> p : chain) {3925checkWeak(p.fst, p.snd);3926}3927Certificate[] newChain =3928new Certificate[chain.size()];3929// buildChain() returns chain with self-signed root-cert first and3930// user-cert last, so we need to invert the chain before we store3931// it3932int j=0;3933for (int i=chain.size()-1; i>=0; i--) {3934newChain[j] = chain.elementAt(i).snd;3935j++;3936}3937return newChain;3938} else {3939throw new Exception3940(rb.getString("Failed.to.establish.chain.from.reply"));3941}3942}39433944/**3945* Recursively tries to establish chain from pool of certs starting from3946* certToVerify until a self-signed cert is found, and fill the certs found3947* into chain. Each cert in the chain signs the next one.3948*3949* This method is able to recover from an error, say, if certToVerify3950* is signed by certA but certA has no issuer in certs and itself is not3951* self-signed, the method can try another certB that also signs3952* certToVerify and look for signer of certB, etc, etc.3953*3954* Each cert in chain comes with a label showing its origin. The label is3955* used in the warning message when the cert is considered a risk.3956*3957* @param certToVerify the cert that needs to be verified.3958* @param chain the chain that's being built.3959* @param certs the pool of trusted certs3960*3961* @return true if successful, false otherwise.3962*/3963private boolean buildChain(Pair<String,X509Certificate> certToVerify,3964Vector<Pair<String,X509Certificate>> chain,3965Hashtable<Principal, Vector<Pair<String,X509Certificate>>> certs) {3966if (KeyStoreUtil.isSelfSigned(certToVerify.snd)) {3967// reached self-signed root cert;3968// no verification needed because it's trusted.3969chain.addElement(certToVerify);3970return true;3971}39723973Principal issuer = certToVerify.snd.getIssuerX500Principal();39743975// Get the issuer's certificate(s)3976Vector<Pair<String,X509Certificate>> vec = certs.get(issuer);3977if (vec == null) {3978return false;3979}39803981// Try out each certificate in the vector, until we find one3982// whose public key verifies the signature of the certificate3983// in question.3984for (Enumeration<Pair<String,X509Certificate>> issuerCerts = vec.elements();3985issuerCerts.hasMoreElements(); ) {3986Pair<String,X509Certificate> issuerCert = issuerCerts.nextElement();3987PublicKey issuerPubKey = issuerCert.snd.getPublicKey();3988try {3989certToVerify.snd.verify(issuerPubKey);3990} catch (Exception e) {3991continue;3992}3993if (buildChain(issuerCert, chain, certs)) {3994chain.addElement(certToVerify);3995return true;3996}3997}3998return false;3999}40004001/**4002* Prompts user for yes/no decision.4003*4004* @return the user's decision, can only be "YES" or "NO"4005*/4006private String getYesNoReply(String prompt)4007throws IOException4008{4009String reply = null;4010int maxRetry = 20;4011do {4012if (maxRetry-- < 0) {4013throw new RuntimeException(rb.getString(4014"Too.many.retries.program.terminated"));4015}4016System.err.print(prompt);4017System.err.flush();4018reply = (new BufferedReader(new InputStreamReader4019(System.in))).readLine();4020if (reply == null ||4021collator.compare(reply, "") == 0 ||4022collator.compare(reply, rb.getString("n")) == 0 ||4023collator.compare(reply, rb.getString("no")) == 0) {4024reply = "NO";4025} else if (collator.compare(reply, rb.getString("y")) == 0 ||4026collator.compare(reply, rb.getString("yes")) == 0) {4027reply = "YES";4028} else {4029System.err.println(rb.getString("Wrong.answer.try.again"));4030reply = null;4031}4032} while (reply == null);4033return reply;4034}40354036/**4037* Stores the (leaf) certificates of a keystore in a hashtable.4038* All certs belonging to the same CA are stored in a vector that4039* in turn is stored in the hashtable, keyed by the CA's subject DN.4040* Each cert comes with a string label that shows its origin and alias.4041*/4042private void keystorecerts2Hashtable(KeyStore ks,4043Hashtable<Principal, Vector<Pair<String,X509Certificate>>> hash)4044throws Exception {40454046for (Enumeration<String> aliases = ks.aliases();4047aliases.hasMoreElements(); ) {4048String alias = aliases.nextElement();4049Certificate cert = ks.getCertificate(alias);4050if (cert != null) {4051Principal subjectDN = ((X509Certificate)cert).getSubjectX500Principal();4052Pair<String,X509Certificate> pair = new Pair<>(4053String.format(4054rb.getString(ks == caks ?4055"alias.in.cacerts" :4056"alias.in.keystore"),4057alias),4058(X509Certificate)cert);4059Vector<Pair<String,X509Certificate>> vec = hash.get(subjectDN);4060if (vec == null) {4061vec = new Vector<>();4062vec.addElement(pair);4063} else {4064if (!vec.contains(pair)) {4065vec.addElement(pair);4066}4067}4068hash.put(subjectDN, vec);4069}4070}4071}40724073/**4074* Returns the issue time that's specified the -startdate option4075* @param s the value of -startdate option4076*/4077private static Date getStartDate(String s) throws IOException {4078Calendar c = new GregorianCalendar();4079if (s != null) {4080IOException ioe = new IOException(4081rb.getString("Illegal.startdate.value"));4082int len = s.length();4083if (len == 0) {4084throw ioe;4085}4086if (s.charAt(0) == '-' || s.charAt(0) == '+') {4087// Form 1: ([+-]nnn[ymdHMS])+4088int start = 0;4089while (start < len) {4090int sign = 0;4091switch (s.charAt(start)) {4092case '+': sign = 1; break;4093case '-': sign = -1; break;4094default: throw ioe;4095}4096int i = start+1;4097for (; i<len; i++) {4098char ch = s.charAt(i);4099if (ch < '0' || ch > '9') break;4100}4101if (i == start+1) throw ioe;4102int number = Integer.parseInt(s.substring(start+1, i));4103if (i >= len) throw ioe;4104int unit = 0;4105switch (s.charAt(i)) {4106case 'y': unit = Calendar.YEAR; break;4107case 'm': unit = Calendar.MONTH; break;4108case 'd': unit = Calendar.DATE; break;4109case 'H': unit = Calendar.HOUR; break;4110case 'M': unit = Calendar.MINUTE; break;4111case 'S': unit = Calendar.SECOND; break;4112default: throw ioe;4113}4114c.add(unit, sign * number);4115start = i + 1;4116}4117} else {4118// Form 2: [yyyy/mm/dd] [HH:MM:SS]4119String date = null, time = null;4120if (len == 19) {4121date = s.substring(0, 10);4122time = s.substring(11);4123if (s.charAt(10) != ' ')4124throw ioe;4125} else if (len == 10) {4126date = s;4127} else if (len == 8) {4128time = s;4129} else {4130throw ioe;4131}4132if (date != null) {4133if (date.matches("\\d\\d\\d\\d\\/\\d\\d\\/\\d\\d")) {4134c.set(Integer.valueOf(date.substring(0, 4)),4135Integer.valueOf(date.substring(5, 7))-1,4136Integer.valueOf(date.substring(8, 10)));4137} else {4138throw ioe;4139}4140}4141if (time != null) {4142if (time.matches("\\d\\d:\\d\\d:\\d\\d")) {4143c.set(Calendar.HOUR_OF_DAY, Integer.valueOf(time.substring(0, 2)));4144c.set(Calendar.MINUTE, Integer.valueOf(time.substring(3, 5)));4145c.set(Calendar.SECOND, Integer.valueOf(time.substring(6, 8)));4146c.set(Calendar.MILLISECOND, 0);4147} else {4148throw ioe;4149}4150}4151}4152}4153return c.getTime();4154}41554156/**4157* Match a command with a command set. The match can be exact, or4158* partial, or case-insensitive.4159*4160* @param s the command provided by user4161* @param list the legal command set represented by KnownOIDs enums.4162* @return the position of a single match, or -1 if none matched4163* @throws Exception if s is ambiguous4164*/4165private static int oneOf(String s, KnownOIDs... list) throws Exception {4166String[] convertedList = new String[list.length];4167for (int i = 0; i < list.length; i++) {4168convertedList[i] = list[i].stdName();4169}4170return oneOf(s, convertedList);4171}41724173/**4174* Match a command with a command set. The match can be exact, or4175* partial, or case-insensitive.4176*4177* @param s the command provided by user4178* @param list the legal command set. If there is a null, commands after it4179* are regarded experimental, which means they are supported but their4180* existence should not be revealed to user.4181* @return the position of a single match, or -1 if none matched4182* @throws Exception if s is ambiguous4183*/4184private static int oneOf(String s, String... list) throws Exception {41854186// First, if there is an exact match, returns it.4187int res = oneOfMatch((a,b) -> a.equals(b), s, list);4188if (res >= 0) {4189return res;4190}41914192// Second, if there is one single camelCase or prefix match, returns it.4193// This regex substitution removes all lowercase letters not at the4194// beginning, so "keyCertSign" becomes "kCS".4195res = oneOfMatch((a,b) -> a.equals(b.replaceAll("(?<!^)[a-z]", ""))4196|| b.startsWith(a), s, list);4197if (res >= 0) {4198return res;4199}42004201// Finally, retry the 2nd step ignoring case4202return oneOfMatch((a,b) -> a.equalsIgnoreCase(b.replaceAll("(?<!^)[a-z]", ""))4203|| b.toUpperCase(Locale.ROOT).startsWith(a.toUpperCase(Locale.ROOT)),4204s, list);4205}42064207/**4208* Match a command with a command set.4209*4210* @param matcher a BiFunction which returns {@code true} if the 1st4211* argument (user input) matches the 2nd one (full command)4212* @param s the command provided by user4213* @param list the legal command set4214* @return the position of a single match, or -1 if none matched4215* @throws Exception if s is ambiguous4216*/4217private static int oneOfMatch(BiFunction<String,String,Boolean> matcher,4218String s, String... list) throws Exception {4219int[] match = new int[list.length];4220int nmatch = 0;4221int experiment = Integer.MAX_VALUE;4222for (int i = 0; i<list.length; i++) {4223String one = list[i];4224if (one == null) {4225experiment = i;4226continue;4227}4228if (matcher.apply(s, one)) {4229match[nmatch++] = i;4230}4231}4232if (nmatch == 0) {4233return -1;4234} else if (nmatch == 1) {4235return match[0];4236} else {4237// If multiple matches is in experimental commands, ignore them4238if (match[1] > experiment) {4239return match[0];4240}4241StringBuilder sb = new StringBuilder();4242MessageFormat form = new MessageFormat(rb.getString4243("command.{0}.is.ambiguous."));4244Object[] source = {s};4245sb.append(form.format(source));4246sb.append("\n ");4247for (int i=0; i<nmatch && match[i]<experiment; i++) {4248sb.append(' ');4249sb.append(list[match[i]]);4250}4251throw new Exception(sb.toString());4252}4253}42544255/**4256* Create a GeneralName object from known types4257* @param t one of 5 known types4258* @param v value4259* @param exttype X.509 extension type4260* @return which one4261*/4262private GeneralName createGeneralName(String t, String v, int exttype)4263throws Exception {4264GeneralNameInterface gn;4265int p = oneOf(t, "EMAIL", "URI", "DNS", "IP", "OID");4266if (p < 0) {4267throw new Exception(rb.getString(4268"Unrecognized.GeneralName.type.") + t);4269}4270switch (p) {4271case 0: gn = new RFC822Name(v); break;4272case 1: gn = new URIName(v); break;4273case 2:4274if (exttype == 3) {4275// Allow wildcard only for SAN extension4276gn = new DNSName(v, true);4277} else {4278gn = new DNSName(v);4279}4280break;4281case 3: gn = new IPAddressName(v); break;4282default: gn = new OIDName(v); break; //44283}4284return new GeneralName(gn);4285}42864287private static final String[] extSupported = {4288"BasicConstraints",4289"KeyUsage",4290"ExtendedKeyUsage",4291"SubjectAlternativeName",4292"IssuerAlternativeName",4293"SubjectInfoAccess",4294"AuthorityInfoAccess",4295null,4296"CRLDistributionPoints",4297};42984299private ObjectIdentifier findOidForExtName(String type)4300throws Exception {4301switch (oneOf(type, extSupported)) {4302case 0: return PKIXExtensions.BasicConstraints_Id;4303case 1: return PKIXExtensions.KeyUsage_Id;4304case 2: return PKIXExtensions.ExtendedKeyUsage_Id;4305case 3: return PKIXExtensions.SubjectAlternativeName_Id;4306case 4: return PKIXExtensions.IssuerAlternativeName_Id;4307case 5: return PKIXExtensions.SubjectInfoAccess_Id;4308case 6: return PKIXExtensions.AuthInfoAccess_Id;4309case 8: return PKIXExtensions.CRLDistributionPoints_Id;4310default: return ObjectIdentifier.of(type);4311}4312}43134314// Add an extension into a CertificateExtensions, always using OID as key4315private static void setExt(CertificateExtensions result, Extension ex)4316throws IOException {4317result.set(ex.getId(), ex);4318}43194320/**4321* Create X509v3 extensions from a string representation. Note that the4322* SubjectKeyIdentifierExtension will always be created non-critical besides4323* the extension requested in the <code>extstr</code> argument.4324*4325* @param requestedEx the requested extensions, can be null, used for -gencert4326* @param existingEx the original extensions, can be null, used for -selfcert4327* @param extstrs -ext values, Read keytool doc4328* @param pkey the public key for the certificate4329* @param aSubjectKeyId the subject key identifier for the authority (issuer)4330* @return the created CertificateExtensions4331*/4332private CertificateExtensions createV3Extensions(4333CertificateExtensions requestedEx,4334CertificateExtensions existingEx,4335List <String> extstrs,4336PublicKey pkey,4337KeyIdentifier aSubjectKeyId) throws Exception {43384339// By design, inside a CertificateExtensions object, all known4340// extensions uses name (say, "BasicConstraints") as key and4341// a child Extension type (say, "BasicConstraintsExtension")4342// as value, unknown extensions uses OID as key and bare4343// Extension object as value. This works fine inside JDK.4344//4345// However, in keytool, there is no way to prevent people4346// using OID in -ext, either as a new extension, or in a4347// honored value. Thus here we (ab)use CertificateExtensions4348// by always using OID as key and value can be of any type.43494350if (existingEx != null && requestedEx != null) {4351// This should not happen4352throw new Exception("One of request and original should be null.");4353}4354// A new extensions always using OID as key4355CertificateExtensions result = new CertificateExtensions();4356if (existingEx != null) {4357for (Extension ex: existingEx.getAllExtensions()) {4358setExt(result, ex);4359}4360}4361try {4362// always non-critical4363setExt(result, new SubjectKeyIdentifierExtension(4364new KeyIdentifier(pkey).getIdentifier()));4365if (aSubjectKeyId != null) {4366setExt(result, new AuthorityKeyIdentifierExtension(aSubjectKeyId,4367null, null));4368}43694370// name{:critical}{=value}4371// Honoring requested extensions4372if (requestedEx != null) {4373// The existing requestedEx might use names as keys,4374// translate to all-OID first.4375CertificateExtensions request2 = new CertificateExtensions();4376for (sun.security.x509.Extension ex: requestedEx.getAllExtensions()) {4377request2.set(ex.getId(), ex);4378}4379for(String extstr: extstrs) {4380if (extstr.toLowerCase(Locale.ENGLISH).startsWith("honored=")) {4381List<String> list = Arrays.asList(4382extstr.toLowerCase(Locale.ENGLISH).substring(8).split(","));4383// First check existence of "all"4384if (list.contains("all")) {4385for (Extension ex: request2.getAllExtensions()) {4386setExt(result, ex);4387}4388}4389// one by one for others4390for (String item: list) {4391if (item.equals("all")) continue;43924393// add or remove4394boolean add;4395// -1, unchanged, 0 critical, 1 non-critical4396int action = -1;4397String type = null;4398if (item.startsWith("-")) {4399add = false;4400type = item.substring(1);4401} else {4402add = true;4403int colonpos = item.indexOf(':');4404if (colonpos >= 0) {4405type = item.substring(0, colonpos);4406action = oneOf(item.substring(colonpos+1),4407"critical", "non-critical");4408if (action == -1) {4409throw new Exception(rb.getString4410("Illegal.value.") + item);4411}4412} else {4413type = item;4414}4415}4416String n = findOidForExtName(type).toString();4417if (add) {4418Extension e = request2.get(n);4419if (!e.isCritical() && action == 04420|| e.isCritical() && action == 1) {4421e = Extension.newExtension(4422e.getExtensionId(),4423!e.isCritical(),4424e.getExtensionValue());4425}4426setExt(result, e);4427} else {4428result.delete(n);4429}4430}4431break;4432}4433}4434}4435for(String extstr: extstrs) {4436String name, value;4437boolean isCritical = false;44384439int eqpos = extstr.indexOf('=');4440if (eqpos >= 0) {4441name = extstr.substring(0, eqpos);4442value = extstr.substring(eqpos+1);4443} else {4444name = extstr;4445value = null;4446}44474448int colonpos = name.indexOf(':');4449if (colonpos >= 0) {4450if (oneOf(name.substring(colonpos+1), "critical") == 0) {4451isCritical = true;4452}4453name = name.substring(0, colonpos);4454}44554456if (name.equalsIgnoreCase("honored")) {4457continue;4458}4459int exttype = oneOf(name, extSupported);4460switch (exttype) {4461case 0: // BC4462int pathLen = -1;4463boolean isCA = false;4464if (value == null) {4465isCA = true;4466} else {4467try { // the abbr format4468pathLen = Integer.parseInt(value);4469isCA = true;4470} catch (NumberFormatException ufe) {4471// ca:true,pathlen:14472for (String part: value.split(",")) {4473String[] nv = part.split(":");4474if (nv.length != 2) {4475throw new Exception(rb.getString4476("Illegal.value.") + extstr);4477} else {4478if (nv[0].equalsIgnoreCase("ca")) {4479isCA = Boolean.parseBoolean(nv[1]);4480} else if (nv[0].equalsIgnoreCase("pathlen")) {4481pathLen = Integer.parseInt(nv[1]);4482} else {4483throw new Exception(rb.getString4484("Illegal.value.") + extstr);4485}4486}4487}4488}4489}4490setExt(result, new BasicConstraintsExtension(isCritical, isCA,4491pathLen));4492break;4493case 1: // KU4494if(value != null) {4495boolean[] ok = new boolean[9];4496for (String s: value.split(",")) {4497int p = oneOf(s,4498"digitalSignature", // (0),4499"nonRepudiation", // (1)4500"keyEncipherment", // (2),4501"dataEncipherment", // (3),4502"keyAgreement", // (4),4503"keyCertSign", // (5),4504"cRLSign", // (6),4505"encipherOnly", // (7),4506"decipherOnly", // (8)4507"contentCommitment" // also (1)4508);4509if (p < 0) {4510throw new Exception(rb.getString("Unknown.keyUsage.type.") + s);4511}4512if (p == 9) p = 1;4513ok[p] = true;4514}4515KeyUsageExtension kue = new KeyUsageExtension(ok);4516// The above KeyUsageExtension constructor does not4517// allow isCritical value, so...4518setExt(result, Extension.newExtension(4519kue.getExtensionId(),4520isCritical,4521kue.getExtensionValue()));4522} else {4523throw new Exception(rb.getString4524("Illegal.value.") + extstr);4525}4526break;4527case 2: // EKU4528if(value != null) {4529Vector<ObjectIdentifier> v = new Vector<>();4530KnownOIDs[] choices = {4531KnownOIDs.anyExtendedKeyUsage,4532KnownOIDs.serverAuth,4533KnownOIDs.clientAuth,4534KnownOIDs.codeSigning,4535KnownOIDs.emailProtection,4536KnownOIDs.KP_TimeStamping,4537KnownOIDs.OCSPSigning4538};4539for (String s: value.split(",")) {4540int p = oneOf(s, choices);4541String o = s;4542if (p >= 0) {4543o = choices[p].value();4544}4545try {4546v.add(ObjectIdentifier.of(o));4547} catch (Exception e) {4548throw new Exception(rb.getString(4549"Unknown.extendedkeyUsage.type.") + s);4550}4551}4552setExt(result, new ExtendedKeyUsageExtension(isCritical, v));4553} else {4554throw new Exception(rb.getString4555("Illegal.value.") + extstr);4556}4557break;4558case 3: // SAN4559case 4: // IAN4560if(value != null) {4561String[] ps = value.split(",");4562GeneralNames gnames = new GeneralNames();4563for(String item: ps) {4564colonpos = item.indexOf(':');4565if (colonpos < 0) {4566throw new Exception("Illegal item " + item + " in " + extstr);4567}4568String t = item.substring(0, colonpos);4569String v = item.substring(colonpos+1);4570gnames.add(createGeneralName(t, v, exttype));4571}4572if (exttype == 3) {4573setExt(result, new SubjectAlternativeNameExtension(4574isCritical, gnames));4575} else {4576setExt(result, new IssuerAlternativeNameExtension(4577isCritical, gnames));4578}4579} else {4580throw new Exception(rb.getString4581("Illegal.value.") + extstr);4582}4583break;4584case 5: // SIA, always non-critical4585case 6: // AIA, always non-critical4586if (isCritical) {4587throw new Exception(rb.getString(4588"This.extension.cannot.be.marked.as.critical.") + extstr);4589}4590if(value != null) {4591List<AccessDescription> accessDescriptions =4592new ArrayList<>();4593String[] ps = value.split(",");4594for(String item: ps) {4595colonpos = item.indexOf(':');4596int colonpos2 = item.indexOf(':', colonpos+1);4597if (colonpos < 0 || colonpos2 < 0) {4598throw new Exception(rb.getString4599("Illegal.value.") + extstr);4600}4601String m = item.substring(0, colonpos);4602String t = item.substring(colonpos+1, colonpos2);4603String v = item.substring(colonpos2+1);4604KnownOIDs[] choices = {4605KnownOIDs.OCSP,4606KnownOIDs.caIssuers,4607KnownOIDs.AD_TimeStamping,4608KnownOIDs.caRepository4609};4610int p = oneOf(m, choices);4611ObjectIdentifier oid;4612if (p >= 0) {4613oid = ObjectIdentifier.of(choices[p]);4614} else {4615try {4616oid = ObjectIdentifier.of(m);4617} catch (Exception e) {4618throw new Exception(rb.getString(4619"Unknown.AccessDescription.type.") + m);4620}4621}4622accessDescriptions.add(new AccessDescription(4623oid, createGeneralName(t, v, exttype)));4624}4625if (exttype == 5) {4626setExt(result, new SubjectInfoAccessExtension(accessDescriptions));4627} else {4628setExt(result, new AuthorityInfoAccessExtension(accessDescriptions));4629}4630} else {4631throw new Exception(rb.getString4632("Illegal.value.") + extstr);4633}4634break;4635case 8: // CRL, experimental, only support 1 distributionpoint4636if(value != null) {4637String[] ps = value.split(",");4638GeneralNames gnames = new GeneralNames();4639for(String item: ps) {4640colonpos = item.indexOf(':');4641if (colonpos < 0) {4642throw new Exception("Illegal item " + item + " in " + extstr);4643}4644String t = item.substring(0, colonpos);4645String v = item.substring(colonpos+1);4646gnames.add(createGeneralName(t, v, exttype));4647}4648setExt(result, new CRLDistributionPointsExtension(4649isCritical, Collections.singletonList(4650new DistributionPoint(gnames, null, null))));4651} else {4652throw new Exception(rb.getString4653("Illegal.value.") + extstr);4654}4655break;4656case -1:4657ObjectIdentifier oid = ObjectIdentifier.of(name);4658byte[] data = null;4659if (value != null) {4660data = new byte[value.length() / 2 + 1];4661int pos = 0;4662for (char c: value.toCharArray()) {4663if (!HexFormat.isHexDigit(c)) {4664continue;4665}4666int hex = HexFormat.fromHexDigit(c);4667if (pos % 2 == 0) {4668data[pos/2] = (byte)(hex << 4);4669} else {4670data[pos/2] += hex;4671}4672pos++;4673}4674if (pos % 2 != 0) {4675throw new Exception(rb.getString(4676"Odd.number.of.hex.digits.found.") + extstr);4677}4678data = Arrays.copyOf(data, pos/2);4679} else {4680data = new byte[0];4681}4682setExt(result, new Extension(oid, isCritical,4683new DerValue(DerValue.tag_OctetString, data)4684.toByteArray()));4685break;4686default:4687throw new Exception(rb.getString(4688"Unknown.extension.type.") + extstr);4689}4690}4691} catch(IOException e) {4692throw new RuntimeException(e);4693}4694return result;4695}46964697private boolean isTrustedCert(Certificate cert) throws KeyStoreException {4698if (caks != null && caks.getCertificateAlias(cert) != null) {4699return true;4700} else {4701String inKS = keyStore.getCertificateAlias(cert);4702return inKS != null && keyStore.isCertificateEntry(inKS);4703}4704}47054706private void checkWeak(String label, String sigAlg, Key key) {4707if (sigAlg != null) {4708if (!DISABLED_CHECK.permits(SIG_PRIMITIVE_SET, sigAlg, null)) {4709weakWarnings.add(String.format(4710rb.getString("whose.sigalg.disabled"), label, sigAlg));4711} else if (!LEGACY_CHECK.permits(SIG_PRIMITIVE_SET, sigAlg, null)) {4712weakWarnings.add(String.format(4713rb.getString("whose.sigalg.weak"), label, sigAlg));4714}4715}47164717if (key != null) {4718if (!DISABLED_CHECK.permits(SIG_PRIMITIVE_SET, key)) {4719weakWarnings.add(String.format(4720rb.getString("whose.key.disabled"), label,4721String.format(rb.getString("key.bit"),4722KeyUtil.getKeySize(key), fullDisplayAlgName(key))));4723} else if (!LEGACY_CHECK.permits(SIG_PRIMITIVE_SET, key)) {4724weakWarnings.add(String.format(4725rb.getString("whose.key.weak"), label,4726String.format(rb.getString("key.bit"),4727KeyUtil.getKeySize(key), fullDisplayAlgName(key))));4728}4729}4730}47314732private void checkWeak(String label, Certificate[] certs)4733throws KeyStoreException {4734for (int i = 0; i < certs.length; i++) {4735Certificate cert = certs[i];4736if (cert instanceof X509Certificate) {4737X509Certificate xc = (X509Certificate)cert;4738String fullLabel = label;4739if (certs.length > 1) {4740fullLabel = oneInMany(label, i, certs.length);4741}4742checkWeak(fullLabel, xc);4743}4744}4745}47464747private void checkWeak(String label, Certificate cert)4748throws KeyStoreException {4749if (cert instanceof X509Certificate) {4750X509Certificate xc = (X509Certificate)cert;4751// No need to check the sigalg of a trust anchor4752String sigAlg = isTrustedCert(cert) ? null : xc.getSigAlgName();4753checkWeak(label, sigAlg, xc.getPublicKey());4754}4755}47564757private void checkWeak(String label, PKCS10 p10) {4758checkWeak(label, p10.getSigAlg(), p10.getSubjectPublicKeyInfo());4759}47604761private void checkWeak(String label, CRL crl, Key key) {4762if (crl instanceof X509CRLImpl) {4763X509CRLImpl impl = (X509CRLImpl)crl;4764checkWeak(label, impl.getSigAlgName(), key);4765}4766}47674768private void printWeakWarnings(boolean newLine) {4769if (!weakWarnings.isEmpty() && !nowarn) {4770System.err.println("\nWarning:");4771for (String warning : weakWarnings) {4772System.err.println(warning);4773}4774if (newLine) {4775// When calling before a yes/no prompt, add a new line4776System.err.println();4777}4778}4779weakWarnings.clear();4780}47814782/**4783* Prints the usage of this tool.4784*/4785private void usage() {4786if (command != null) {4787System.err.println("keytool " + command +4788rb.getString(".OPTION."));4789System.err.println();4790System.err.println(rb.getString(command.description));4791System.err.println();4792System.err.println(rb.getString("Options."));4793System.err.println();47944795// Left and right sides of the options list. Both might4796// contain "\n" and span multiple lines4797String[] left = new String[command.options.length];4798String[] right = new String[command.options.length];47994800// Length of left side of options list4801int lenLeft = 0;48024803for (int j = 0; j < command.options.length; j++) {4804Option opt = command.options[j];4805left[j] = opt.toString();4806if (opt.arg != null) {4807left[j] += " " + opt.arg;4808}4809String[] lefts = left[j].split("\n");4810for (String s : lefts) {4811if (s.length() > lenLeft) {4812lenLeft = s.length();4813}4814}4815right[j] = rb.getString(opt.description);4816}4817for (int j = 0; j < left.length; j++) {4818String[] lefts = left[j].split("\n");4819String[] rights = right[j].split("\n");4820for (int i = 0; i < lefts.length && i < rights.length; i++) {4821String s1 = i < lefts.length ? lefts[i] : "";4822String s2 = i < rights.length ? rights[i] : "";4823if (i == 0) {4824System.err.printf(" %-" + lenLeft + "s %s\n", s1, s2);4825} else {4826System.err.printf(" %-" + lenLeft + "s %s\n", s1, s2);4827}4828}4829}4830System.err.println();4831System.err.println(rb.getString(4832"Use.keytool.help.for.all.available.commands"));4833} else {4834System.err.println(rb.getString(4835"Key.and.Certificate.Management.Tool"));4836System.err.println();4837System.err.println(rb.getString("Commands."));4838System.err.println();4839for (Command c: Command.values()) {4840if (c == KEYCLONE) break;4841System.err.printf(" %-20s%s\n", c, rb.getString(c.description));4842}4843System.err.println();4844System.err.println(rb.getString(4845"Use.keytool.help.for.all.available.commands"));4846System.err.println(rb.getString(4847"Use.keytool.command.name.help.for.usage.of.command.name"));4848}4849}48504851private void tinyHelp() {4852usage();4853if (debug) {4854throw new RuntimeException("NO BIG ERROR, SORRY");4855} else {4856System.exit(1);4857}4858}48594860private void errorNeedArgument(String flag) {4861Object[] source = {flag};4862System.err.println(new MessageFormat(4863rb.getString("Command.option.flag.needs.an.argument.")).format(source));4864tinyHelp();4865}48664867private char[] getPass(String modifier, String arg) {4868char[] output =4869KeyStoreUtil.getPassWithModifier(modifier, arg, rb, collator);4870if (output != null) return output;4871tinyHelp();4872return null; // Useless, tinyHelp() already exits.4873}4874}48754876// This class is exactly the same as com.sun.tools.javac.util.Pair,4877// it's copied here since the original one is not included in JRE.4878class Pair<A, B> {48794880public final A fst;4881public final B snd;48824883public Pair(A fst, B snd) {4884this.fst = fst;4885this.snd = snd;4886}48874888public String toString() {4889return "Pair[" + fst + "," + snd + "]";4890}48914892public boolean equals(Object other) {4893return4894other instanceof Pair &&4895Objects.equals(fst, ((Pair)other).fst) &&4896Objects.equals(snd, ((Pair)other).snd);4897}48984899public int hashCode() {4900if (fst == null) return (snd == null) ? 0 : snd.hashCode() + 1;4901else if (snd == null) return fst.hashCode() + 2;4902else return fst.hashCode() * 17 + snd.hashCode();4903}49044905public static <A,B> Pair<A,B> of(A a, B b) {4906return new Pair<>(a,b);4907}4908}4909491049114912