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