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