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