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