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