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